HTML Logo by World Wide Web Consortium (www.w3.org). Click to learn more about our commitment to accessibility and standards.

Moving forward with Composr

ocPortal has been relaunched as Composr CMS, which is now in beta. ocPortal 9 will be superseded by Composr 10.

Head over to compo.sr for our new site, and to our migration roadmap. Existing ocPortal member accounts have been mirrored.


ocPortal Code Book

For ocPortal version 9.0
« Return to Code Book table of contents


Front-end

Design/copy Coding Standards

ocProducts maintains a checklist for standards. These standards used to be listed in this Code Book, but there are now too many to clearly explain here, and we needed a document we could morph and use on a routine basis for quick review.

Standard language strings you can re-use

The following language strings from global.ini and critical_errors.ini are commonly reused:
  • Nothing here: MISSING_RESOURCE, NO_ENTRIES, NO_CATEGORIES, NONE, NONE_EM
  • Actions: PROCEED, SAVE, SET, CHOOSE, USE, ADD, EDIT, DELETE, SEND, VIEW, MORE, BROWSE, _JOIN, _LOGIN
  • Choices and records: YES, NO, EXISTING, NEW, OLD
  • Users and usergroups: SUBMITTER, USERNAME, PASSWORD, GROUP, _USERGROUP, BY, BY_SIMPLE, GUEST, MEMBER
  • URLs, uploads and thumbnails: URL, IMAGE_URL, UPLOAD, THUMBNAIL
  • Resources: PAGE, ENTRY, CATEGORY
  • Table columns/fields, Identifiers: IDENTIFIER, CODENAME, TITLE, NAME
  • Table columns/fields, Properties: SIZE, DATE, DATE_TIME, TYPE, VALIDATED
  • Table columns/fields, Other: AMOUNT, FROM, TO, REASON, PARAMETER_A, PARAMETER_B
  • Rating, sorting, browsing and results: RATING, SORT, SORT_BY, RESULT, _RESULTS, START, DONE, ROOT, NEXT, PREVIOUS

Web development Coding Standards

ocProducts maintains a checklist for standards. These standards used to be listed in this Code Book, but there are now too many to clearly explain here, and we needed a document we could morph and use on a routine basis for quick review.

Templates and themes

Template files are stored in a raw format, and are defined with the standard 'override system' of ocPortal; as well as this, though, a theme only has to define those templates and css files which have been changed from the 'default' theme. Search, parsing, and applying linguistic translation, to templates like this would not be efficient, so ocPortal compiles the templates upon first-use (unless this option is disabled).

The ultimate compilation target for templates is 'serialised tempcode'; if the template is cached as this, it will be loaded direct into memory, and it will be bound to the parameters of the template (for example, a download box template might take the download name and URL as parameters).

Tempcode is ocPortal'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 a powerful set of control mechanisms.

Tempcode syntax

The syntax for Tempcode (the ocPortal language used for controlling template output) has the following basic syntax:
  • {X} means "Insert parameter X" (parameters are essentially variables which are passed into the template by the PHP code).
  • {!X} means "Insert language string X".
  • {!X,BLAH} means "Insert language element X with parameter BLAH". BLAH will usually be a parameter itself, so it might look like: {!X,BLAH,{SOME_PARAM}}. (often language strings will have a place for such a parameter, for instance: A_SITE_ABOUT=A site about {1})
  • {$X} means "Insert symbol X". The symbols are not PHP variables (although the syntax is similar, because it is a similar concept). An example of a symbol is 'BASE_URL' (so to use this write {$BASE_URL}). Symbols can also be used to perform functions, such as equality checks.
  • {+START,BLAH}...{+END} means "Wrap (…) with a directive named BLAH". A directive can be anything that does something to the code it wraps. There are a number of directives that you may use, including output filters such as 'IF', loops, and boxes.
This syntax is explained in more detail in the main ocPortal documentation.

