LoginRegisterCommercial SupportContact Us


POD documentation > Content Management > ContentBase.pm

ContentBase.pm

Core class for the CMS
posted on 2:55 PM, July 12, 2009


ContentBase.pm : generic content-management methods

This is the base class for the ExSite core content management framework. (Core content pertains to basic web page content such as HTML, images, stylesheets, etc.) The base class contains tools that can be used/inherited by all CMS data types (ie. sites, pages, content definitions, and content data), as well as tools that deal with the entire content stack at once.

NOTE: CMS objects can exist in two forms: (1) as data objects (a hash of attribute keys/values, equivalent to a DB record), or (2) as code objects. The names of the data objects correspond to the names of the DB tables used to store their attributes. The names of the code objects correspond to the Perl modules/classes that define them. These names are similar, but do not match exactly:

+------------------------------+-----------------------------+ | DATA OBJECT | CODE OBJECT | +------------------------------+-----------------------------+ | section | Section | | page | Page | | content | Content | | content_data | ContentData | +------------------------------+-----------------------------+

It can be considered a bug that we use both conventions when referring to object types, not always consistently.

ContentData objects are often called ``revisions''.

new()

This is a general-purpose constructor that is used by all CMS classes. As such, it is generally called via one of the other CMS classes (ie. ContentData, Content, Page, or Section). Usage is typically:

    my $page = new ExSite::Page(id=>$page_id);      # numeric ID
    my $page = new ExSite::Page(page=>$page_data);  # page datahash

where ``Page'' can be replaced with the name of the code object, and ``page'' can be replaced with the name of the data object. For certain classes, other parameters are also allowed. See the documentation for those classes for details.


Context Handling

The ``context'' consists of the related elements in the content ``stack''. For instance, an image object will belong to a certain page, which will belong to a certain section; it will also have a current revision among the known revisions.

set_context()

    $this->set_context($mode,$revision);

The rules for identifying parents in the stack is simply to use the foreign key reference in the child record. The rules for identifying children in the stack are more complicated:

Setting the context consists of finding and loading the other objects in this stack. It is assumed that the current data object is already loaded.

$mode is none|all|parent|child, depending on whether one needs no context, full context, parent context, or child context only. ``all'' is most versatile; other modes may save a bit of work and be more efficient for particular cases.

$revision is ``current'' or ``active'' to select the latest approved/published revision of the content, or ``newest'' to select the latest revision even if not approved.

Many of the context-testing functions below will not return useful results if the context has not been set. However, we don't want to automatically set the context every time, because it is costly and many basic operations do not require it.

clear_context()

Removes any context settings that have already been made.

reset_context()

$this->reset_context($mode,$revision);

set_context() saves its results so that it will not execute again unnecessarily. reset_context() forces the context to be set over again. Parameters are the same as set_context()

set_revision()

$this->set_revision($revtype);

Sets the preferred revision, eg. ``active'' or ``newest''. (``active'' is the default)

get_revision()

Returns the preferred revision, eg. ``active'' or ``newest''. (``active'' is the default)

whatami()

Returns the type of the current object, one of ``Page'', ``Section'', ``Content'', or ``ContentData''.

is()

Tests if the current object is of a certain type. $type can be one of ``Page'', ``Section'', ``Content'', or ``ContentData''.

Example - given a content object $x:

if ($x->is("Page")) { # we are a Page object

has()

    if ($this->has($type)) { ...

Test if the context contains a particular type of CMS object. $type can be one of ``Page'', ``Section'', ``Content'', or ``ContentData''. Returns TRUE if the given type is found in the current object's context. If FALSE, the given type may be found if you call set_context().

Example - given a content object $x:

if ($x->has("Section")) { # we have our section data loaded into the context

has_content()

Returns TRUE if the object contains actual web content (ie. a revision). Returns FALSE if not, or if the context has not been set.

get()

    my $value = $this->get($type,$attribute);

Returns the value of the given attribute, for the given object $type in the context. $type can be one of ``Page'', ``Section'', ``Content'', or ``ContentData''. $attribute can be any attribute of the $type. If $attribute is not given, all attributes are returned in a datahash.

Example - given a content object called $img, get the filename of the page it is located in:

my $filename = $img->get("page","filename");

Or, get all attriubtes of the page it is located in:

my $page = $img->get("page");

If $type is not a context item, then we will try to return an attribute of the object that is named $type.

id()

    my $id = $this->id($type);

Returns the numeric ID of the requested $type in the context.

$type can be one of ``Page'', ``Section'', ``Content'', or ``ContentData'', which must be loaded into the context. If $type is not given, returns the numeric ID of the current object.

Example - given a content object called $img:

    my $id = $img->id();            # ID of the image
    my $page_id = $img->id("page"); # ID of the image's page


CMS Access Control

Access control in the ExSite CMS is normally handled on a section-by-section basis. Each section has its own set of managers, who can each have the following types of access to the section:

editorial
The manager can create, update, and delete editorial objects.

design
The manager can create, update, and delete design objects and templates.

administrator
The manager can create, delete, and publish pages.

Who has manager access to a section, and what types of access they have, is determined by their administrator keys to the section. An administrator key is a hash with the following keys/values:

member_id
The UID of the user whom this key belongs to.

section_id
The section ID the key is for.

priveleges
A plain text list of the types of access (above) that are granted by this key. (Yes, it is spelled wrong.)

If you have a group of items that require different access privileges from the rest of the section, consider placing these items into their own section and issuing a new set of keys for them.

get_keys()

Returns the key(s) that the current user has for the current section. They are returned in an array (or arrayref in scalar context), even if there is only one.

allow_update()

    if ($this->allow_update($type,$data)) { ...

Returns TRUE if the user can modify the $type, which can be one of ``page'', ``section'', ``content'', or ``content_data''. $data is the attribute datahash for the $type, if not using the one in the context.

allow_insert()

    if ($this->allow_insert($type,$data)) { ...

Returns TRUE if the user can create a new $type, which can be one of ``page'', ``section'', ``content'', or ``content_data''. $data is the attribute datahash for the $type.

allow_delete()

    if ($this->allow_delete($type,$data)) {...

Returns TRUE if the user can delete the $type, which can be one of ``page'', ``section'', ``content'', or ``content_data''. $data is the attribute datahash for the $type, if not using the one in the context.

owns()

Returns TRUE if the user owns the object, that is, if the user has an administrator key for the section that the object belongs to.


Content Searches

When building a page, ExSite encounters various tags that reference content objects such as images, HTML, etc. ExSite must search for matching objects to resolve these references, within the parameters of the current context. (For example, there could be many index.html pages in the system; which one is meant depends on where one begins looking for it.)

find()

    $this->find($name,$mode);

Locates a named content object, starting from the current context (which will be set automatically if not already set). The object is sought in the following locations:

    - in current page's content objects
    - in templates' content objects, including inherited templates
    - in content libraries for this section
    - in content libraries for ancestral sections

The first eligible match satisfies the algorithm, and searching will stop.

Returns the found object's attribute hash if $mode is ``data''. If $mode is ``object'' (default), find() will return a new content object.

find_prefetch()

    $this->find_prefetch($name,$mode);

Performs the actual content search for find() by looking through the prefetch() cache. This is efficient, but it will perform a more costly search directly in the database if the prefetch cache appears not to contain anything useful.

find_db()

    $this->find_db($name,$mode);

Performs the actual content search for find() by looking through the database. This may require many queries, and is substantially slower than find_prefetch(). However, it is guaranteed to find the content, whereas the prefetch cache could in principle be out of sync with the database.

find_db_nonlocal()

    $this->find_db_nonlocal($name,$mode);

Content searches that exhaust the current section may have parent sections that share content, or public templates/libraries that can also be searched. This method searches external sections for missing content.

find_page()

    my $page = $this->find_page($name,$section_id);

Returns the attribute hash for a page with filename $name. The $section_id may optionally be specified, if looking outside the current context.

find_content()

    my $content = $this->find_content($name,$page_id);

Returns the attribute hash for a content object named $name. The

$page_id may optionally be specified, if looking outside the current context. This is used by the more powerful find() method to search individual pages, templates, and libraries for matching content.

find_content_data()

    my $cdata = $this->find_content_data($type,$content_id);

Returns the attribute hash for the revision in the content object indicated by $content_id. $content_id is optional, and only needed in the case where the content object is different from that in the context.


Metadata

CMS objects can carry around various metadata. These data are largely descriptive, and have no functional purpose within the CMS. However, they can be included in HTML output, and may be significant for other purposes such as search engine indexing. Metadata are stored in a hash table, indexed under various keys.

To include metadata in HTML output, you can include a tag like:

    <!--$metadata_key-->

which will be replaced with the metadata value for that key. For example, to insert the title into the HTML, use:

    <!--$title-->

fetch_metadata()

Resets/loads the default metadata for a CMS object. The metadata for any CMS object is initialized to the contents of the current page record. The contents of the section record are added to this. All section keys are prepended with ``site_'' so they will not collide with the page keys. For instance, use the key title to get the page title, or site_title to get the section title.

set_metadata()

$this->set_metadata($key,$value);

You can add to the metadata, or change default metadata values with this method. If you re-use a key that is already set by fetch_metadata(), then you replace the default value. This is typically done by plug-ins that want to adjust page titles and descriptions to reflect specialized data objects that they are displaying, rather than rely on the generic page title. For example:

    $share{Page}->set_metadata("title","Foo Bar");


Content Expansion

get_start_html()

    my $html = $this->get_start_html($mode);

Initalizes the HTML representation of an object.

Page content objects are typically built starting from an HTML framework that comes out of a template. This template might be precompiled, which means that it resides on disk as a partially-processed HTML file. If not precompiled, then it resides in the database as raw HTML. This method identifies and returns the appropriate block of initial HTML to begin building the page from.

For non-page content objects, this simply returns an HTML representation of the object.

expand()

    my $html = $this->expand(%options);

Expands the content in the current context, by replacing CMS tags with the URLs or the HTML content that they represent. Returns the expanded for the content.

Options:

html

Use this as the starting HTML, instead of using the context.

content
a hash of content names and values to use preferentially over those in the database

method
a combination of data, url, content, page, module, depending on which expansions you want to apply to this content. All are selected, by default. You can also specify dummy-content or

dummy-module to substitute placeholder graphics instead of full content.

expand
if set to ``template'', we only expand the template/design content. Remaining tags are left in place. DCD tags are left unexpanded.

cms
if true, include CMS tools in the output, for interactive page editing.

mode
``dynamic'' (fetch everything from DB source) or ``static'' (default; fetches published and precompiled versions where possible)

get_page_url()

    my $url = $this->get_page_url($id);

Return replacement text for a {{$id}} CMS tag. $id refers to a page--tags such as {{123}} or {{index.html}} will be replaced with the correct URL to the indicated page (referenced by a numeric page ID, or the page file name).

get_content_url()

    my $url = $this->get_content_url($name,$option);

Return replacement text for a [[$name]] CMS tag. $name is the name of a content object--tags such as [[image.gif]] will be replaced with the correct URL to the indicated content object. $option is a reference to an option hash that accepts the following parameters:

mode
static|dynamic - force the URL to fetch the published or database revisions of the content, respectively. This is optional; the system defaults to static, if it is available, dynamic otherwise.

expand
template - expand only the template elements; leave page-specific content unexpanded.

get_content()

    my $html = $this->get_content($name,$option);

Return replacement HTML for a <!--content(...)--> CMS tag

This is similar to get_content_url(), except the full HTML is substituted instead of just the URL. Accepts the same options as expand(), and acts on the following:

content
This can point to a hash of predefined content definitions, which you can use to override the content database.

method
Same as the method item passed to expand(). The ``dummy-content'' method is the only one used in this method.

expand
``template'' if we are precompiling a template, and wish to leave page-specific data unexpanded.

cms
Inserts interactive page editing tools, if true.

placeholder_image()

    my $imgtag = $this->placeholder_image($content_id);

This generates an HTML image tag that can be used as a placeholder image for unexpanded content objects. This is used by the HTML editor to represent external content objects that you cannot edit. However, the content object as a whole can be deleted, by removing the placeholder image.

get_dynamic_content()

my $html = $this->get_dynamic_content($module,$args,$option);

Return replacement text for a <!--&Module(...)--> CMS tag

Loads the $module plug-in, and calls its write() method, passing the $args to it. Returns the output of the module. If the module fails to load/compile, a Perl error string will be returned.

$option is the same as is passed to expand(). If the ``expand'' option or ``dummy-module'' methods are selected, a placeholder image will be substutited instead of the module content.

If the ``static-module'' method is selected, each module will be queried to see if its output is static for this page; only if static will the content will be substituted. (This allows for precompiling certain modules' output, eg. menus, while rendering others at page view time.)

get_dynamic_content_indirect()

    my $html = $this->C

This is an AJAX version of get_dynamic_content().

This method returns replacement text for CMS tags of the form:

    <!--&&Module(...)-->    # indirect substitution, direct re-links
    <!--&&&Module(...)-->   # indirect substitution, indirect re-links

This fetches the DCD content using a separate server request, instead of inlining it directly.

The main advantage is that you can publish the main page to a static HTML file, yet keep some page elements dynamic. This is especially useful for index.html pages, which must be static, but may contain dynamic elements (eg. recent news, upcoming events, current specials, etc.). The solution is either continuous republishing of the index page (which may still be a better solution for heavily loaded sites) or using an indirect dynamic content fetch.

The disadvantage is that the full page is slower (although the base page may be much faster), and that JavaScript must be enabled to perform the secondary content fetches.

The '&&' variant does direct re-links, ie. links from the dynamic content point to full URLs that generate a new page. The '&&&' variant does indirect re-links, ie. links back to the same module in the same page only fetch the DCD content and inline it dynamically into the current page without generating a whole new page.


CMS Data Fetching Methods

These are wrappers for the DB data fetching methods. The primary difference from the generic DB data fetching methods is that we use the prefetch caches as much as possible to try and avoid hitting the database for records we have already fetched.

The following methods take the same parameters as the equivalent methods in ExSite::DB.

fetch()

Attempts to locate page, content, and content_data records in the prefetch caches before resorting to a database query.

fetch_child()

Attempts to locate content, and content_data records in the prefetch caches before resorting to a database query.

fetch_match()

At present, this is a simple wrapper to a normal database query. (Ie. the prefetch caches are not used.)


Prefetch Cache

Content prefetching can greatly reduce the number of database queries that get executed in the course of building a page. They do this by prefetching content related to a page or site, and then trying to resolve content requests by using these prefetch caches rather than database queries.

Prefetching must be done by the caller to take full advantage of this. It is done by page.cgi, so normal page views will benefit from this performance enhancement.

The structure of the prefetch cache is as follows:

$share{prefetch}{SECTION}{PAGE}{CONTENT}{REVISION}

``SECTION'' is a section ID. ``PAGE'' is a page ID or filename. ``CONTENT'' is a content ID or a content object name. ``REVISION'' is a content data ID, or a revision name. That means any object is addressible by ID or name, and the following styles of lookup (or a mix of both) work:

    $share{prefetch}{6}{89}{121}{887}
    $share{prefetch}{6}{"index.html"}{"body"}{"active"}

These examples fetch a revision record from the prefetch cache. To fetch a section, page, or content record, use the key ``_'', eg.

    my $pagedata = $share{prefetch}{6}{"index.html"}{_};

is_prefetched()

    $this->is_prefetched($section_id);

Returns TRUE if we have loaded prefetch data for the given section. If no section ID is given, returns TRUE if we have loaded prefetch data for any section.

prefetch()

    $this->prefetch($section,$allrevs);

Preloads all useable content records (pages, content objects, and revisions) for the given section, and stores them in a data structure under $share{prefetch}. $section can be a section ID or a section datahash. If not provided, we use the section from the current context. Note that inactive pages are not fetched.

By default we only store only the newest and active revisions of content data in the prefetch cache. All other revisions are discarded. You can override this by setting $allrevs to a TRUE value.

prefetch_update()

    $this->prefetch_update($type,$data);

Updates the prefetch cache with the datahash $data which represents a $type (which can be one of ``section'', ``page'', ``content'', or ``content_data''. This ensures that page builds or previews following a content update will render using the new content, rather than the information that was in the prefetch cache before the update occurred.


Publishing

safe_path()

    my $safepath = $this->safe_path($unsafepath,$prefix);

Makes a path safe for publishing to. If the path has already been untainted, then it is returned as-is. If not, then it must conform to the following rules:

- no ".." elements allowed - all directories must begin with a word character (\w) - the path must begin with the system HTML area directory (you can override this using $prefix)

If the path does not conform, then nothing is returned. Otherwise, the untainted path is returned.

Filed under: POD