General CMS features are covered in the ExSite 4 Content Model documentation, but there are a few points of interest to developers, that are worth covering here.
V4 prefers static content views, where possible. The system supports hourly, daily, and weekly publish rules, in addition to static and dynamic. These can help to keep your static content refreshed automatically even when it changes unpredictably (for example, upcoming events, new posts, member directories).
Personalized pages are harder to render as static views. If the personalizations are small, you can sometimes move the dynamic elements into Javascript (the Login plugin does this, for instance).
V4 allows for intelligent determination of whether a content object should be static or dynamic. If the publish field is not set, the system will call the object's publish_rule_heuristic() method to determine the most appropriate publish rule to use. For example, the default heuristic is:
For example, event views can change day-to-day, as registrations open and close. In v3, that meant that they had to be dynamic, but they would continue to be dynamic even after events were over. In v4, events will automatically assume their publish rule is "daily" before the event if there is registration, and "static" after the event. That means you get best results by not setting any publish rule at all, which allows it to be flexible enough to change the rule depending on the date.
If only a specific content object on the page needs to be dynamic or access-controlled, you can set the access or publish rule on that content only. The main page will be static, and the dynamic content will be fetched with javascript. If the object is an image, and access is denied, it will be replaced with an access denied image.
Note that if your page is completely dynamic, it will still generate a static file containing a meta-redirect to the dynamic view. That means static URLs should always work for simple GET requests.
Publishing will be invoked automatically by certain workflow operations (like "publish", obviously). In some cases, those workflow operations can be initiated by unprivileged users. For example, if comments are accepted without moderation, then threads and/or forum indexes will republish automatically when they are submitted. You must pass an approve=>1 flag to the publish() call to approve such operations.
NOTE: CMS tags in this section have been altered by adding additional spaces so that they do not get processed by the CMS. The actual tags should have those spaces removed.
You do not necessarily have to do your own URL generation when linking to content.
You can request a specific view when you link to content using [ [content] ]
tags to get the URL. For instance, say you have a content object called image, then you can link to the normal default view using [ [image] ]
. But if you have a high-res version on file, you can link to that instead, using [ [image:large] ]
.
You can also force dynamic views and even select specific revisions, using tags like [ [image?revision=REV] ]
where REV is a revision name (newest, draft, etc) or number.
Similarly, { {page?query_string} }
will substitute a dynamic link to the page, with the appropriate query string settings. If you just use { {?query_string} }
, the page will link to itself. { {} }
will link to itself with no query string setting, but will use a static URL, while { {!} }
will always link to a dynamic URL. { {&Module} }
will link to the service page for a module, so you can link to the shopping cart using { {&Pay} }
, for instance.
The metadata insertion tags < !--$special-->
have a few new options:
< !--$page_header-->
and < !--$page_footer-->
are used for appending to the head section of the document, as well as to the end of the document. If your plug-in has some additions that belong properly in the head section (for example, style blocks, or metadata), then do this:
$share{page_header} .= $head_out;
Similarly, if your plug-in has some additional that are better put at the end of the document (for example, javascript code that should load after other content has loaded), then do this:
$share{page_footer} .= $foot_out;
Those %share variables will automatically be substituted in where the < !--$page_header-->
and < !--$page_footer-->
tags are inserted. If you didn't include your own page_footer tag, it will append to the end of the HTML document.
Note that ExSite automatically puts stuff into these variables. For example, optional javascript will get added to page_footer so that it runs after the page is loaded. For this reason, always append to these %share variables so you don't wipe out any existing settings.
The < !--$metadata-->
tag will be replaced with a full set of meta tags, depending on which ones have been preconfigured to display automatically. To include meta tags automatically, use a setting like
content.metadata.page.description.show = 1 # ExSite content-specific metadata
content.metadata._.twitter:card.show =1 # generic metadata
All such meta tags with a show setting will be included, if they have valid explicit or implicit content.
To include standard Javascript in your output (on pages or control panels), use:
$out .= &insert_js($script1, $script2, $script3, ... );
so that we can avoid inserting javascript that has already been included somewhere else. $script can be:
/_ExSite/js
, such as misc or httprequestIf you are are including any standard javascript in your front-end website templates, you can mark them as included using config settings like
content.js.bootstrap = 1
Then any attempt by a plugin to include bootstrap will do nothing, since it knows it was already loaded by the template.
You can track what CSS files have been inserted in a similar way:
$out .= &insert_css($css);
or, to put the style links/blocks in the document head where they belong:
$share{page_header} .= &insert_css($css);
$css
can be:
/_ExSite/css
, such as Report.css
(the suffix is optional)The dispatcher is a new v4 feature that tries to gang together multiuple AJAX calls into a single AJAX call, for more efficient AJAX communications. You do not need to do anything to enable this feature, except set
content.ajax_method = dispatch
which should be the default.
The dispatcher replaces all module AJAX calls, such as those invoked using single and double-AJAX notation like
< !--&&Module()-->
with a single call to dispatch(). This aggregates all of the AJAX calls into a single http request, and returns the results in a JSON structure. This structure is parsed, and the various outputs are dispatched back to their appropriate page elements. That means that a page should only ever generate one AJAX hit on the server, no matter how many AJAX modules are invoked on the page.
AJAX posts will be dispatched separately, however.