Composr Tutorial: Tempcode programming

Written by Chris Graham (ocProducts)
Tempcode is Composr's template programming language. On the simplest level, it provides a substitution mechanism so that parameters and global symbols (like the current user's username, or the time) can be inserted into a template.

It also serves as a complete programming language with support for control structures, built-in core functions and variable substitutions.


Syntax

The "syntax" of a language is simply the grammar. Tempcode's syntax is extremely simple.

Tempcode provides four constructs other than the plain text of the template itself:
  1. Parameters: {PARAMETER} where PARAMETER is a code-name of something that was actually passed to the template.
  2. Language strings: {!STRING,<parameters>...} where STRING is a real language string from a loaded language file (e.g. Banner code could use strings from banners.ini, but most other code could not; global.ini contains strings usable anywhere).
  3. Symbols: {$SYMBOL,<parameters>...} where SYMBOL is a real symbol Composr supports. Embedding a symbol is like making a function call or running a command in the complex place, or a substitution in the simple case.
  4. Directives: {+START,DIRECTIVE,<parameters>...}...{+END} where DIRECTIVE is a real directive Composr supports. Directives essentially wrap portions of the template, controlling that portion; they are generally used for types of condition checking (e.g. IF) or loops. There are also directives like {+IMPLODE,<parameters>...} that do not wrap, but work outside the normal "string manipulation" bounds that symbols do.

Escaping

Any of the above 4 Tempcode constructs may be escaped. By "escaped", I mean made to fit in an encoding scheme, such as HTML or URLs, such that special text characters do not interact with that encoding scheme in an insecure/corrupting way). This is done with ease using escaping filters, just by placing the character associated with the mode of escaping before the closing '}'.
For example, {BLAH*} will add the parameter BLAH, escaped for HTML output.

The following escaping filters are provided (green indicates common usage, red indicates advanced usage)…

