Webcit Templating Engine
Developing Templates
If you run webcit with -T1, it will re-parse your template on every call, so you can instantly check back your edits. Note that it won't find new template files without a restart.
In general an URL of the format http://yourcitadel/do_template?template=yourtemplate will load yourtemplate.html from the static/t/ directory.
Most errormessages regarding the templates are rendered into your template on output, so if you find it jammed (because of for example your token is inside of quotes or so) viewing the page source will show you the full text; however, some errors can't be printed into the HTML. All errors are printed to webcits log facility, so running it from the commandline makes sense while developing templates.
Grammar
The webcit templating engine evaluates html templates at start time. It respects all tokens bordered with <?…>. The Tokentext is replaced.
- 'token' token is looked up
- First in the server global token / callback list
- Second in the session local token / callback list
- tokens may have parameters in braces like token(“some string”, 1337);
- Depending on the token, parameters may be ignored, optional or required.
- there are several special modifiers which change the context of the parameter:
- _”foo” : foo will be looked up in the gettext i18n database
- :“foo” : a string-setting with the name 'foo' will be looked up and be used instead
- ;”foo” : an integer-setting with the name 'foo' will be looked up and be used instead
- B”foo” : an URL/POST Parameter (bstr in citadel notation) will be used as parameter; so if you have '/somecall?foo=bar' bar will be used.
- string tokens may have modifiers as parametersso they're escaped
- Tokens may either trigger special callbacks in webcit or replace it with the content of a previously fed string.
Special Tokens
(tokenvalue in single quotes)
Subtemplates: '='
| Parameters | Description |
|---|---|
| 1 | subtemplatename; is is completed by _m.html or .html to be looked up on the template cache |
Gettext: '_'
| Parameters | Description |
|---|---|
| 1 | text to display in the users tongue; its retrieved via Gettext |
Conditionals '?', '!' or '%'
The '?' and '!' Conditionals consist of two tokens, which span a part of the template. All texts and tokens between the pair are skipped if the conditional snaps in. '?' and '!' Conditionals are paired by their second parameter, which has to be an identical number. The enclosed text is skipped if the condition is true *(?) or false (!).
'%' Conditionals don't span over a pair. They choose between two short texts, like you would do with an implicit if in c:
(cond)?a:b
You can use implicit parameters on the 'a'/'b' tokens, so their content is i18n'able or loadable from settings / url parameters… you name it. Their Signature is compatible to '?' and '!', plus the two possible strings; Pad Param 2, 3 and 4 with '1' if not needed.
| Parameters | Description |
|---|---|
| 1 | (string) Name of the conditional; the second conditional token may just be 'X', its ignored. |
| 2 | (integer) Identifier is a number pairing two tokens to a conditional. |
| 3,4 | Parameters for the individual conditional; documented with the detaildescription |
| 5 | (% conditional only) String to put in if result is true |
| 6 | (% conditional only) String to put in if result is false |
| Conditionalname | Context repquired? | Description |
|---|---|---|
| concerning this webcit | ||
| COND:IS_HTTPS | none | is the current template requested via HTTPS? |
| COND:BSTR | none | does this http-parameter say “yes”? |
| concerning this server | ||
| COND:SERV:OPENID | none | is the Serverside openid facility enabled? |
| COND:SERV:NEWU | none | does the Server allow self service account creation? |
| COND:SERV:SUPPORTS_GUEST | none | does this server allow guest logins? |
| COND:SERV:FULLTEXT_ENABLED | none | does this server have a fulltext indexer? |
| COND:SERV:LDAP_ENABLED | none | does this server authenticate against an ldap server? |
| COND:SERVCFG | creates servcfg | checks for a specific server config being set |
| COND:EXPIRE:MODE | none | which expiry mode is set for this server/floor/room |
| concerning this user | ||
| COND:LOGGEDIN | none | is this a logged in user? |
| COND:AIDE | none | Checks whether the user has AIDE privileges or not. |
| COND:ROOMAIDE | none | Checks whether the user has ROOM AIDE privileges or not. |
| COND:MAY_CREATE_ROOM | none | is the current user allowed to create new rooms? |
| COND:USERACCESS | USERLIST | compares the user accesslevel to parameter |
| COND:USERNAME | USERLIST | Checks whether the contexts userlists UID is the one specified by 'usernum' or the Username is the same as specified by 'username'. |
| COND:USER:PIC | none | does this user have an avatar set? |
| COND:USERLIST:FLAG:USE_INTERNET | USERLIST | does the context user have access to internet email? |
| concerning this users preferences | ||
| COND:PREF | none | Checks against the users preferences |
| COND:PREF:SET | none | Checks against the users preferences |
| COND:ICONBAR:ACTIVE | none | whether the user has configured this part of the iconbar to be active |
| COND:ROOM:SET | none | does the user have this setting for the current room enabled? |
| concerning this users session | ||
| COND:ICONBAR:WHOLISTEXPANDED | none | whether the user has opened the list of currently online users |
| COND:ICONBAR:ROOMLISTEXPANDED | none | whether the users has opened the room selection list |
| COND:PAGE:WAITING | none | is there an instant message waiting for this user? |
| COND:IMPMSG | none | do we have a status update for the user from some of his recent actions? |
| concerning the current room, or a room in the list iterated | ||
| COND:ACCESS:DELETE | none | does the user have delete rights in the current room? |
| COND:ALLOWED_DEFAULT_VIEW | none | is this view allowed as default view for this room? |
| COND:THISROOM:DEFAULT_VIEW | none | is this the default view for this room? |
| COND:ROOM:TYPE_IS | none | is this the type of this room? |
| COND:THISROOM:HAVE_VIEW | none | is this view allowed as view for this room? |
| COND:THISROOM:CURR_VIEW | none | is this view allowed the view for this room? |
| COND:ROOM:EDITACCESS | none | is the user allowed to change at least some of this rooms properties? |
| COND:THISROOM:FLAG:QR | none | is this flag set in the bitflag properties vector for this room? |
| COND:THISROOM:FLAG:QR2 | none | is this flag set in the bitflag properties vector for this room? |
| COND:THISROOM:EDIT | none | are we allowed to edit all the properties of this room? |
| COND:THISROOM:ORDER | none | sort direction for tihs room; 0=unsorted, 1=asc 2=desc TODO |
| COND:THISROOM:HAVE_PIC | none | does this room have a special image associated with it? |
| COND:THISROOM:HAVE_INFOTEXT | none | is there an info text set in this room? |
| COND:UNGOTO | none | do we have a room to go back to? |
| COND:WIKI:PAGE | none | TODO |
| COND:WIKI:TYPE | none | TODO |
| COND:FILE:ISPIC | CTX_FILELIST | is the current file an image? |
| COND:DAV:NS | none | WIP |
| COND:DAV:NSCURRENT | CTX_DAVNS | WIP |
| COND:ROOM:dav_CONTENT | CTX_DAVNS | WIP; whether this room has groupdav'eable content |
| COND:DAV:DEPTH | none | WIP |
| COND:REST:DEPTH | none | WIP |
| COND:ROOM:REST:ISSUBROOM | none | WIP |
| COND:FLOOR:ISSUBROOM | none | WIP |
| COND:FLOOR:NROOMS | none | WIP |
| COND:ROOM:REST:ISSUBFLOOR | none | WIP |
| COND:FLOOR:ISVIRTUAL | none | WIP |
| COND:LISTSUB:EXECUTE:SUBSCRIBE | none | WIP |
| COND:LISTSUB:EXECUTE:UNSUBSCRIBE | none | WIP |
| COND:LISTSUB:EXECUTE:CONFIRM:SUBSCRIBE | none | WIP |
Server Side implementation
Conditionals may be registered using
RegisterConditional(Hashkey, Keylen, NParameters, Callbackhook, int ContextTypeRequired)
If your conditional doesn't require a prepared context, put in CTX_NONE, else define your Context-type and put it there. The templating engine will refuse to evaluate this token, if the Context doesn't match; CTX_NONE tokens will be evaluated even if there is a context.
Callbackhook has to be like this:
int Callbackhook(WCTemplateToken *Tokens, void *Context, int ContextType)
- Token contains the pre-evaluated tokens for analysis
- Context may contain information of the execution status
- the return value provides the condition to the template parser.
ITERATOR s
Iterators are used to represent lists into the templates. They create a subcontext with a subtemplate, after evaluating all tokens of the subtemplate, the subtemplate is pasted to the main template.
| Parameters | Set To if unused/optional | Description |
|---|---|---|
| 1 | % | name of the iterator to evaluate |
| 2 | % | Subtemplate for each iteration |
| 3 | 0 | StartAt; set to != 0 if you want to skip values |
| 4 | 0 | StepWidth; set to != 0 if you want to jump forward in bigger steps |
| 5 | -1 | StopAt; set to != -1 if you want to stop before the end |
the iterator itself provides the following list of tokens to the subtemplate:
| Token | Type | Description |
|---|---|---|
| ITERATE:ODDEVEN | String | contains 'odd' or 'even' alternating. |
| ITERATE:KEY | String | contains the key to this hash-value |
| ITERATE:N | int | which place of the list are we? |
| ITERATE:LASTN | int | 0/1 if you're the last in the list |
Server side Implementation
Iterators may be registered using
RegisterIterator(Hashkey, Keylen, nAdditionalParams, StaticServerHashlist, GetHashCallback, SubTemplCallback, HashDestructorCallback, ContextTypeProvided, ContextTypeRequired)
- GetHashCallback Function to build the hashlist to iterate over
- SubTemplCallback Function to be called before the subtemplate is evaluated; should register tokens
- Destructor Function that may destroy the hashlist (maybe its a list available all server lifetime; may be NULL therefore)
HashList *GetHashCallback(void)
void SubTemplCallback(StrBuf *TemplBuffer, void *Context, Tokens)
- TemplBuffer contains the Stringbuffer with the subtemplate.
- Context holds the pointer to your hashlist entry
void Destructor(HashList *Killme)
- Killme can be deleted.
Global Replacers
| Tokenname | Content |
|---|---|
| ROOMNAME | The name of the room you're in |
| SERV:PID | The process id of the server |
| SERV:NODENAME | The name of this citadel |
| SERV:HUMANNODE | The nicer readable human node name |
| SERV:FQDN | The Full Quallified Domainname |
| SERV:SOFTWARE | The Server Revision |
| SERV:REV_LEVEL | The Server SVN Revision |
| SERV:BBS_CITY | Where does this citadel live |
| CURRENT_USER | Who is logged in here? |
| CURRENT_ROOM | where are you? |
DOBOXED
Renders a Box, with a subtemplate
| Parameters | Type | Description |
|---|---|---|
| 1 | String | Subtemplate to render into the box |
| 2 | String | (optional) Render another Subtemplate into the Boxtitle |
DOTABBED
Renders tabbed dialogs. Takes Pairs of values:
| Parameters | Type | Description |
|---|---|---|
| 1 | String | Render another Subtemplate into the Tabtitle |
| 2 | String | Subtemplate to render into the box |
Application specific codes
- Message rendering (SMTP/Mime messages, no vcard/ical/…)
- Room specific Since most of the time your Session is considered to be in a room…