Any Tempcode construct may be escaped (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) by escaping filters with ease, just by placing the symbol 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:
  • (*) HTML (e.g. Hello & Goodbye –> Hello & Goodbye)
  • (;) Between single quotes (e.g. Who's here –> Who\'s here)
  • (#) Between double quotes (e.g. She said, "Hello" –> She said, \"Hello\")
  • (~) Where new lines are not allowed (text is drawn up to reside on a single line)
  • (@) Comcode (e.g. Use the [url] tag –> Use the \[url] tag)
  • (/) Special Javascript SGML-issue (e.g. print('</p>'); –> print('<\/p'))
  • (^) Where new lines become "\n" (multiple lines drawn together with \n as a separator)
  • (|) Javascript Ids (e.g. This is a -terrible- ID –> This__is__a____terrible____ID)
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 ocPortal templates actually use HTML escaping so that plain text placed inside a template does not interfere with the HTML structure itself and displays literally.

A full description of the Tempcode language is available in the Tempcode programming tutorial.

WCAG notes

Adherence to the following guidelines can't be automatically detected, so need to be checked manually in your XHTML:
  • <noscript> is given whenever appropriate and possible
  • When plugins are used, info about it must be displayed
  • When an appropriate markup language exists, use markup rather than images to convey information.
  • Mark up lists and list items properly.
  • Ensure that all information conveyed with color is also available without color, for example from context or markup.
  • <blockquote> may not used for non-quoting

The following guidelines must be adhered to by webmasters themselves:
  • Until user agents allow users to freeze moving content, avoid movement in pages and Until user agents allow users to control flickering, avoid causing the screen to flicker. By default, nothing flickers, but Comcode allows it. It's a question of whether a site is designed to be accessible for all, or 'fancy' for the majority
  • Alternatives given to multimedia content
  • Use the clearest and simplest language appropriate for a site's content.
  • Divide large blocks of information into more manageable groups where natural and appropriate.
  • Specify the expansion of each abbreviation or acronym in a document where it first occurs.
  • Place distinguishing information at the beginning of headings, paragraphs, lists, etc.


PNG images

As a standard ocPortal uses 32-bit PNG files over other image types, unless:
  • animation is required (.gif used instead)
  • background image transparency is required (8-bit .png used instead)
  • where the image needs to be given a size different to it's natural dimensions (.gif used instead)

We use PNG files as they have the added advantage that they can be 'alpha-blended' for smooth, blended, visuals.

GIF files are:
  • limited to 256 colours
  • only have "binary transparency". so for smooth blending the image itself needs to be "pre-compiled" against the background colour of your website (which may be edited in the CSS, or be different in different areas of the site, so you really don't want pre-compilation assumptions).
  • can not be created by most versions of PHP, which rules out the ocPortal Theme Wizard from being able to modify them

JPEG files do not support transparency, and as we prefer to not assume any particular image won't be edited to have transparency, we opt to not use them. Also JPEG has serious problems when images are re-saved, which is a standard event when editing default images. Otherwise we acknowledge JPEG provides superior compression in most normal circumstances.

Besides the lack of animation, and problems on IE6, there is one further problem with PNG images- various web browsers, including Internet Explorer and Safari, are incompatible with the settings used by Photoshop to save them. The colours are always slightly wrong – noticeable if the images need to line up with background colours defined in the CSS. The problem is caused by the browsers not supporting PNG gamma settings properly. Fortunately this problem is solved by passing the images through a free compression-optimisation program called PNGGauntlet (for Windows; use OptiPNG on a Mac), which is worth doing anyway as it provides significant loss-less file-size savings over what Photoshop can do. Instructions for using PNG Gauntlet:
  • Backup your images, just in case something goes wrong.
  • Download PNGGauntlet and run it. Select RGB+Alpha as the output type, and minimise depth reduction. Do not preserve gamma information.
  • Tick (check) the 'Overwrite Original Files' checkbox.
  • In the file selection dialogue, choose to only show PNG files. We should ONLY be optimising these, otherwise PNGGauntlet will convert any GIFs and JPEGs to PNGs.
  • Start optimi[sz]ing. Choose your files. You must do the optimisation one directory at a time because PNGGauntlet will erase the task list each time you click 'Choose files to optimize'.
  • Once done, you may need to tell Windows to reset the permissions in the images folder from that of the parent folder (because PNGGauntlet will copy the compressed image in from a temp dir, with the permissions of that temp dir). If you don't do this, you may find you get a permission denied when viewing the images and lots of red crosses.

Javascript

Javascript is separate from Java, and built into web browsers directly. It is used to provide an interactive element to the user's experience in their web browser. ocPortal has a loose framework for producing templates that use Javascript.

Javascript libraries

ocPortal has an inbuilt set of Javascript libraries, which are split across a number of Javascript template files that each begin with the name 'JAVASCRIPT'. The core library that is loaded onto every screen is named just JAVASCRIPT.tpl. This core library is very general and doesn't know about ocPortal's individual modules- it is a general-purpose library to provide functionality usable anywhere. Most of the other Javascript libraries are specific to individual modules (e.g. the chat module uses JAVASCRIPT_CHAT.tpl) or to specific aspects of ocPortal (e.g. the Comcode editing interface uses JAVASCRIPT_EDITING.tpl). There are a few additional general purpose Javascript libraries:
  1. JAVASCRIPT_AJAX – AJAX call functionality, which should be used for all AJAX code.
  2. JAVASCRIPT_DRAGDROP – A drag and drop library (do not use this unless drag & drop really is the best way to build an interface, which is very rare)
  3. JAVASCRIPT_MORE – A supplementary library of useful functions that are not important enough to be loaded up for every screen. Consider making use of this if you are making a Javascript-intensive screen.
  4. JAVASCRIPT_TRANSITIONS – Provide various transition/animation effects.

To flag a javascript file to be loaded up, use the 'require_javascript' API command in the PHP code. For example, if you wanted to load up the Javascript defined in the 'JAVASCRIPT_MORE' template, use:
require_javascript('javascript_more');
This command is usually best placed:
  1. in screen functions if it is not used by all screens in your module
  2. or, in the run function if it is used by all/most screens in your module

Alternatively, you can load Javascript from a template with a command like:

Code

{$REQUIRE_JAVASCRIPT,javascript_more}

You may add new Javascript templates as needed. Just make sure to follow the naming convention.
When writing new Javascript code try to make good use of the existing functions that are defined in whatever Javascript you already have included. For example, use the standard cookie manipulation functions and standard AJAX functions, rather than making new ones. If existing functions are not sufficient consider improving those functions. If you are porting some existing Javascript libraries to ocPortal you may need to resolve the following:
  • After renaming files, you may find Javascript makes assumptions about the naming and paths to other files, such as images, or other complementary Javascript files. You'll need to adjust the associated code for that. In the case of references to images, it is best to use the ocPortal theme-image system, and thus reference them using the '{$IMG,...}' symbol.
  • You may find you need to put some "" symbols before some "{" symbols, to stop certain bits being parsed as Javascript.

In ocPortal, Javascript libraries are automatically minified to save on bandwidth, unless &keep_no_minify=1 is in the URL (very useful for debugging).

Useful ocPortal Javascript functions

  • Escaping: escape_html, escape_comcode
  • Cookies: set_cookie, read_cookie
  • Introspection: get_elements_by_class_name, abstract_get_computed_style, browser_matches
  • Calling up dependencies: load_snippet, require_javascript
  • Viewport: get_window_width, get_window_height, get_window_scroll_width, get_window_scroll_height, get_window_scroll_x, get_window_scroll_y
  • Element positioning: find_pos_x, find_pos_y, find_width, find_height
  • Tooltips: activate_tooltip
  • Misc: set_opacity, add_event_listener_abstract, cancel_bubbling, keep_stub, JAVASCRIPT_MORE: many useful string and array functions
  • HTML manipulation: get_inner_html, set_inner_html
  • AJAX: JAVASCRIPT_AJAX: do_ajax_request
  • Animation: toggleable_tray, smooth_scroll, JAVASCRIPT_TRANSITIONS: fade_transition

Integrating third-party Javascript libraries

If you're working on a standalone website you can just edit HEADER.tpl to include whatever Javascript calls you like. If you're working on an addon, however, you will want to code it into your addon to insert the necessary code into <head> dynamically. You can do this by setting $GLOBALS['EXTRA_HEAD'] to contain whatever Tempcode is required. Typical code will look like:

PHP code

global $EXTRA_HEAD;
if (
is_null($EXTRA_HEAD)) $EXTRA_HEAD=new ocp_tempcode();
$EXTRA_HEAD->attach(make_string_tempcode('Extra code goes here'));


Javascript event handlers

Add in Javascript event handlers to your HTML normally as required. For example, to make a popup window link:

Code

<a href="{BASE_URL*}/popup.htm"
   onclick="window.open(this.getAttribute('href'));">Click me</a>
It's acceptable to write inline event handler code like this as long as it doesn't get too long.

Sometimes it is not appropriate to add an event directly to an HTML node, usually for one of these reasons:
  • You can't always make the assumption that a node will only have a single event handler
  • Sometimes event handlers need putting on generic events such as 'onload'
  • Sometimes you want to add event handlers from inside Javascript code, on-the-fly
In these circumstances you can add the event to an HTML node indirectly by using the add_event_listener_abstract function. This function works consistently across browsers (so long as you don't try and use 'this' inside the event handler code).

Inline Javascript

It's acceptable to use inline Javascript (the 'script' tag) if it's only a short amount of code. This is a useful technique:
  1. if you want to pass template parameters directly into Javascript variables
  2. or, if you need the code to run as soon as the screen loads

You should use 'CDATA' for your <script> tag, like:

Code

<script type="text/javascript">// <![CDATA[
   ...
//]]></script>

It is important that:
  • You use the "//" bits. Usually ocPortal will be running as HTML even though it is marked-up as XHTML (this is a little known fact but is the case for almost websites out there, whatever they are running). HTML interprets everything in the script tag as CDATA automatically so everything inside there is parsed as Javascript; the "//" bits are simply Javascript comments to stop the CDATA bits being interpreted as Javascript code.
  • You should actually use CDATA so the document does validate as XHTML.
  • You should not put it all on one line, because then the Javascript line-comment will end up commenting all the code out and nothing will happen.
  • Don't use any ocPortal Javascript except for Javascript in the main 'JAVASCRIPT.tpl' library, unless you have put it inside an 'onload' event handler, like:

    Code

    add_event_listener_abstract(window,'load',function () { ... } );
  • This is because ocPortal does not want to pre-load any more Javascript library code than is necessary via loading it in <head> (it stops the page rendering at all until the files have been downloaded), and we can't assume the order Javascript will be loaded in compared to the order in which inline code is executed (it changes randomly, and between browsers). By putting our code inside an onload handler we force it to only run after all our dependent Javascript files have loaded.

Example: mixing event handlers, inline Javascript, and a library

This is a little example that does something completely pointless. This complex combination of event handlers, inline Javascript, and a Javascript library, should only be used if there is a reason for it. If there is no reason, simpler code is better. The complex example is shown here to show how different Javascript methods can fit together if they need to.

In the template:

Code

<script type="text/javascript">// <![CDATA[
/* Store a template parameter into a global variable.
Placing on the window object is the same as setting a global variable.
Writing it like this just makes it more clear it's a global variable which reduces the risk of obscure bugs.
Note the escaping here: It adds escaping for both the string quotes (;) as well as the CDATA section (/). This makes sure the parameter can not be used to create an XSS injection vulnerability. */
window.my_template_parameter='{MY_TEMPLATE_PARAMETER;/}';
//]]></script>

<a href="#" onclick="output_my_parameter();">Click me</a>

In the Javascript library (which must have been loaded using require_javascript):

Code

function output_my_parameter()
{
   // Output the global variable we made.
   window.alert(window.my_template_parameter);
}

Simpler example

The above example used global variables which is bad programming practice. I did it because it was a good example, but the following example is much better quality and simpler…

In the template:

Code

<a
   href="#"
   onclick="output_my_parameter('{MY_TEMPLATE_PARAMETER;*}');"
>Click me</a>
In the Javascript library:

Code

function output_my_parameter(message)
{
   window.alert(message);
}

Writing AJAX functionality

For adding an ajax functionality, we can use the functions defined in the JAVASCRIPT_AJAX.tpl template (in particular, do_ajax_request). For using those functions, we should include the javascript_ajax in code like:

Code

require_javascript('javascript_ajax');

Also we need our own Javascript template file to implement our AJAX-tied behaviour, because the code is probably going to be too long to include in-line with the HTML. For example, create a new file named JAVASCRIPT_EXAMPLE.tpl, which would be loaded up from PHP code by:

Code

require_javascript('javascript_example');

AJAX requests can be either synchronous or asynchronous. A synchronous request runs linearly: Javascript makes the call and then the browser will wait for the response before resuming execution. An asynchronous request involves a later (at an unknown point) 'call back' to a response handler (the "method") that we would write specially. Technically a synchronous-AJAX request is a contradiction (the 'A' in AJAX means 'asynchronous'), but the way people use the term AJAX means it's best we not worry about this. It's rare for us to use synchronous requests as it can make the browser freeze whilst it waits.

In our Javascript template file, we typically define basically two functions if we are writing asynchronous AJAX. One function is for initiating the AJAX event (can be any name you like) and another is to handle the response of the AJAX script (has any name, but it must be passed to the ocPortal do_ajax_request function). If you are writing synchronous AJAX you can pass the null value instead of a function, and the do_ajax_request function will respond with the result.

To handle the AJAX request on the server-side we will need to call a new entry-point script that we'll place in the 'data' folder; so we make a URL to this script, and of course we also need to write it.
In the data/<script-name>.php, we require_code a file that will contain a new PHP function to implement our server-side behaviour and then call that function (and nothing else). It is an ocPortal convention to define this function in sources/ajax.php, but we don't have to (and shouldn't if it's not a very core AJAX function to ocPortal). Remember if you are writing third-party functionality it should go into a file under 'sources_custom/' instead.

Our PHP function should return a response in XML (i.e. XML format, with a text/xml mime type) or for the very simplest use-cases in Text format (i.e. with a text/plain mime type). You can only return XHTML if you do it under an text/plain mime type. The only time in which XHTML is returned with a text/html mime type is if there was some kind of error and ocPortal outputs an error screen, in which case it would be automatically accompanied with an HTTP status code of '500', and any non-200 status code would prompt the AJAX framework to put out an error message. If you are writing your own webmaster-friendly error handling then you should put out errors in the XML frame ocPortal uses (described below), or you should encode your own error passing system into the pay-load data.

Note that you must use XML format in any of these situations:
  • You need to implement error handling and just returning a blank response upon some kind of known error would be insufficient.
  • You need to return structured data.
If in doubt, use XML.

The XML frame you should output from PHP should be structured as follows:

Code

<?xml version="1.0" encoding="charset"?>
<request>
   <result>
      (Your pay-load data goes here. It is likely this is XML,
      with your own specific set of tags,
      but it can be XML-encoded plain-text if you like.)
   </result>
   <message>
      <error>(Some kind of error message that will be put in an alert)</error>
      (Some kind of informational message that will be put in an alert)
   </message>
   <method>
      (Javascript function name that overrides what was passed to do_ajax_request)
   </method>
</request>

This example of the full usage. However normally you don't return any method or messages, so you will see real code is usually much simpler, like:

PHP code

header('Content-Type: text/xml; charset='.get_charset());
echo 
'<?xml version="1.0" encoding="'.get_charset().'"?'.'>';
echo 
'<request><result>';
echo 
xmlentities($out);
echo 
'</result></request>';


Once the server has returned a result to the client, it will be processed for handling.
  • For synchronous requests, the result object is returned from do_ajax_request linearly (asynchronous requests will return nothing from do_ajax_request). Code may then use the 'responseText' and 'responseXML' properties of this as required.
  • For asynchronous requests, the response handler (the 'method') function is called, with two parameters: ajax_result_frame (the full XML), and ajax_result (the pay-load XML). It is the responsibility for the method function to process the XML in ajax_result (ajax_result_frame is unlikely to be used by the method) – the code can assume the XML will be valid because HTTP status error handling and XML frame error handling will already have occurred.
There is an implied format contract between the PHP code we wrote and the Javascript we wrote. In other words, we are not likely to have incompatibilities between the data of the server (PHP) and client (Javascript) as you will have designed them together. Therefore you don't need to write any special error handling code to make sure the structure returned is correct, unless you've written in your own extra error handling into the pay-load.

Example:
  • Template – JAVASCRIPT_AJAX_PEOPLE_LISTS.tpl
  • Data file – data/namelike.php (calls namelike_script() defined in sources/ajax.php)
  • Ajax php script – source/ajax.php

Tip: If you develop on Firefox and have the Firebug addon installed, leave Firebug open when testing AJAX, as all the requests will come up in the Firebug console, and you can review them there to debug any server-side issues.

Advanced tip: You can either pass a function to the do_ajax_request function as your method, or you can pass an object that has a response function in it. The latter is useful if you want to make your response a neat part of an OOP approach.