Character Purpose Example
Before After
HTML and XML
(*) Text embedded within HTML. Hello & Goodbye Hello &amp; Goodbye
= HTML escaping (like above), if you are 100% sure you want the data escaping, even if it comes from something that might already be in HTML such as a language string. as above
(|) Make something appropriate for use as an HTML/XML ID, usually to be referenced by JavaScript and CSS. This is a -terrible- ID This__is__a____terrible____ID
JavaScript
(/) Works around a special JavaScript SGML-issue: anything looking like HTML tags within a <script> tag must be specially-escaped. print('</p>'); print('<\/p>');
% Make something appropriate as a strict codename. This is used where JavaScript code is involved for the highest level of security. This This
This is Hack-attack triggered
CSS
' Make something appropriate for use as a CSS value (anything that isn't a part of a very constrained non-string value is replaced with underscore). green and red green_and_red
Both JavaScript and CSS
(;) Make something fit between JavaScript/CSS-style single quotes. Who's here Who\'s here
(#) Make something fit between JavaScript/CSS-style double quotes. She said, "Hello" She said, \"Hello\"
(~) Where new lines are not allowed (text is drawn up to reside on a single line). a
b
ab
(^) Where new lines become \n (multiple lines drawn together with \n as a separator). a
b
a\nb
Comcode
(@) Make sure something doesn't look like a Comcode tag. Use the [url] tag Use the \[url] tag
URLs
& Make sure something fits in as a URL parameter, with special Comcode URL escaping also. a&b a:amp:b
. Make sure something fits in as a URL parameter, if you don't want extra Composr URL escaping to happen. Use this when building up non-Composr URLs. a&b a%26b
Special (not actually for escaping)
- Special code to indicate that a construct should not be pre-processed. This is useful very occasionally if you want to stop something like a block being preloaded if it is buried under a Tempcode IF directive and hence doesn't always run. Stopping pre-processing will stop any JavaScript or CSS dependencies being called up, however.
+ Must be used as the only escaping character. This indicates that something:
  • should not be escaped
  • definitely is passed into the template (no user-friendly warnings will be given if it is not)
  • should be output directly, the way PHP wants (so will not follows the normal rules for Tempcode display of boolean values or arrays)
This is substantially faster in terms of the amount of memory, CPU time, and storage. However, it should only really be used by programmers knowing exactly what they are doing.


It is absolutely crucial that Tempcode programmers use the proper escaping. Without it, all kinds of insecurities and unreliabilities can develop. About 50% of parameters in the default Composr templates actually use HTML escaping so that plain text placed inside a template does not interfere with the HTML structure itself and displays literally.

If you're wondering what the characters refer to: nothing. They are just characters that are easy to type on a regular keyboard. We like the ability to be able to apply the escapings in this shorthand, in the same way that maths benefits from concise symbolic shorthand.

The Tempcode tree (advanced)

Templates are composed together into a tree structure, and then the tree structure is output. This is discussed in further detail in the Composr site structure tutorial.

Philosophy

Tempcode is quite different from other templating languages, and it's worth covering that.

Most templating languages will try to either:
  1. use XML, to add programming constructs into the mark-up itself
  2. allow PHP code, or an abstraction of PHP code, to run within the templates

Our approach is distinctly different, as we uphold four strong principles:
  1. templates should be editable in an HTML editor without Tempcode being broken (although the HTML editor at this time must be able to support 'fragments of HTML')
  2. templates should not contain code on the same level as PHP, for security reasons (a theme should not be able to delete files, for instance)
  3. there should be a very clear separation of role between templates and code; code should not be put into templates simply because it is convenient, as this muddies the architecture, causing maintenance issues
  4. templates should not 'execute': Tempcode should act as a filter/token-placer for output, reshaping output, rather than intertwining code with it. This is again, an architectural issue

Advanced philosophy (experts only)

For those interested in 'programming language semantics', Tempcode is neither a functional or imperative language, but has aspects of both. A functional language essentially is focused on output being controlled by a complex mathematical expression. An imperative language essentially is focused on output being controlled by a sequence of commands. With Tempcode, output is focused on the stream of text coming from a template, and the composition of these streams; 'symbols' and 'directives' can control, manipulate and add to the stream, but they are more like embedded functional-language function-chains and control-tags than imperative code. Sequences of commands can be simulated by placing symbols and directives next to each other in the output stream and using variable manipulation features to bridge data across the portions of the output stream.

Popular template languages such as 'Velocity' or 'Smarty' are able (and often used) to function in a similar sense to this, but their language design is imperative at core, and the result simply is just far less elegant. Tempcode just 'feels right', with its clean syntax and PHP-separation, while other languages look literally like conventional programming code has been added inside the template in an ad-hoc way (even if the full programming syntax has been simplified a bit).

Placing theme images

Party like it's 1999

Under Construction

(this guy's back must
be really hurting by now)
This is your first practical example of Tempcode, and perhaps the most common use of Tempcode symbols.

To insert an image into your template, use Composr's image symbol: {$IMG,codename}. Replace codename with the image code for the image you want to use. This will turn into the image's URL. Be aware that this does not insert any HTML for you, so to use it fully:

Code (HTML)

<img src="{$IMG*,under_construction_animated}" alt="Under Construction" title="Under Construction" />
This would insert the image http://yourbaseurl/themes/default/images/under_construction_animated.png into your page, as shown on the right.

The advantage to using the Composr code instead of hand coding the image is two-fold. First, you can have different versions of theme images for different languages and themes. Secondly, if you ever move the image, you don't have to edit every page that ever used it to point to the new location. You can edit images from within the themes management screen (go to Admin Zone > Style > Themes).

Symbols

Numbers and logic

Tempcode does not have explicit data-types, everything is text. However we may interpret the text in symbols as follows:
  • Tempcode number: the text is assumed to be a number written as a normal decimal string (e.g. 12345). Numbers should not contain any special formatting other than the British English decimal point (.) symbol.
  • Tempcode boolean: the text is assumed to be 0 (meaning false) or 1 (meaning true).

Further notes

If a symbol is missing important parameters, it generally is skipped entirely.

Some of the more advanced symbols are not fully laid-out, but they are all defined in sources/symbols.php or sources/symbols2.php in an obvious way, so advanced users can read this file.

You are very unlikely to ever want to use rows in red, as they are intended for use by core Composr code.

General

Symbol name / example Purpose
(blank) Place a comment. E.g... {$,this is a comment}
{$RAND} A random number between 0 and 2147483647
{$SET_RAND,a,b,c} A random choice from the given parameters
{$CYCLE,cyclename,a,b,c} Cycle through parameters in a sequence (intended for use across template calls to create, for example, striping effects). The first parameter is the name of the cycle, and the following parameters are the parameters of the cycle. The pointer for the named cycle will move forward/around each time the cycle symbol is referenced (e.g. {$CYCLE,mycycle,a,b,c,d} would produce "a" the first time it's called, "b" the next, "c" next, then "d", then back to "a" again). If you only provide one parameter then the cycles current index is returned. If only two parameters are provided then the second parameter is split up by commas; this is useful for programming cycles on a higher level (e.g. for making chequered patterns using floats).
{$RESET_CYCLE,cyclename} Reset a named cycle sequence
{$MAILTO} Get an obfuscated "mailto: " string (obfuscated to make it hard for e-mail scavengers to detect)
{$HONEYPOT_LINK} If a Project Honeypot script is installed, this will insert a link to it, using a different method for different page names to increase the likeliness of a bot snag.
{$INSERT_SPAMMER_BLACKHOLE} If spammer blackholes are enabled, this inserts a field into a form to create the blackhole. Blackholes allow automatic detection and banning of would-be spammers. Additionally, the CSRF-prevention security token is injected via this symbol.
{$CSS_DIMENSION_REDUCE,30px,2} Reduce a CSS dimension by a certain number of px. If the input is not in px, no function is performed and the output stays as the input.
{$BETA_CSS_PROPERTY,<css rule>} Adds browser vendor prefixes to a CSS rule that is not widely supported yet. This effectively marks the CSS rule out, and theoretically allows new Composr versions to alter how that rule is handled without direct changes to your theme CSS.
{$REQUIRE_JAVASCRIPT,example} Include a JavaScript file in the output stream. If you set the second parameter to '1', then loading will be deferred until the end of the page
{$FACILITATE_AJAX_BLOCK_CALL,block code,differing parameters} This symbol generates a string parameter suitable for the Composr JavaScript call_block function, which is used to make an AJAX call to load up a block and overwrite the contents of a specific div with it. Calling this symbol has the side-effects of triggering an include of Composr's AJAX code, and setting up a permission so that the server-side AJAX handler knows that the current session is authorised to load up this pattern of block. The symbol is best used by example -- see how it is currently used by code.
{$REQUIRE_CSS,css_example} Include a CSS file in the output stream
{$ANCHOR,anchorname} Place a named HTML anchor (intended for use from in Comcode)
{$CSS_TEMPCODE} Get the Tempcode for all the CSS includes for this page
{$PAGE_TITLE} Get the currently set page title/td>
{$SET_TITLE,xxx} Set the current page title (affects things like the <title&rt;
{$EXTRA_HEAD} Get the contents of this global Tempcode variable (used by some parts of the system to inject extra markup)
{$EXTRA_FOOT} Get the contents of this global Tempcode variable (used by some parts of the system to inject extra markup)
{$JS_TEMPCODE} Get the Tempcode for all the JS includes for this page


Format conversion and obfuscation

Symbol name / example Purpose
{$ESCAPE,what_to_escape,optional_escaping_type} Perform escaping on the first parameter. Uses the same escaping as integrated template escaping, except a numeric constant (defined in the second parameter) is used to determine the escaping, rather than special characters (see sources/tempcode.php for a list of all the constants). Defaults to HTML escaping.
{$OBFUSCATE,to_obfuscate} Obfuscate a string to make it harder for e-mail scavengers
{$STRIP_TAGS,<strong>example</strong>} Remove HTML from some text
{$STRIP_HTML,<strong>example</strong>} Turn some HTML into a plain-text string (i.e. no HTML entities either)
{$ENTITY_DECODE,this &amp; that} Decode XML or HTML entities, so that written-out XML turns into actual parsed XML
{$TRUNCATE_LEFT,something long,5,1,0,0,0.2} Truncate a string, keeping what's on the left as priority. First parameter is the text to truncate. Second parameter is the truncation length. Optional third parameter is a binary value for whether to show a tooltip. Optional fourth parameter is a binary value for whether the input text is already in HTML format (output text is always in HTML format). Optional fifth parameter is a binary value for whether the length desired should be based on readable characters rather than XHTML characters. Optional sixth parameter is a decimal fraction (e.g. 0.2) that represents the tolerance that can be used to preserve grammar (paragraphs and sentence).
{$TRUNCATE_RIGHT,something long,5,1,0,0,0.2} As above, truncating to keep what's on the right as priority.
{$TRUNCATE_SPREAD,something long,5,1,0,0,0.2} As above, truncating to keep an equal left/right portion.
{$TRUNCATE_EXPAND,something long,5,1,0,0,0.2} As above, with any truncated text going underneath an expander.
{$ALTERNATOR_TRUNCATED,something long,5,if truncated,if not truncated,1} A complex symbol to allow alternation between situations when something would and would not become truncated.
{$PARAGRAPH,something} Add a paragraph around the contents, but only if it does not already contain any block-level elements. This is needed to combine valid (X)HTML for arbitrary Comcode insertion with the desire to have neat semantics and automatic margining.
{$CLEAN_FILE_SIZE,10000} Convert a number of bytes into a nice clean human readable file size.
{$TRIM, abc } Trim any textual or XHTML white-space from the given string.
{$DEEP_CLEAN, abc } Apply the deep clean process on the parameter. Clean common ugly patterns out, such as leading white-space, all-caps, or paste-from-Word problems.


Users, Members, and Usergroups

Symbol name / example Purpose
{$MEMBER} The Member-ID of the current user
{$IS_GUEST} Whether the current user is a Guest
{$USERNAME,2} The username of the current user / given user (optionally takes a member ID, otherwise assumes the current user). If the second parameter is passed as '1', the display name is used (if configured).
{$DISPLAYED_USERNAME,admin} Turns a username into a display name, using the configured display name generator (if there is one -- otherwise, no change)
{$AVATAR,2} The member's avatar (optionally takes a member ID, else assumes current user)
{$MEMBER_EMAIL,2} The member's e-mail address (optionally takes a member ID, else assumes current user)
{$PHOTO,2} The member's photo (optionally takes a member ID, else assumes current user)
{$MEMBER_PROFILE_URL,2} The member's profile URL (optionally takes a member ID, else assumes current user)
{$CNS_MEMBER_HTML,2} The member's information box (optionally takes a member ID, else assumes current user)
{$CNS_RANK_IMAGE,2} The member's rank images in composed HTML format (optionally takes a member ID, else assumes current user)
{$IS_IN_GROUP,1-3} Find whether the current member is in a usergroup (parameters together constitute an Selectcode list, except if the last is primary or secondary then this specifies that the check be limited to checking such usergroup membership).
{$CPF_VALUE,Some Custom Field} Find the value of a custom profile field (pass in the ID number of a custom profile field, which can be found from the URL of where you to edit it -- or pass in the human-readable name of the field; if you pass a name it can also interpret translated/Comcode text fields properly). If you pass a second parameter, it will specify the member ID to use (if not passed, it will default to the current member). Note that Tempcode is not context-sensitive apart from the parameters passed into a template. The current member is whoever is logged in; if you are editing a template displaying a member's details (e.g. CNS_MEMBER_PROFILE_SCREEN) then you will need to explicitly specify the member ID in the second parameter using the member ID passed into that template (typically {MEMBER_ID}).


Date and time

Symbol name / example Purpose
{$DATE_AND_TIME} The formatted current date and time (may take optional parameters, not discussed here)
{$DATE} The formatted current date (may take optional parameters, not discussed here)
{$TIME} The formatted time (may take optional parameters, not discussed here)
{$SECONDS_PERIOD,12345} Show the number of hours/minutes/seconds in a given period of seconds
{$FROM_TIMESTAMP,%d %B %Y,123456780} Converts a time-stamp to a formatted date/time (optional first parameter is a strftime-style date/time formatting string, optional second parameter is a timestamp). If no parameters given it will simply return the current timestamp.
{$TO_TIMESTAMP,April 1st 2009} Converts a formatted date/time to a timestamp via auto-recognition (first parameter is a timestamp). If no parameters given it will simply return the current timestamp.
{$MAKE_RELATIVE_DATE,123456789} Converts a timestamp into a textually described relative time (e.g. "10 minutes" [ago]).
{$TIME_PERIOD,1234} Converts a number of seconds into a textually described time period (e.g. "10 minutes").


Catalogues

Symbol name / example Purpose
{$CATALOGUE_ENTRY_BACKREFS,123} Get a comma-separated list of IDs of catalogue entries that reference the given catalogue entry. A second parameter may be given, which is a limit on how many to return. A third parameter may be set as the name of a content type if the backrefs should be resolved to linked content entries (for the case where the linkage is via custom fields rather than a pure catalogue). A fourth parameter of the rating type code may be given if sort-by-rating is needed (highest rated first).
{$CATALOGUE_ENTRY_FIELD_VALUE,123,0} Get the value of a field in a specific catalogue entry (example would be 1st field in entry #123).
{$CATALOGUE_ENTRY_FIELD_VALUE_PLAIN,123,0} As per CATALOGUE_ENTRY_FIELD_VALUE
{$CATALOGUE_ENTRY_FOR,download,3} Get the ID of the auto-maintained catalogue entry bound to the given content entry, i.e. where the content entries custom fields are stored. This can then be used with the CATALOGUE_ENTRY_FIELD_VALUE symbol (above) or the CATALOGUE_ENTRY_ALL_FIELD_VALUES symbol (below).
{$CATALOGUE_ENTRY_ALL_FIELD_VALUES,123} Show a field-map view of all fields in the entry. You can also pass in a second param with the value '1' if you want raw field rows, rather than a table (useful for adding field-map data into an existing table).


Galleries

Symbol name / example Purpose
{$GALLERY_VIDEO_FOR_URL,uploads/galleries/foo.mov} Get the gallery video ID for a named URL, importing to a new video if needed. This is useful for managing documentation out of git, referencing videos that will then be synchronised with Youtube upon first reference. You'll likely use this symbol to pass as a parameter to the main_content block.


Locations / Currencies

Symbol name / example Purpose
{$CURRENCY,123,USD} Do a currency conversion / Get the currency (first parameter is a number for the amount in the site's default currency, second parameter is the source currency, optional third parameter is the target currency [defaults to the site currency])
{$CURRENCY_SYMBOL} Get the nice currency symbol for the site's default currency
{$COUNTRY} Find the current user's country
{$COUNTRY_CODE_TO_NAME,UK} Convert an ISO country code to a written country name
{$COUNTRY_NAME_TO_CODE,United Kingdom} Convert a written country name to an ISO country code
{$REGION} Find the current user's region (usually the same as the country, but may be customised within Composr to be more-specific)


Environmental querying

Symbol name / example Purpose
{$_GET,page} Extract the requested script GET parameter
{$IS_VIRTUALISED_REQUEST} Find if this is a virtualised page load, via something like the main_include_module block
{$QUERY_STRING} Extract all requested script parameters (the 'query string', i.e. part of the URL after "?")
{$_POST,title} Extract the requested script POST parameter
{$_POSTED} Whether the current request was sent with POST parameters (i.e. it was a form post)
{$ZONE} The zone the user is in
{$PAGE} The page the user is in
{$CANONICAL_URL} Get the canonical URL (for search engines)
{$SELF_URL,0,0,0,a=b} Get URL to current screen. You don't need to give any parameters, but if you do: the first is whether to go to the website home page if you are at where you're at due to a form post, the second is whether to included posted parameters as parameters in the URL, the first is whether to avoid the URL Scheme facility, and further parameters are additional URL parameters.
{$SELF_PAGE_LINK} Get page-link to current screen.
{$METADATA,created} Fetch a defined dublin core metadata property (by code name), relating to the current content. Also supports some Facebook connect fields, HTML SEO metadata (keywords, meta_description). You can also query HTML SEO metadata for specific content item, by giving two additional parameters: the content type and the content ID
{$REFRESH} HTML code for a site refresh, if one is in progress
{$FEEDS} HTML code for RSS/Atom feeds
{$RUNNING_SCRIPT,index} Find whether an entry script is running
{$MATCH_KEY_MATCH,_WILD:downloads:browse} Whether the given match-key matches the current URL
{$MOBILE} Whether a Smartphone (for example) is being used. Composr is designed to be pretty good at detecting this, but if you need to specify additional user agent strings you can make a text_custom/mobile_devices.txt file with mobileuseragentsubstring=0|1 lines in it (e.g. iphone=1 for iPhone to be detected as mobile, and ipad=0 for iPad to not be detected as mobile). If you want to detect smartphones in CSS we suggest you either use this symbol (which the default theme uses) or if you prefer use CSS media queries inside your CSS files to create override rules
{$THEME} The user's current theme
{$JS_ON} Whether JavaScript is enabled (Tempcode boolean)
{$LANG} The user's current language
{$BROWSER_UA} The user's browser, based on their user-agent string
{$OS} The user's OS, based on their user-agent string
{$DEV_MODE} Whether development mode is on (adds in extra errors, to pick up on coding standard violations)
{$NO_SAFE_MODE} Detect if PHP safe mode isn't on the server
{$BROWSER_MATCHES,wysiwyg} Find whether the current browser matches a named property / has a feature. Possible properties: wysiwyg, windows, mac, linux, mobile, ie, ie8, ie8+, ie9, ie9+, gecko, safari (covers Google Chrome also), ios, android, bot
{$USER_AGENT} The user-agent string of the current user
{$IP_ADDRESS} The IP address of the current user
{$TIMEZONE} The server timezone
{$HTTP_STATUS_CODE} Find the HTTP status code (e.g. 404) for the current request, usually 200
{$BROWSER,wysiwyg,You have WYSIWYG,You do not have WYSIWYG} Conditionally chooses between two input strings, based on a browser property (see above for a list of properties). First parameter is the property, second and third are the conditional strings.
{$GEOLOCATE,12.45.67.89} Find the/a user's ISO country-code (optionally takes an IP address, otherwise defaults to current user's IP). Requires the stats addon to be installed, and for the geolocation data to have been installed into the database (usually this is automatic)
{$FORCE_PREVIEWS} Find whether the user has forced previews to show
{$PREVIEW_URL} Find the URL to perform a preview for the form on the current screen
{$CURRENTLY_INVISIBLE} Whether the current user is set to be invisible
{$CAN_SPELLCHECK} Whether spellchecking is possible
{$SESSION} Get the session ID for the current member
{$VERSION_NUMBER} Get the Composr version number
{$CHARSET} Get the character set
{$DECIMAL_POINT} Find the decimal point character for the current locale (e.g. .)
{$HEADER_TEXT} Get the header text (put in the page title)
{$SHOW_HEADER} Whether the header will be shown
{$SHOW_FOOTER} Whether the footer will be shown
{$WIDE} Whether the side panels will be shown
{$WIDE_HIGH} Whether the side panels, header, and footer, will be shown
{$LOGO_URL} The logo URL for the current zone
{$HAS_SU} Whether the current user is allowed to use the Switch User feature
{$STAFF_ACTIONS} A drop-down of contextual actions for the current screen
{$HELPER_PANEL_TUTORIAL} The helper panel's tutorial
{$HELPER_PANEL_TEXT} The helper panel's text
{$MESSAGES_TOP} Attached messages, for prominent display
{$MESSAGES_BOTTOM} Attached messages, for less prominent display
{$LATE_MESSAGES} Attached messages that happened after output started (after main page generation). These will usually be errors relating to the output process itself.
{$BREADCRUMBS} The breadcrumbs
{$FORUM_CONTEXT} The ID of the forum currently within, or blank
{$MATURITY_FILTER_REQUESTED} Do a header check, to see if family-friendly browsing experience was requested by the user's safe header preference. This symbol only works when Composr is installed on a server with the PHP Apache module, unless a new HTTP_PREFER server variable is added (see apache-request-headers – read callum85's comment for details).
{$TAPATALK} Whether output is being generated for a Tapatalk client


Placement, and general Composr interfacing

Symbol name / example Purpose
{$FIND_SCRIPT} Find the URL to a named Composr entry script (e.g. dload)
{$FIND_SCRIPT_NOHTTP} Find the URL to a named Composr entry script (e.g. dload), but as a relative URL to domain name root
{$MAKE_URL_ABSOLUTE,someurl} Take what may be a relative URL (relative to site base URL), and make sure it is an absolute URL
{$ENSURE_PROTOCOL_SUITABILITY,someurl} Make sure a URL is safe to embed in the current page if the current page is HTTPS, e.g. by proxying a non-HTTPS URL
{$IMG,bottom} Find a named theme image code's URL (takes the theme image code). Note that this gets the image URL, it does not write the HTML out for an image element based upon it (you need to do that yourself).
{$IMG_MTIME,bottom} Find the last-modification unix timestamp for a theme image. This is useful for adding a cache-busting parameter after an image URL and ?, e.g. <img src="{$IMG*,bottom}?{$IMG_MTIME%,bottom}" />.
{$IMG_INLINE,bottom} Find a named theme image code's data URL. It works the same as {$IMG,...} except the image data gets embedded directly. If there's more than 32kb of data, the normal URL will be used instead.
{$IMG_WIDTH,bottom} Find the width of the theme image/URL (particularly useful for making web pages render faster). Uses the persistent cache to remember values, if possible.
{$IMG_HEIGHT,bottom} Find the height of the theme image/URL (particularly useful for making web pages render faster). Uses the persistent cache to remember values, if possible.
{$CDN_FILTER,URL} Take an absolute URL, under the main base URL's domain, and change it to work under the Content Delivery Network. Only use this if you know the particular URL you pass will be available via the CDN.
{$KEEP,0,1} Stuff to append to a Composr URL for 'keep parameter' passing (Composr passes any parameters between screens if their names start keep_, and this symbol enables that). The first parameter should be '1' if you need to place this directly on the front of a URL. The second parameter should '1' if you need to always attach a session ID (i.e. if you are creating a link that might be used somewhere outside the current cookie context, e.g. to feed into a download manager)
{$EXTEND_URL,http://example.com/index.php?foo=a,bar=b} Add parameter(s) onto a URL. Starts by appending ? or & by analysis of existing URL structure. Does not handle URL encoding of the appended parameter, which you should do first if applicable.
{$BLOCK,block=main_search,zone=site} Place a block into the template. Takes any number of parameters (but at least 1 for the block name), each of which defines a block parameter and its value. Parameters must include the block name, which is encoded against the special reserved name block (not just placed in there as a lone parameter). For example: {$BLOCK,block=main_news,filter=1\,2\,3}
{$LOAD_PAGE,about,site} Load a page into a template (first parameter is the page name, optional second parameter is the zone name which defaults to the current zone)
{$RATING,downloads,3} Show a content type's rating. The first parameter is the content-type's code (usually the page name it's viewed from, e.g. downloads) and the second parameter is the ID for the resource.
{$NUM_RATINGS,downloads,3} Find the number of users who have rated something.
{$SHOW_RATINGS,downloads,3,100} Show the last 100 ratings on something.
{$ALREADY_RATED,downloads,3} Find if the current user has already rated something.
{$VIEWS,download_downloads,download_views,3} Show how many views a content type has had. The first parameter is the table to read from, the second parameter is the name of the field (it must contain 'views' -- the code will not allow arbitrary field calling for security reasons) the third parameter is the ID for the resource.
{$REVIEW_STATUS,download,3} Show the periodic content review status for the given content, if configured for the content and set as publicly visible.
{$LOAD_PANEL,panel_left,site} Load a panel-page into a template (first parameter is the page name, optional second parameter is the zone name which defaults to the current zone)
{$PAGE_LINK,site:about} Link to a page, using page-link syntax (e.g. "_SELF:pagename:type:1")
{$ADDON_INSTALLED,galleries} Find whether an addon is installed (first parameter is the code-name of the addon)
{$AWARD_ID,2} Find the content ID assigned to the given award
{$NOTIFICATIONS_ENABLED,download,3} Find whether notifications are enabled for something
{$NOTIFICATIONS_AVAILABLE,download} Find whether notifications are allowed for this notification type (i.e. not locked down)
{$LAST_VISIT_TIME} The last visit time of the current user
{$NUM_NEW_TOPICS} The number of topics posted since the last time the current user visited
{$NUM_NEW_POSTS} The number of posts posted since the last time the current user visited
{$SESSION_HASHED} A hashed version of the session ID, used as a unique identifier without revealing the user's session
{$IS_HTTPAUTH_LOGIN} Whether the user has an HTTP-auth login
{$MEMBER_PROFILE_URL,3} Get the URL to a member profile
{$LOGIN_LABEL} The label for the username field when logging in (the label depends on how login options are configured, e.g. Username, E-mail address, Username / E-mail address)
{$STOCK_CHECK,Product id} Get the stock count for an eCommerce product
{$IS_FRIEND,2,3} Detect if the second parameter/member ID (or current member if no second parameter) has befriended the first parameter/member ID.


Composr configuration querying

Symbol name / example Purpose
{$SITE_NAME} The site name
{$HEADER_TEXT} The header text of the current screen, taken from current zone's setting unless content is being viewed specifies it
{$ZONE_HEADER_TEXT} The header text of the current zone
{$COPYRIGHT} The site copyright
{$STAFF_ADDRESS_PURE} The staff address, without obfuscation
{$STAFF_ADDRESS} The obfuscated staff address
{$DOMAIN} The domain name of the site
{$SITE_SCOPE} The site scope
{$FORUM_BASE_URL} The forum base URL
{$BASE_URL} The base URL to the installation
{$CUSTOM_BASE_URL} The base URL to custom data in the installation (usually the same as BASE_URL)
{$BASE_URL_NOHTTP} / {$CUSTOM_BASE_URL_NOHTTP} The base URL to the installation, but just the path, no protocol or domain name. This is useful in JS files (which cache as strings, not Tempcode) because it doesn't cause JS security errors when HTTPS and HTTP variants of a site are both in action.
{$SSW} Whether Sunday is the start of the week (Tempcode boolean)
{$VALID_FILE_TYPES} Get a comma-separated list of valid file types
{$INLINE_STATS} Whether to show view/download counts inside the public visible website (Tempcode boolean)
{$SHOW_DOCS} Whether documentation links are configured to be shown
{$CONFIG_OPTION,optionname} Get the value of a config option
{$VALUE_OPTION,optionname} Get the value of a hidden option/value
{$HIDE_HELP_PANEL} Whether the help panel has been hidden
{$BRAND_NAME} The brand name, defaults to Composr
{$BRAND_BASE_URL} The brand URL, defaults to http://compo.sr
{$TUTORIAL_URL} A tutorial URL, linking under the brand URL's docs zone
{$COPPA_ON} Whether COPPA is configured to be on


Composr permission querying

Permission checks are useful if you want to show screen contents in a way that somehow reflects your configured permission scheme.
These are advanced symbols, and mirror Composr's internal API. The parameters are not discussed here, but you may find them from looking at the code.

Symbol name / example Purpose
{$IS_STAFF,2} Is the current user / given user staff (optionally give it a member ID else works on current member, returns Tempcode boolean)
{$IS_ADMIN,2} Is the current user / given user a super admin (optionally give it a member ID else works on current member, returns Tempcode boolean)
{$HAS_PRIVILEGE,submit_midrange_content} Member has a privilege (returns Tempcode boolean)
{$HAS_ZONE_ACCESS,adminzone} Member has access to a zone (returns Tempcode boolean)
{$HAS_PAGE_ACCESS,admin_stats,adminzone} Member has access to a page (but NOT necessarily the zone it is in) (returns Tempcode boolean)
{$HAS_ACTUAL_PAGE_ACCESS,admin_stats,adminzone} Member has access to a page and its zone (returns Tempcode boolean)
{$HAS_CATEGORY_ACCESS,downloads,3} Member has access to a category (returns Tempcode boolean)
{$HAS_SUBMIT_PERMISSION,mid,12.34.56.78,2,cms_example,category,345} Member has the specified kind of submit permission (returns Tempcode boolean). Parameters: range, IP address, member, CMS page, category, category_id.
{$HAS_DELETE_PERMISSION,mid,2,2,cms_example,category,345} Member has the specified kind of delete permission (returns Tempcode boolean). Parameters: range, owner, member, CMS page, category, category_id.
{$HAS_EDIT_PERMISSION,mid,2,2,cms_example,category,345} Member has the specified kind of edit permission (returns Tempcode boolean). Parameters: range, owner, member, CMS page, category, category_id.


Composr interfacing, specific/advanced features

Symbol name / example Purpose
{$TOTAL_POINTS,3} Get the total points the specified member has
{$POINTS_USED,3} Get the total points the specified member has used (spent).
{$AVAILABLE_POINTS,3} Get the total points in the specified member's account; some of these will probably have been spent already
{$CNS} Whether Conversr is being used (returns Tempcode boolean)
{$HAS_FORUM,General Chat} Whether a forum is available (returns Tempcode boolean)
{$VERSION} The major version of Composr being used
{$MEMBER_OVERRIDE} The Member-ID of the Account being viewed (e.g. if you're staff looking in somebody else's Account then this would find the member ID, otherwise it is your own member ID
{$COOKIE_PATH} Get the cookie save path
{$COOKIE_DOMAIN} Get the cookie save domain
{$IS_A_COOKIE_LOGIN} Find if a cookie login is active
{$SESSION_COOKIE_NAME} Get the name for the session cookie
{$BANNER,some_banner_type} Display a banner of specific type (first parameter is the banner type, second is an optional binary value for whether the banners should be limited to only internal banners)
{$ATTACHMENT_DOWNLOADS,3} Find how many downloads an attachment has had (first parameter is the ID number of an attachment)
{$COMMENT_COUNT,downloads,3} Find the number of comments made for a resource (first parameter is the resource type code, second parameter is the resource ID)
{$IMAGE_WIDTH,http://example.com/pic.png} Find the width of an image
{$IMAGE_HEIGHT,http://example.com/pic.png} Find the height of the image
{$THUMBNAIL,http://example.com/pic.png,40x60,galleries} Generate a thumbnail URL from another image URL
{$SUPPORTS_FRACTIONAL_EDITABLE,cms:cms_example,1} Whether some content has permissions to be editable inline, via the FRACTIONAL_EDITABLE directive. The '1' is if an access check passed (if omitted, does an Admin Zone access check instead).
{$GROUP_ID,3} Find a member's primary usergroup
{$GROUP_NAME,4} Find the name of a members nth usergroup
{$URL_FOR_GET_FORM,someurl} Strips out the query string parameters from a URL, so that this URL can be used as the action for a GET form
{$HIDDENS_FOR_GET_FORM,someurl} Takes the query string parameters of a URL and turns them into hidden form inputs, so that a GET form will relay them
{$FIND_ID_VIA_URL_MONIKER,<content-type>,<url-moniker>} Finds the ID for a given content type's URL moniker
{$FIND_URL_MONIKER_VIA_ID,<page>,<type>,<id>} Finds the URL moniker for an item of content's type/ID
{$FIND_GUID_VIA_ID,<resource-type>,<resource-id>} Find the resource GUID from the resource ID
{$FIND_COMMANDRFS_FILENAME_VIA_ID,<resource-type>,<resource-id>} Find the Commandr-fs (repository) filename from the resource ID. There is an optional third parameter, which may be set to 1 if you want the resource sub-path to also be included. Sub-paths are relative to the repository-fs path for the resource-type.
{$FIND_LABEL_VIA_ID,<resource-type>,<resource-id>} Find the resource label from the resource ID
{$FIND_ID_VIA_LABEL,<resource-type>,<label>} Find the resource ID from the resource label
{$FIND_ID_VIA_GUID,<guid>} Find the resource ID from the resource GUID. It is assumed you as the programmer already know the resource-type.
{$FIND_ID_VIA_COMMANDRFS_FILENAME,<resource-type>,<filename>} Find the resource ID from the Commandr-fs (repository) filename
{$DISPLAY_CONCEPT,concept_name} Displays a link to a concept, if it has been saved already from a concept table.
{$SELF_TUTORIAL_LINK,tutorial_id,page-link} Creates a tutorial link for the given tutorial_id to the given page-link. Needs doing in Tempcode because the Comcode parser doesn't know the target page for Comcode being parsed.
{$CPF_LIST,<field-title>} Find all the possible values for a custom profile field in a loop-iterable string
{$AUTHOR_MEMBER,author_name} Find the member ID associated with an author, or guest ID if none


Thumbnail generation

The {$THUMBNAIL} tag is actually much more sophisticated and powerful than described above. It's full syntax is as follows…

Usage: {$THUMBNAIL,source_image,widthxheight,output_dir,filename,fallback_image,type,where,background,only_make_smaller}
where:
  • source_image is the URL to the full image
  • widthxheight is the desired width/height for the thumbnail. For types (see below) of width or height or crop you can omit either of the width or height for automatically calculated proportionality (e.g. x20 for just a height). If you specify both for crop it crops to the exact specified box rather than to the implied proportional box.
  • output_dir is the name of a directory under Composr's uploads directory
  • filename is the name of the thumbnail filename. If not provided it will be automatically chosen based on the source_image and an articulation of the thumbnail settings
  • fallback_image is the URL to the image to use if no thumbnail could be generated. If not provided the source_image will be used
  • type is either:
    • box for an image which should be maximally fitted into the given box dimensions (the default)
    • width for an image which is stretched to the given width and a height in proportion to that
    • height for an image which is stretched to the given height and a width in proportion to that
    • crop for an image which has been shrunk until one of its dimensions matched that given and the rest has been chopped off
    • pad for an image which has been shrunk until it completely fits within the given dimensions, and then padded out
    • pad_vert_crop_vert for an image that may only have cropping or padding done above/below and therefore gets stretched to force that to be the case
    • pad_horiz_crop_horiz for an image that may only have cropping or padding done left/right and therefore gets stretched to force that to be the case
  • The where argument is not needed for width or height type thumbnails. For crop & pad types it is where to crop/pad:
    • start
    • end
    • both (assuming bottom-to-top and right-to-left)
    • If the where option is omitted for cropped thumbnails then it will be centred. For padded thumbnails it will resize the image to get rid of any need for padding (ie. "padding none").
  • For pad types the background is used to pad the image:
    • a hex code for a specific colour (with alpha)
    • or, none for no padding (ie. the image dimensions are not those given, but will fit inside them).
    • or, if the option is omitted for padded thumbnails then the average colour of the image is used.
  • only_make_smaller can be set to "1" if the image should not be resized at all if it is already smaller than the requested thumbnail size

Logic

Note that all logic symbols return Tempcode boolean values (except ?). This makes them suitable for nesting with each other, or using with the 'IF' directive.
Understanding of these symbols requires an understanding of basic boolean operations but they are fairly intuitive (e.g. 'AND' will return true if all its parameters are true).

Symbol name / example Purpose
{$EQ,a,b} Test for string equality (pass in any number of strings to check they are all the same)
{$NEQ,a,b} Test for string inequality (this is the same as {$NOT,{$EQ,...}})
{$NOT,1} Boolean NOT (pass in one Tempcode boolean to get the opposite boolean out)
{$OR,1,1} Boolean OR (pass in any number of Tempcode booleans to check that one is true)
{$NOR,1,1} Boolean NOR (this is the same as {$NOT,{$OR,...}}). NOR means "Neither"
{$XOR,1,1} Boolean XOR (pass in two Tempcode booleans to check that just one of them is true but not both of them). XOR means "one or the other but not both"
{$AND,1,1} Boolean AND (pass in any number of Tempcode booleans to check they are all true)
{$NAND,1,1} Boolean NAND (this is the same as {$NOT,{$AND,...}}). NAND means "Not both"
{$LT,1,2} Test for numerical less-than inequality (pass in a Tempcode number)
{$GT,1,2} Test for numerical greater-than inequality (pass in a Tempcode number)
{$IS_EMPTY,this obviously is not blank} Find whether a string is blank
{$IS_NON_EMPTY,this obviously is not blank} Find whether a string is not blank
{$?,1,shows if true,shows if not true} Do an inline IF test, outputting the second parameter if the first evaluates to true and the third (optional parameter) if it does not

{$?,0,yes,no}no
{$?,1,yes,no}yes
{$?,0,yes} → (blank)
{$?,1,yes}yes


Arithmetic

Symbol name / example Purpose
{$NEGATE,3} Negate a number
{$MULT,3,3} Multiply two numbers
{$ROUND,3.2} Round a number
{$MAX,3,4} Find the maximum between two numbers
{$MIN,3,4} Find the minimum between two numbers
{$MOD,3} Make the given number positive (e.g. -3 becomes 3, 3 becomes 3)
{$REM,10,3} Find the remainder if the first number is divided by the second
{$DIV,10,3} Divide two numbers to get an integer result
{$DIV_CEIL,10,3} Divide two numbers to get an integer result, but round up
{$DIV_FLOAT,10,3} Do a division to get a decimal result
{$SUBTRACT,4,3} Perform a subtraction
{$ADD,4,3} Add two numbers


String manipulation

Symbol name / example Purpose
{$REPLACE,exemplar,something,this is exemplar} Do a string replace (three parameters: what's being replaced, what's being replaced with, what's being replaced in)
{$AT,something,3} Extract a string character (first parameter is the string, second parameter is the index of the character to get counting from 0)
{$STRPOS,something,hi} Find the position of a string sub-string (first parameter is the search string, second parameter is the substring, returns Tempcode number)
{$IN_STR,something,in} Find whether a sub-string is in a string (first parameter is the search string, second parameter is the substring, returns Tempcode boolean
{$SUBSTR_COUNT,abacus,a} Find how many times a substring appears (first parameter is the search string, second parameter is the substring, returns Tempcode number)
{$SUBSTR,something,2,2} Extract a string sub-string (first parameter is the string, second parameter is the index of where to extract from, third parameter is the extraction length)
{$LENGTH,something} Find the length of a string (give it a string, returns a Tempcode number)
{$PAD_LEFT,12,4,0} Pad something out on the left. This example produces 0012
{$PAD_RIGHT,12,4,0} Pad something out on the right. This example produces 1200
{$WCASE,A b C} Convert string to word case
{$LCASE,A b C} Convert string to lower case
{$UCASE,A b C} Convert string to upper case
{$WORDWRAP,blah blah blah,4,<br />,1} Word-wrap a string (first parameter is the string, second parameter is the maximum line length, optional third parameter is the divider string which defaults to an HTML line-break, optional fourth parameter is a binary value specifying whether to force hard cuts)
{$PREG_MATCH,\d,1 2 3} See if something matches a regular expression (first parameter is the regexp, second parameter is the subject string)
{$PREG_REPLACE,\d,a,1 2 3} Perform a regular expression replace (first parameter is the regexp, second parameter is the replacement string, third parameter is the subject string)
{$COMMA_LIST_REVERSE,a\,b\,c} Reverse a comma-separated list
{$COMMA_LIST_GET,a=1\,b=2,a} Find a value from a comma-separated list.
{$COMMA_LIST_SET,a=1\,b=2,a,2} Set a value in a comma-separated list.
{$COMMA_LIST_FROM_BREADCRUMBS} Convert breadcrumbs to a comma-separated list.
{$COMMA_LIST_POP,a\,b\,c} Remove the last element from a comma-separated list and throw that element away.
{$COMMA_LIST_SHIFT,a\,b\,c} Remove the first element from a comma-separated list and throw that element away.
{$COMMA_LIST_PUSH,a\,b\,c,d} Add an element to the end of a comma-separated list.
{$COMMA_LIST_UNSHIFT,b\,c\,d,a} Add an element to the start of a comma-separated list.
{$COMMA_LIST_NICIFY,a\,b\,c} Put a space after commas in a comma-separated list.
{$NUMBER_FORMAT,1234} Make a number (integer) look nice, with commas etc (takes a Tempcode number)
{$FLOAT_FORMAT,1234.5678} Make a decimal number look nice, with commas etc, to 2 decimal places (takes a Tempcode number)
{$TEMPCODE,{$MEMBER}} Evaluates the given parameter as Tempcode (takes a string). This is useful for very rare situations where you want to write in symbols as parameters to Comcode tags where the parameter is treated as a plain-string. This is only useful in Comcode, when typing Tempcode you can nest Tempcode constructs almost however you like.
{$COMCODE,[b]Comcode[/b]} Evaluates the given parameter as Comcode (takes a string). This is useful in conjunction with the above (TEMPCODE), as anything passed into a symbol won't be evaluated as Comcode.
{$URLDECODE,a%20b} Remove URL encoding


Tempcode variables

You can store and retrieve values in Tempcode variables. These work across templates, and also work in any-order due to the Composr Tempcode pre-processor (i.e. something can be SET after the GET for it).

Symbol name / example Purpose
{$ISSET,test} Find whether a Tempcode variable is set (give it the name of a variable, returns Tempcode boolean)
{$INIT,test,1} Initialise a Tempcode variable to a value, but only if it is not yet set (give it the name of a variable, and a value, returns nothing)
{$SET,test,1} Set a Tempcode variable to a value (give it the name of a variable, and a value, returns nothing). Bear in mind that the Composr Tempcode-preprocessor will likely cause your code to run once in every code branch before it outputs (meaning even code inside IF directives that never are true will still pre-process), and once again as it outputs. So always reinitialise your variables in your template to make sure they are not dirty from the pre-processing stage. This behaviour may seem strange, but it is necessary for performance, comprehensive caching, and reasons of Composr needing conditionless output foresight.
{$GET,test} Get the value of a Tempcode variable (give it the name of a variable, returns the value). If you set the 2nd parameter to "1" then the Tempcode cache will be reset for the value being received (allowing it to re-evaluate the value, i.e. operate fully dynamically). This must be set on all GET calls to the same value, otherwise the Tempcode may be flattened prematurely. The - must be used to do the initial SET.
{$THEME_WIZARD_COLOR,#RRGGBB,name,equation} Sets a theme wizard colour that can be retrieved from name as a Tempcode variable. The equation is actually ignored during Tempcode evaluation but is picked up by the Theme Wizard.
{$INC,test} Increment a Tempcode variable (give it the name of a variable, returns nothing)
{$DEC,test} Decrement a Tempcode variable (give it the name of a variable, returns nothing)


Directives

Note that you cannot use directives for symbol parameters because directives control code flow rather than inline calculations. The way Tempcode logic is internally structured cannot parse/hold it. You can almost always use the <kbd>?</kbd> symbol to guide inline calculations instead.

Directive name Purpose
BOX These allow the 'standard box' layout convention to be used without duplicating a lot of HTML in different templates. CSS could never provide this itself, as the box 'styling' actually involves a large chunk of complex structure.
The parameters to the directive are (all may be blank or left out):
  1. the title (blank means no title is used)
  2. the type of the table (there will be a template, STANDARDBOX_<type> for any valid type code; the default is default). default and accordion come as standard.
  3. the CSS width
  4. '|' separated list of options (meaning dependant upon templates interpretation)
  5. '|' separated list of meta information (key|value|key|value|...) (e.g. Name|Allen|Gender|Male|Country|USA)
  6. '|' separated list of link information (linkhtml|...) (e.g. <a href="http://example.com">Example</a>|<a href="http://google.com">Example</a>)
  7. the links added to the top left of the box (Defaults to false)
CSS_INHERIT Include another theme's CSS into your CSS file, so you only need to specify differences. The parameters are: CSS file name, theme name, seed colour [optional]. The contents of the directive should be left blank.
INCLUDE Include a template in another and pass parameters (this makes a template very much like a function in a conventional programming language). Note that the automated smart-decaching (for developers manually editing template files) will not work on manually edited included templates because Composr's optimiser means that the actual template is "compiled into" the template that calls it and thus not regularly individually checked. If you try to include a template into itself, and don't give any new parameters, Composr will include the default version of that template in (if you do give parameters, or are running HipHop-PHP, you'll probably get a fatal error instead)
WHILE Do a loop until the Tempcode boolean used as the directive parameter evaluates to false.
LOOP Do a loop over an array. This is not used much in default templates and only works if arrays are actually what is passed into the template (unless the third operating mode is used, see below). It operates in 3 possible ways:
  1. With a template parameter that is a simple 1-dimensional list of strings. E.g. 'EXAMPLE'=>array('a','b','c') could be looped via {+START,LOOP,EXAMPLE}...{+END}. Within the directive {_loop_key} and {_loop_var} are bound (so it's a little like a PHP foreach loop).
  2. With a template parameter that is a 2-dimensional array structure, a list of maps of key names to strings. E.g. 'EXAMPLE'=>array(array('FOO'=>'a1', 'BAR'=>'b1'), array('FOO'=>'a2', 'BAR'=>'b2')) could be looped via {+START,LOOP,EXAMPLE}...{+END}. Within the directive {_loop_key} and {FOO} and {BAR} are bound.
  3. With no template parameter, just a hard-coded list within the template. E.g. {+START,LOOP,a\,b\,c}...{+END}. Within the directive {_loop_key} and {_loop_var} are bound (so it's a little like a PHP foreach loop with hard-coded data). The "\" is required to encode a comma-separated list within a single directive parameter.
If also has support for automatic HTML table columnisation and sorting, as an advanced feature (very rarely used). If you want to use that check at the implementation in the PHP code to see how it works.
IF_PASSED/IF_NON_PASSED Find if a named parameter was passed (Do not encase the parameter with {})
IF_PASSED_AND_TRUE/IF_NON_PASSED_OR_FALSE Find if a named parameter was passed and its truth state (useful for robust optional boolean parameters that default to false)
IF_EMPTY/IF_NON_EMPTY Find if some Tempcode evaluates to empty. This is extremely useful, as it allows you to, for example, now show a table if it has no rows passed in. From a programming point of view, it allows us to avoid making new templates and improve accessibility compliance.
IF_ARRAY_EMPTY/IF_ARRAY_NON_EMPTY Find if an array is empty.
IF This simply tests a condition. This condition is often generated by nesting symbols together. For example, {+START,IF,{$EQ,{A},{B}}}...{+END}
OF This is a non-wrapping directive that extracts a string from an array at a certain array position.
IMPLODE This is a non-wrapping directive that turns an array into a string by placing a separator between each element.
COUNT Find the number of elements in an array.
SET Like the SET symbol, but sets with surrounded block of code rather than the second parameter.
RECONTEXTUALISE_IDS Prefixes all the IDs contained within (prefix is given as the first parameter). Reassociates form labels correctly. This directive is useful if you need to have the same form on an HTML page more than once.
PARAM_INFO Shows all parameters in the current scope. Very useful for debugging what goes into a template. Use without and directive contents like, {+START,PARAM_INFO}{+END}
FRACTIONAL_EDITABLE,The [i]Title[/i],title,cms:cms_example,1,1,1 Used to make a string of some content editable inline (usually, the title). The first '1' means to support Comcode. The second '1' means include visible editing links (as opposed to needing a magic keypress). The third '1' is if an access check passed (if omitted, does an Admin Zone access check instead). The inner content of the directive is the displayed title (e.g. parsed Comcode, or at least HTML-escaped) – it is what would be there anyway if there was no directive.
IN_ARRAY See if something is contained in an array, returning a boolean. This only works with arrays passed into templates (not something like a delimitated-list). This is rarely used because it can't be embedded easily within wider expressions. Usually you will use IF_IN_ARRAY.
IF_IN_ARRAY,example,PASSED_IN_ARRAY,output if it is Output something if something is in an array. This only works with arrays passed into templates (not something like a delimitated-list). First parameter is a string to check for, second parameter is the name of an array, third parameter is what to output.
IF_NOT_IN_ARRAY,example,PASSED_IN_ARRAY,output if it is not Opposite of IF_IN_ARRAY
NO_PREPROCESS Don't do any pre-processing under this directive. This improves performance for complex Tempcode trees (especially for conditionally displayed blocks), but it does mean that things like CSS and JavaScript includes that are required by blocks may not be detected.
CASES Show different output based on the value of something
COMMENT A comment (i.e. does nothing)
NO_PREPROCESSING Avoid preprocessing of anything inside the directive. This is needed for complex cases such as when you need to do heavy optimisation of unusual code paths, or unusual GET/SET situations with shared variables which may not always exist (and need to be evaluated therefore in a strictly linear way).


Directive examples

The most important directives are:
  • IF_NON_EMPTY
  • IF_EMPTY
  • IF

IF_NON_EMPTY

This directive is commonly used when a box or explanation needs placing around something, unless that something happens to be blank (empty).
For example, the following is a fairly typical thing to see in a template:

Code (HTML)

{+START,IF_NON_EMPTY,{DOWNLOADS}}
        <p>We have the following downloads:</p>
        {DOWNLOADS}
{+END}
 

The IF_NON_EMPTY directive evaluates {DOWNLOADS}, to see if it is blank. If it is not blank, the contents of the directive is output, otherwise nothing happens.
As you can see from this example, we need to check for emptiness so we don't show a boxed message about downloads that refers to nothingness.

IF_EMPTY

An alternative approach to the above example is to include a special message upon emptiness, rather than skipping over. We might do this if the content being displayed is critical and thus the emptiness must be explicitly acknowledged:

Code (HTML)

<p>We have the following downloads:</p>
{DOWNLOADS}
{+START,IF_EMPTY,{DOWNLOADS}}
        <p class="nothing_here">(none)</p>
{+END}
 

Note here how we are displaying {DOWNLOADS} even if it is empty. Obviously if it is empty, nothing would display, so we can get away with it. If we were being pedantic we might have wrote the following:

Code (HTML)

<p>We have the following downloads:</p>
{+START,IF_NON_EMPTY,{DOWNLOADS}}
        {DOWNLOADS}
{+END}
{+START,IF_EMPTY,{DOWNLOADS}}
        <p class="nothing_here">(none)</p>
{+END}
 
and arguably this does better demonstrate what we're trying to do, even though the behaviour is the same (ignoring invisible differences in HTML white-space).

It's often useful to be a bit clever with the emptiness-checking directives. For example:

Code (HTML)

<p>We have the following downloads and images:</p>
{DOWNLOADS}
{IMAGES}
{+START,IF_EMPTY,{DOWNLOADS}{IMAGES}}
        <p class="nothing_here">(none)</p>
{+END}
 

In this example, the directive checks if the {DOWNLOADS}{IMAGES} is blank, which is exactly the same as checking if they are both blank (because blank next to blank is still just blank).

IF

The 'IF' directive allows particular complex calculations to be done. For a simple example, we can replicate the trick in the last example in a more formal way:

Code (HTML)

<p>We have the following downloads and images:</p>
{DOWNLOADS}
{IMAGES}
{+START,IF,{$AND,{$IS_EMPTY,{DOWNLOADS}},{$IS_EMPTY,{IMAGES}}}}
        <p class="nothing_here">(none)</p>
{+END}
 

This checks to see if "DOWNLOADS is empty and IMAGES is empty".

Tempcode uses what is called a "prefix notation", and is similar to the way you write mathematical functions. For example, in common maths:

Code

f(a,b)
is how you would write "evaluate function 'f' with parameters 'a' and 'b'.
In the similar Tempcode notation you would write (assuming 'A' and 'B' are template parameters and 'F' is a real Tempcode symbol):

Code

{$F,{A},{B}}

Here's another 'IF' directive showing a more complex computation:

Code (HTML)

{+START,IF,{$LT,{ADD_DATE_RAW},{$SUBTRACT,{$FROM_TIMESTAMP},86400}}}
        <p>This content on {$SITE_NAME} is more than one day old.</p>
{+END}
 

Background: a unix timestamp is a common measurement of time on computers – it represents the number of seconds since 1970 and because of its nature as a simple decimal number, is very useful for performing arithmetic.
This example checks to see if ADD_DATE_RAW is less than the current unix timestamp minus 86400. 86400 is the number of seconds in a day, so in other words the example checks to see if ADD_DATE_RAW is at least a day in the past. Because ADD_DATE_RAW is often passed into templates that display content, it checks to see if the content is at least a day old.

I threw {$SITE_NAME} into the example just to demonstrate symbols are not only for use in IF directives – this one is being used to perform a simple placement of the website name. All symbols do is compute results, so those results might be used for further computations as parameters to computational symbols (such as 'SUBTRACT'), or they might be passed into a directive, or they might be simply output. It merely depends on where they are placed.

Tempcode is extremely powerful, and hopefully now you can see how symbols, directives, and parameters, can come together to allow great things to happen!

See tempcode_test.txt for more examples on how to use symbols and directives.


CASES

Code

Capital city:
{+START,CASES,{COUNTRY}}
   UK=London
   Ireland=Dublin
   =Unknown
{+END}

Based on the value of "Country", a different value would be shown. We have coded cases for the UK and Ireland, and anything else would default to "Unknown".

Mode #1 is substring matching:

Code

Healthiness:
{+START,CASES,{FOOD_ITEM},1}
   Cake=Unhealthy
   Pie=Unhealthy
   Salad=Healthy
   =Unknown
{+END}
(Imagine FOOD_ITEM contains something like Chocolate Cake or Banoffee Pie.)

Mode #2 is regular expression matching:

Code

Head of state:
{+START,CASES,{COUNTRY},2}
   ^(Canada|Australia|United Kingdom)$=King/Queen
   ^USA$=POTUS
   =Unknown
{+END}

Encapsulation

You can encapsulate Tempcode within comments of other languages, in order to not break editor syntax highlighting, and so WYSIWYG editors don't corrupt the code.

Examples…

HTML comments…

Code (HTML)

<!-- {+START,IF,{$MOBILE}} -->
Example
<!-- {+END} -->
 

CSS comments…

Code (CSS)

/* {+START,IF,{$MOBILE}} */
Example
/* {+END} */
 

HTML attributes…

Code (HTML)

<span x-tempcode-1="{+START,IF,{$MOBILE}}" class="mobile" x-tempcode-2="{+END}">Example</span>
 

Detailed worked examples

This section will provide real-world examples of using Tempcode. It goes into a minimal amount of PHP programming to provide a context for using a new template file and a context as to how Comcode pages, blocks, and templates, hold together. It explains things in terms of direct file editing rather than editing using the forms within Composr.

If you are not comfortable with creating new code files you may want to glance over those parts. Ordinary themers would normally work by editing existing template files, or by placing Tempcode directly onto Comcode pages.

Creating a new block

Lets start by adding a new sample block to our front page so that we can test different examples of Tempcode. Go to the sources_custom/blocks folder and create and add a new empty file named main_first_block.php.

Now lets add something to our block file. Edit main_first_block.php using your favorite text editor and add the following:

Code (PHP)

<?php

class Block_main_first_block
{
        function run()
        {
                return do_template('BLOCK_MAIN_FIRST_BLOCK',array());
        }
}
 

In this code we are simply creating a new class for our block and then defining the standard run() method which is executed when the block is invoked. We are calling a template file BLOCK_MAIN_FIRST_BLOCK.tpl in the run() method using the do_template() function. This tells the system to use this template file for our block. Our next step is creating a template file for the block.

Creating a template file

Go to the themes/default folder. There you should find two main templates folder templates and templates_custom. The main templates folder has all the pre-defined templates which the system is using already (†) so we don't have to make any changes there. The templates_custom folder will have all the user defined templates or the customised templates, so our template file will go there.
Create a new blank file BLOCK_MAIN_FIRST_BLOCK.tpl and save it in to templates_custom. .tpl is the default extension we give to template files.

† Actually this isn't quite true. The css[_custom], javascript[_custom] and text[_custom] folders also hold templates. Even CSS files are handled as templates in Composr.

Adding the block to front page

Now that we have the block and template file ready, lets add the block to our front page so that we can test the examples. To add a block to the front page we will have to make the changes in the comcode file start.txt which contains all the blocks which show up on the front page.
start.txt file is located in pages/comcode_custom/EN (the same rule as templates applies here as well, i.e. the customised comcode files should go in to _custom folder).

Image

Our empty box

Our empty box

(Click to enlarge)

Edit start.txt file using your text editor, and add this on the end:

Code

[box="first block"][block]main_first_block[/block][/box]

This Comcode adds the block to our front page.

The box defines the outer section of the block and the string parameter passed to it ("first block") will be placed in the title of our box. We could have instead done the box inside the template using Tempcode or direct HTML if we had chosen to, but we wanted to mix some extra Comcode in with our example too.

The block tag includes our new block. Within the block tags we have provided the name of our new block, main_first_block.

If you go to your front page now you should see that a new box has been added with the title "first block" on the top. The box is empty right now as we are yet to add any content to the [block's] template file,

Now that we have a block set up on the front page, we can Test some Tempcode examples here by simply adding code to our template file (BLOCK_MAIN_FIRST_BLOCK.tpl).

Let's proceed to our first example…

Set randomisation example

Image

Set randomisation example

Set randomisation example

(Click to enlarge)

This example demonstrates how a built-in Tempcode construct, the SET_RAND symbol, can be used to randomly make a selection from given options.

Go ahead and paste the following example code into our BLOCK_MAIN_FIRST_BLOCK.tpl file and reload the front page to see the results.

Code (HTML)

<p style="color: {$SET_RAND,red,green,blue,#EE1167};">
        I think you are a {$SET_RAND,lovely,beautiful,groovy} person.<br />
        We randomly selected a word, and randomly picked a text colour.
</p>
 

(Caching should not be an issue as our test block has not defined any block caching, and (assume you haven't disabled it) the smart cache should detect the template has changed.)

This would resolve to some HTML such as:

Code (HTML)

<p style="color: green">
        I think you are a groovy person.<br />
        We randomly selected a word, and randomly picked a text colour.
</p>
 

Number randomisation example

Image

Number randomisation example. We had to refresh a few times to see this, because as expected it was empty 3/4 of the time.

Number randomisation example. We had to refresh a few times to see this, because as expected it was empty 3/4 of the time.

(Click to enlarge)

In this example we use the symbol RAND to pick a random number between 0 and 3 and the number. This number will then be passed to an IF directive that checks if the number is less than 1 using the LT symbol. Go ahead and try this in the template file to see the results.

Code (HTML)

{+START,IF,{$LT,{$RAND,3},1}}
        <p>
                This shows 1/4 of the time. How does it work? RAND picks a random number between 0 and 3, so the number will be less than 1 1/4 of the time.
        </p>
{+END}
 

Obfuscation example

Tempcode is also equipped with security features such as obfuscating an e-mail, getting an obfuscated mailto: string which makes it hard for e-mail scavengers to read it – hence preventing spam.

Code (HTML)

<p>
    I want to let people {$OBFUSCATE,mailto:me@example.com}, but I don't want spam so I will confuse spam-bots.
</p>
 

Truncation example

The TRUNCATE_LEFT symbol can be used to trim data from the left, the TRUNCATE_RIGHT symbol to do the same from right, and the TRUNCATE_SPREAD symbol to keep an equal left/right portion.

All the three functions have the same parameter order, which is as follows:

Code

{$TRUNCATE_LEFT, data, truncation length, optional binary value to show a tool tip, optional binary value for whether the input text is already in HTML format}

Code (HTML)

<p>
    {$TRUNCATE_LEFT,Sometimes you get data that is too long for your layout.,10,1,1}
</p>
 

Match-key example

Image

Match-key example, running from start page (nothing would be seen if we placed the block on another page)

Match-key example, running from start page (nothing would be seen if we placed the block on another page)

(Click to enlarge)

We can use that to show a specific message only if a match-key is satisfied.

A match-key is basically the same thing as a page-link. The only difference is that a match-key is used for matching and will also match any page-links with additional parameters not mentioned in the match-key – i.e. the match is performed against the parameters in the match-key, all other parameters are ignored. As you are probably already comfortable with the term "page-link", we'll use that henceforth.

Code (HTML)

{+START,IF,{$MATCH_KEY_MATCH,site:start}}
        <p>
                I want this displayed only for the start page<br />
                (the page-link means "'site' zone, 'start' page)
        </p>
{+END}
 

You can test this next example in the DOWNLOAD_CATEGORY_SCREEN.tpl file:

Code (HTML)

{+START,IF,{$MATCH_KEY_MATCH,site:downloads:browse:3}}
        <p>
                Maybe I want to add this message to DOWNLOAD_CATEGORY_SCREEN.tpl but to only display if viewing download category 3.<br />
                (the page-link means "'site' zone, 'downloads' page, 'browse' screen type, '3' ID)
        </p>
{+END}
 

This is an alternative way of achieving the same result as above. We perform a string equality (using the EQ symbol) on the id parameter; if it is equal then the message will be displayed.

Code (HTML)

{+START,IF,{$EQ,{$_GET,id},3}}
        <p>
                Or we could do it this way, given DOWNLOAD_CATEGORY_SCREEN.tpl is always under site:downloads:browse anyway, and the 'id' parameter is the only thing altering.
        </p>
{+END}
 

User-agent detection example

It's sometimes useful to know which browser is being used by the user to view the web page. We might have to make some tweaks specifically for browsers like Internet Explorer to ensure cross-browser compatibility – or perhaps we want to give out messages to users to tell them to upgrade their browsers. Ideally we would never have to do this, but the world of web browsers is kind of the wild-west, and things are always advancing and changing.

Code (HTML)

{+START,IF,{$BROWSER_MATCHES,ie}}
        <p>
                You're an Internet Explorer user.
        </p>
{+END}
 

Admin check example

Image

Admin check example (we're logged in as admin so we do see it)

Admin check example (we&#039;re logged in as admin so we do see it)

(Click to enlarge)

This example shows how we could display messages or alerts specifically for admins.

Code (HTML)

{+START,IF,{$IS_ADMIN}}
        <p>
                Only admins see this.
        </p>
{+END}
 

Group membership example

This functionality is similar to the above IS_ADMIN, but it works by usergroup. If the member belongs to any of usergroups 1, 3, 4 or 5, then the message will be displayed.

Image

Group membership example (viewed from a test user in one of these usergroups)

Group membership example (viewed from a test user in one of these usergroups)

(Click to enlarge)

Code (HTML)

{+START,IF,{$IS_IN_GROUP,1,3-5}}
        <p>
                Only members in groups 1/3/4/5 see this.
        </p>
{+END}
 

(here we used Selectcode to show how we can do a range)

Guest check example

This example shows different content to guests and non-guests:

Code (HTML)

{+START,IF,{$IS_GUEST}}
        <p>
                Only guests see this.
        </p>
{+END}
{+START,IF,{$NOT,{$IS_GUEST}}}
        <p>
                Only non-guests see this (you are {$USERNAME*}).
        </p>
{+END}
 

Thumbnail example

This example is dynamically changing the image resolution of your avatar, using the THUMBNAIL symbol and the AVATAR sybmol. The second parameter of the THUMBNAIL symbol defines the resolution we are generating.

Code (HTML)

<img alt="Your avatar made into a really tiny thumbnail" title="Your avatar made into a really tiny thumbnail" src="{$THUMBNAIL*,{$AVATAR},20x20}" />
 

Zebra striping example

Image

Zebra striping example

Zebra striping example

(Click to enlarge)

This example shows some zebra striping, within a set of boxes put out by a loop.

We can use loops within Tempcode using the LOOP directive, and the loop variable can be fetched within the loop using the _loop_var parameter.

This example is not about loops; we just use loops as a way of putting out multiple boxes – really we are trying to show how zebra striping can work. Zebra striping can also work between multiple attached instances of the same template, which is a far more common example, but would have been a bit harder for us to demonstrate via sample code.

In this example, it loops from A to I and in the loop it uses the CYCLE symbol to go through the parameters passed to it (two colours) in a sequence. The first loop iteration will result in a div element with a background property set to the first value (i.e #EEE) and _loop_var will display the current loop value for that (i.e. A). Then it proceeds through the loop, but the cycle is repeating between just two values (our zebra stripes) even though the loop goes on for nine values.

Code (HTML)

<div>
        Zebra-striping by setting up a named cycle (we use the loop to simulate an arbitrary repeating situation to use the cycle within):
        {+START,LOOP,A\,B\,C\,D\,E\,F\,G\,H\,I}
                <div style="background: {$CYCLE,bgcol,#EEE,#AAA}">
                        {_loop_var}
                </div>
        {+END}
</div>
 

GUIDs

There is a design issue that comes to us when we design template structure… do we re-use templates so that editing is easier, load is decreased, and consistency is raised; or do we allow maximum customisation of different aspects of the system by not re-using templates?
We have tended to stick to a middle ground between these extremes, and re-used templates when the usage pattern was almost identical. For example, almost all of Composr uses the same form field templates, but Wiki+ posts use different templates to forum posts. However, there are still places where we re-use templates in situations that sites may wish to develop in separate directions.

The solution to this is to make use of the GUID each template usage is given when it is used.
Each do_template call in Composr, which loads up a template, passes in a parameter, _GUID. The GUIDs may then be used along with tempcode directives to control what the template outputs.
The template editor has special support for the GUIDs, showing what GUIDs a template is used with and providing links to view the code associated with each GUID. It also provides a feature to automatically insert an 'IF' directive that differentiates against one of the GUIDs to provide a place to put output that will only be shown for it.

If you enter the template editor from the 'Template tree' screen then you will be told the GUID that was used by the node in the tree that you selected.

Concepts

Symbol
A special element that may be inserted into
Parameter
A parameter to a template; only parameters that the code calling the template supports may be used
Directive
A tool used to surround an area of a template between a START and an END, to apply something to it (such as only using it conditionally)
Variable
A named piece of memory used by Tempcode symbols that manipulate variables, or informally some internal other piece of Composr memory that Tempcode has a way of accessing
Construct
A Tempcode symbol, parameter, or language reference

See also


Feedback

Please rate this tutorial:
Item has a rating of 5 (Liked by KingBast)

Have a suggestion? Report an issue on the tracker.

Back to Top