
Using Smarty to do the math in your stylesheet
This recipe shows you how you can use Smarty to compute sizes in your stylesheet. By setting a few key sizes and computing other dimensions from those values, we make it much easier to change the layout of a site while preserving the proportions.
Getting ready
For this recipe, you will need login access to the site's Administration area as a member of the "Admin" group or as a member of a group with a fair number of permissions: "Add Stylesheets", "Add Stylesheet Associations", and "Manage All Content".
How to do it...
- Log in to your CMS Administration area.
- Using the top menu, go to "Layout" and click on "Stylesheets".
- Click on the "Add a Stylesheet" button.
- Enter "width_calculator" into the"Name" field.
- Enter the following stylesheet into the "Content" area:
[[assign var='box_width' value='300']] [[assign var='middle_width' value='10']] [[assign var='left_percent' value='20']] [[math assign='left_size' equation='(box_width - middle_width) * left_percent/100' box_width=$box_width middle_width=$middle_width left_percent=$left_percent]] #wrapper { border: 1px solid black; width: [[$box_width]]px; min-height: 80px; } #middle_portion{ float: left; width: [[$middle_width]]px; min-height:80px; } #left_portion { float: left; width: [[$left_size]]px; background: #ffbfc9; min-height: 80px; } #right_portion { float: right; width: [[$box_width-$left_size-$middle_width]]px; background: #bfc4ff; min-height: 80px; }
- Under "Media Types", click the checkbox for "screen : Intended primarily for color computer screens" and then click"Submit".
- Find "width_calculator" in the stylesheet list, and click its CSS icon.
- In the drop-down menu, select "NCleanBlue" and click on the "Attach to this Template" button.
- Using the top menu, go to "Content" and select "Pages".
- Click on the "Add New Content" button.
- Fill in the name of the new page as "Width Test".
- Enter "Width Test" as the menu text.
- If you are using a WYSIWYG editor for the content area, uncheck the checkbox labeled "Turn WYSIWYG on/off".

- Enter the following HTML code into the content area:
<div id="wrapper"> <div id="left_portion"> This is the left portion. </div> <div id="middle_portion"> .<br/>.<br/>.<br/>. </div> <div id="right_portion"> This is the right portion. </div> </div>
- Click"Submit".
- View your site from the user side. Click on the new "Width Test" page.

- Use Web Developer tools or other browser capabilities to view the generated CSS:
#wrapper { border: 1px solid black; width: 300px; min-height: 80px; } #middle_portion{ float: left; width: 10px; min-height:80px; } #left_portion { float: left; width: 58px; background: #ffbfc9; min-height: 80px; } #right_portion { float: right; width: 232px; background: #bfc4ff; min-height: 80px; }
How it works...
In this recipe, we are exploring the mathematical capabilities of Smarty, and how we can use them in our stylesheets. To demonstrate this, we will create a box that is divided into two portions with a fixed divider between them. The box, the two portions, and the divider are all defined using simple HTML div tags with IDs that we can use to target them with our CSS rules.
We begin by setting a few basic variables using Smarty "assign" statements. First, we set our outer box dimension to the unitless value of 300. We omit the units since we will be performing arithmetic on the value and can assign the units later when we substitute in the variable in the CSS itself. You can see that below in the CSS in the rules for #wrapper
, where there is the width specification:
#wrapper { border: 1px solid black; width: [[$box_width]]px;
Next, we assign the other values that will define our geometry. We set the width of the division, which we call middle_width
, as ten units. Then we set left_percent
, that will determine the percentage of the total box occupied by the left portion, and we use the value 20 (for twenty percent).
Once the basic geometrical description is set, we use Smarty to compute the size of the left portion. In this case, since we will be doing more complex math than addition and subtraction, we use the Smarty "math" command to specify an equation and to assign the computed value to a variable named left_size
. As you can see, the Smarty math command binds the variables in the equation template to Smarty variables. For simplicity, we used our existing variable names in the equation, but we still have to bind the equation template variables to our defined variables, which is why we have the seemingly redundant assignments like box_width=$box_width
.
The output of the Smarty computation, stored in left_size
, is still unitless. When we substitute the value into our CSS, you can see that we put the units of px
(for pixels) after the variable instantiation.
The manual page for Smarty recommends avoiding the use of the math command when performance is an issue, for example, like in a loop. It turns out that for simple arithmetic, it's easy to avoid. As you will see where we compute the width of the right_portion
div, you can put subtraction into an ordinary Smarty variable instantiation. In this case, we subtract our left portion and middle portion from the overall width.
Now that you have the dimensions determined mathematically, you can play around to see how it functions. Try changing the percentage of the div that the left_portion
occupies, or modifying the overall box width. From this, you can see that if your entire layout was done using Smarty math, it takes only a single variable change to alter your design — all without affecting proportions.
There's more...
One thing to look out for when using Smarty logic in your stylesheet is caching. People are occasionally surprised by the results they get when they put Smarty logic in their CSS that is supposed to do something conditionally, based upon the current page name or Smarty variables set in the "Smarty data or logic that is specific to this page" field of the Page admin. The symptom is that the logic doesn't work at all, or alternatively works the first time, but then triggers on pages where it shouldn't. The reason for this is stylesheet caching. For performance reasons, CMS Made Simple generates stylesheets the first time they're needed, and uses caches thereafter. Until you change the stylesheet source, the same CSS will be served up to all pages.
The solution to this problem is to move any page-specific logic to your page template. If you want a different style for H1 tags on your home page, put the class into a conditional rather than putting the condition in the stylesheet:
<h1 class="{if $special_condition==true}special_class{else}normal_class{/if}">
This will work as you expect.
See also
- Using Smarty to create a color set in your stylesheet recipe
- Using Smarty loops to generate similar stylesheet constructs recipe
Renaming the "Extra Page Attributes" in the CMS Admin
When editing a page in the CMS Admin area, there are three fields under the "Options" tab called "Extra Page Attribute 1", "Extra Page Attribute 2", and "Extra Page Attribute 3". You can use these in your page template for a variety of purposes, but those uses may not be obvious to the Administrator. This recipe shows you how you can change the names of these attributes to clarify how they're used.
Getting ready
This recipe requires that you have login access to the site's Administration area as a member of the "Admin" group (or as a member of a group with "Modify Any Page" permission settings). You will also need permissions to create a file on the server, either via FTP or some other means.
How to do it...
- Using your FTP client or a login shell, find your base installation directory.
- Inside the "Admin" directory, create a directory named "custom".
- Inside your new "custom" directory, create a directory named "lang".
- Inside the new "lang" directory, create a new directory called "en_US".
- Using your favorite text editor, create a file containing the following:
<?php $lang['admin']['extra1'] = 'Make'; $lang['admin']['extra2'] = 'Model Year'; $lang['admin']['extra3'] = 'Mileage'; ?>
- Save this file as "admin.inc.php" in your newly created
custom/lang/en_US
directory. - Log in to your CMS Administration area.
- Using the top menu, go to "Content" and click on "Pages."
- Click to edit any page, and then click on the "Options" tab.
- Scroll down and see your new labels:

How it works...
The CMS Made Simple Administration area has built-in localization support. The way localization works is that the text strings that are displayed by the Admin area are abstracted: within the code, a string name is used instead of the string itself, and that name is used in conjunction with files of translated strings. For example, the string that is expressed in English as "Extra Page Attribute 1" is labeled in the code using a tag "extra1".
If you have the Admin set to "US English", the translation subsystem reads in the en_US
version of the admin.inc.php
file where the tag "extra1" is translated to "Extra Page Attribute 1". Similarly, if your Admin is set to German, the translation subsystem reads in the de_DE
version of admin.inc.php
, and the tag "extra1" is translated to "Zusätzliches Seiten-Attribut 1".
Tip
One advantage of this system of localization is that the CMS allows you to override the translation of these tags. In the Admin area, the CMS translation functions look for the special directory "custom/lang/LANG", where LANG is the current Admin user's language code, and if it exists, the translation system will read in the "admin.inc.php" file within.
Since this custom file is read in after the translations, any tag that you override here will then reflect your changes in the Admin area.
As shown in this recipe, you simply put a list of tag definitions into your custom "admin.inc.php" file to change how the tags are displayed.
There's more...
In this recipe, we only overrode the names of the extra page attribute tags in English. If your site has multiple administrators who use different languages, you'll want to override the names of the attributes in all of those languages.
The process for doing this is equally simple. Inside of the "custom/lang" directory, you'll need to create a directory for each language that needs to be overridden. The naming convention for these directories is the two-letter ISO 639-1 language code (in lowercase) followed by an underscore, followed by the two-letter ISO 3166-1 country code. For an easy way to find the list of the languages supported by CMS Made Simple, take a look in the "admin/lang/ext" directory under your base installation.
Once you have created the language directory, create an "admin.inc.php" file within it, and add your translations to that file.
What else can I rename?
You can change the text for any string displayed by the Administration area code. To find the names of the tags to override, look at "admin.inc.php" in the "admin/lang/en_US" directory within your CMS base installation.
While it is true that all of the text displayed by the Administration area may be overridden in a Custom file, this statement could be considered misleading because of the tight integration between the Administration area and modules. Technically, many strings are displayed by modules, not by the Administration area itself. For example, if you look through the "admin.inc.php" file, you won't find any of the strings for MenuManager or other core modules.
It turns out that overriding strings from Modules is done in a different way, which is detailed in a later recipe.
See also
- Chapter 4, Making a module localizable recipe
- Chapter 10, Overriding Module strings recipe
Creating a personnel directory using Menu Manage
This recipe shows the power of the standard Core modules when used in conjunction with a simple naming convention. We create a personnel directory where a top-level directory page links to individual biographical pages for employees. It is designed to minimize the difficulty for the person maintaining the directory: it automates much of the formatting, and avoids any requirement to enter redundant data.
Getting ready
This recipe requires that you have login access to your site's Administration area with permission to "Add Pages", "Add Global Content Blocks", "Manage Menu", and "Modify Any Content". You will also need permissions to create a file on the server.
How to do it...
- Using your FTP client or a login shell, find your base installation directory.
- Inside the "Admin" directory, create a directory named "custom".
- Inside your new "custom" directory, create a directory named "lang".
- Inside the new "lang" directory, create a new directory called "en_US".
- Using your favorite text editor, create a file containing the following:
<?php $lang['admin']['extra1'] = 'Title'; $lang['admin']['extra2'] = 'Phone Number'; $lang['admin']['extra3'] = 'Email Address'; ?>
- Save this file as "admin.inc.php" in your newly created
custom/lang/en_US
directory. - Log in to your CMS Administration area.
- Using the top menu, go to "Content" and click on "Global Content Blocks".
- Click on the "Add Global Content Block" button.
- In the"Name" field, type "personnel".
- If you are using a WYSIWYG editor for the content area, uncheck the checkbox labeled "Turn WYSIWYG on/off".

- Type the following code into the "Content" text area:
<div class="person"> <img src="uploads/images/personnel/{$page_alias}.jpg" alt="{title}" /><br /> <strong>{title}, {$content_obj->GetPropertyValue('extra1')}</strong><br /> Phone: {$content_obj->GetPropertyValue('extra2')}<br /> Email: {$content_obj->GetPropertyValue('extra3')} </div>
- Click on"Submit".
- Using the top menu, go to "Layout" and click on "Menu Manager".
- Click on the "Add Template" button.
- In the "New Template Name" field, type "personnel".
- In the "Template Content" field, type the following code:
{foreach from=$nodelist item=node} <div style="float:left; padding:10px;"> <img src="/uploads/images/personnel/thumb_{$node->alias}.jpg" alt="{$node->menutext}" /> <img src="/uploads/images/personnel/thumb_{$node->alias}.jpg" alt="{$node->menutext}" /> <br /> <a href="{$node->url}">{$node->menutext}</a>, {$node->extra1} </div> {/foreach}
- Click on"Submit".
- Using the top menu, go to "Content" and click on "Image Manager".
- Type "personnel" into the field labeled "Create New Folder" and click on "Create".
- Click on the folder icon labeled "personnel".
- Using the "Upload" field, upload a picture. It should be in JPEG format, and should have dimensions of 150 by 200 pixels. It should be named "sierra.jpg".
- Using the "Upload" field, upload another picture. It should be in JPEG format, and should have dimensions of 150 by 200 pixels. It should be named "javier.jpg".
- Using the top menu, go to "Content" and click on "Pages".
- Click on the "Add New Content" button.
- In the "Title" and "Menu Text" fields, type "Personnel".
- In the "Content" text area, type the following code:
{menu loadprops='1' childrenof=$page_alias template='personnel'}
- Click on the "Options" tab, and type "personnel" in the "Page Alias" field.
- Click"Submit".
- Click on the "Add New Content" button.
- In the "Title" field, type "Sierra S. Maxwell".
- In the "Menu Text" field, type "Sierra".
- With the "Parent" drop-down, select the "Personnel" page you just created.
- In the "Content" area, type in the following:
{global_content name='personnel'} Sierra has led the CMS Module Factory for ten years, following a long career in industrial welding.
- Click on the "Options" tab, and type "sierra" into the "Page Alias" field.
- Scroll down to "Title" and type in "CEO".
- Enter some imaginary values for the "Phone Number" and "Email Address" fields.
- Click on"Submit".
- Click on the "Add New Content" button.
- In the "Title" field, type "Javier Sullivan".
- In the "Menu Text" field, type "Javier".
- With the "Parent" drop-down, select the "Personnel" page.
- In the "Content" area, type in the following:
{global_content name='personnel'} Javier has been our hard-working President since inventing the Miraculous Module Maker.
- Click on the "Options" tab, and type "Javier" into the "Page Alias" field.
- Scroll down to "Title" and type in "President".
- Enter some imaginary values for the "Phone Number" and "Email Address" fields.
- Click on"Submit".
- View your site from the user side. Click on the new "Personnel" page:

- Click on "Sierra" to view the CEO's bio page:

How it works...
This recipe takes advantage of several built-in CMS Made Simple features and several tricks:
- Overriding Extra Page Attribute names
- Accessing page variables in a Global Content Block
- Using the page alias as part of a naming convention
- Creating custom MenuManager templates
- Relying on the ImageManager's thumbnailing feature
The first of these tricks, changing Extra Page Attribute names, is described in detail in another recipe in this chapter, so it won't be described here.
Let's start by looking at the "personnel" Global Content Block (GCB) we defined. It is used to present structured information: in this case, the people in our imaginary company. It guarantees a standardized presentation. The data-entry person who fills in the user pages doesn't have to remember any particular format. To achieve that end, you'll notice that the entire GCB is generic — it makes no reference to any specific person. Each piece of information it presents comes from page variables or is based on the page alias.
First, the "personnel" GCB presents an image of the person. The image name is simply the page alias (followed by the ".jpg" extension). It assumes a subdirectory of the usual image upload directory, which helps in keeping the personnel pictures separate and organized. The "alt" tag for the image uses the standard {title} tag, that is available to all CMS Made Simple pages. When we create pages for people, we will use their names for the page titles, so the image will be their picture and the "alt" tag will contain their name. The "personnel" GCB then includes the three Extra Page Attributes, and labels them according to how we use them.
When the Global Content Block is included in a page, all of the data fields that are referred to generically will be filled in with the appropriate value for the containing page. So when we create Sierra and Javier's biographical pages, we start by including the "personnel" GCB to present their information in a consistent, structured fashion.
At this point, we have consistent biographical pages for our personnel, but if we were to just leave it at that, someone would need to make the top-level directory, that links to the individual bio pages. Instead, we use the Menu Manager to do the work for us.
In our main personnel directory page, we add a tag to call Menu Manager. While Menu Manager is typically used to create menus (as its name suggests), what it does under the hood is generate a data structure of hierarchical page information, which it then formats according to a template. Menus often format the data structure into unordered lists, but we're not limited to that kind of markup. In this case, we're specifying our "personnel" Menu Manager template, that formats the lists into separate divs for each page. We'll describe that in more detail below.
Before we examine our Menu Manager template, we need to look at a few parameters that we use that aren't generally used in site menus. These parameters are quite handy, and once you use them a few times, you'll find yourself discovering other uses. The first of these is "loadprops." By default, Menu Manager loads only a limited set of information about the pages: name, menu text, link, and so on. By specifying "loadprops=1", we're saying that we want Menu Manager to load the extended properties of the page, which include such useful things as the Extra Page Attributes.
The next interesting parameter to Menu Manager is "childrenof." By passing a page alias, this parameter limits the information it returns to the children of the specified page in the content hierarchy. If you want to limit a menu to a site section, this is one way of doing that. In this case, we're being sneaky, and passing "childrenof" a variable: the page alias for the current page. In other words, we're telling Menu Manager to return a hierarchical list of pages that are the children of the page containing this tag. We'll be assigning all our biographical pages this personnel directory page as their parent, so this tag will bring in a list of all of these bio pages.
Now that we know we're processing a list of all the bio pages, complete with Extra Page Attributes, let's look at our Menu Manager template. For each entry in the list, we're given a "node" variable that has the specific page details as attributes. Each directory entry is going to start with a picture. Remember our naming convention where the people's pictures are named after their page alias? We use that same convention here, except we prepend the name with "thumb_" to retrieve the thumbnail image that is automatically generated by the Image Manager when we upload JPEG images. For the "alt" tag in the images, we use the menu text attribute for the bio page; if we wanted to be more formal and use the person's full name, we could use the page title instead. We then specify the person's name again, and wrap it in a link to their page. You'll notice that accessing the Extra Page Attributes in Menu Manager templates is even easier than in a standard page template, since it's a direct attribute of the node ($node->extra1).
There's more...
Much of this recipe is designed to simplify the creation of a personnel section for a site without requiring the person to do the data entry to key in redundant information or worry too much about formatting. This is not done just to make the job of the data entry person easier — long experience has shown that forcing someone to enter the same data in multiple places results in errors, and counting on someone to use consistent formatting on multiple pages rarely works out.
There is one place where we could improve this recipe and minimize the opportunity for error even further: we could obviate the necessity for the data entry person to include the Global Content Block tag for each biography page. The way we do this is to make a copy of our main template, put the GCB tag into this template before the {content} tag, and then save it as the "personnel" template. Then, when the data entry person creates a new biographical page, they simply select the "personnel" template when they are keying information into the "Options" tab.
Using other page attributes
When generating a custom directory or another collection using pages and the Menu Manager, there are a number of page attributes you can use. The entire set is listed in the module help for Menu Manager, but some of the more interesting ones include:
hierarchy:
This is a outline-type representation of the page's position in the site hierarchy, (for example "1.2")haschildren:
— This is true if there are pages below this specific node in the site hierarchy.image:
— If a page has been assigned an image, this will be the image's name (without any path information). This is only available if you tell Menu Manager to load all properties.thumbnail:
If a page has been assigned a thumbnail, this will be the thumbnail image's name (without any path information). This is only available if you tell Menu Manager to load all properties.created:
— the date the page was created.modified:
— the date the page was last modified.
Why use a naming convention for images?
You probably already knew that you could assign an image and thumbnail to a content page. As you can see in the list of attributes above, the names of those files are available to the Menu Manager template. Given this, why would we use a naming convention based on the page alias rather than these attributes?
There are a few tradeoffs for each approach:

In our recipe we're already telling Menu Manager to load extended properties for each page, so there's not much of an advantage to using a naming convention. If, however, we were not using the Extra Page Attributes in our directory, we could improve performance by loading only standard content attributes.
In the end, you should select the approach that you think will be simplest for the person or people who will maintain the site.
See also
- Renaming the "Extra Page Attribute" fields in the CMS Admin recipe
- Creating a basic Google Sitemap with Menu Manager and mod_rewrite recipe
Creating a basic Google Sitemap with Menu Manager and mod_rewrite
This recipe uses the Module Manager to create an XML site map according to Google's sitemap standard. Since all of the major search engines have adopted this format, the sitemap should work well for all indexes.
Getting ready
This recipe requires login access to your site's Administration area as a member of a group with permissions to "Add Pages", "Manage Menu", and "Modify Any Content". For the recipe to work completely, you need to be running Apache web server or a web server that has some mod_rewrite
compatible capability. You will need to be able to update the Apache configuration, either directly or via .htaccess
files. You will also need permissions to create files on the server.
How to do it...
- Log in to your CMS Administration area.
- Using the top menu, go to "Layout" and click on "Menu Manager".
- Click on the "Add Template" button.
- In the "New Template Name" field, type "sitemap".
- In the "Template Content" field, type the following code:
<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> {if $count > 0} {assign var='now' value=$smarty.now|strtotime} {foreach from=$nodelist item=node} <url> <loc>{$node->url|escape:'html'}</loc> <lastmod>{$node->modified|date_format:'%Y-%m-%dT%T-08:00'}</lastmod> <changefreq>{assign var='mod' value=$node->modified|strtotime}{math assign='age' equation="(n-m)/86400" n=$smarty.now m=$mod}{if $age < 2}hourly{elseif $age< 14}daily{elseif $age < 30}weekly{else}monthly{/if}</changefreq> <priority>{math equation='1/depth' depth=$node->depth}</priority> </url> {/foreach} {/if} </urlset>
- Click on"Submit".
- Using the top menu, go to "Content" and click on "Pages".
- Click on the "Add New Content" button.
- In the "Title" and "Menu Text" fields, type "sitemap".
- In the "Content" text area, type the following code:
{menu template='sitemap' show_all='1' collapse='0'}
- Click on the "Options" tab.
- Uncheck the "Show in Menu" checkbox.
- Type "sitemap" in the "Page Alias" field.
- Click"Submit".
- If you are already using the standard
.htaccess
file to domod_rewrite
pretty URLs, add the following after the "RewriteBase" line:RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^sitemap.xml$ index.php?page=sitemap&showtemplate=false [L]
- If you are not using
mod_rewrite
pretty URLs, use your favorite text editor to create a file containing the following:<IfModule mod_rewrite.c> RewriteEngine on # #Sub-dir e.g: /cmsms RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^sitemap.xml$ index.php?page=sitemap&showtemplate=false [L] </IfModule>
- Save this file as ".htaccess" in the base CMS Made Simple root directory.
- You can test your site map with any web browser: go to http://www.yoursite.com/sitemap.xml
How it works...
This recipe uses Menu Manager to output a list of all the pages on the site, and a custom template to format that list as XML according to the Google sitemap standard. The structure of the XML file is elementary: an XML declaration and a "urlset" tag that contains a collection of site-specific URLs, each with a few descriptive attributes.
Let's examine the Menu Manager template. It starts by declaring that the file is an XML file and the characters are encoded in UTF-8. Next, it outputs the main container, which is the urlset
tag. This urlset
attribute specifies an XML name space (xmlns) from the sitemaps.org website; in this case, we're specifying that we'll be using the namespace for the sitemap 0.9 schema.
Next, the template sets a variable containing the current time. This will be used when describing the currency of each URL.
The next part is the main substance of the template: we loop through the site pages (in the nodelist variable), and for each page, we output an URL tag. Each URL tag contains several attributes:
- a "loc" attribute that contains the actual URL. We use Smarty's HTML escaping modifier in case the URL has characters like ampersands in it. This prevents the creation of invalid XML.
- a "lastmod" attribute, that is the "modified" date of the page reformatted into a date string according to the W3C date standard.
- a "changefreq" attribute, that designates how frequently the content is expected to change. The allowable values are strings: always, hourly, daily, weekly, monthly, yearly, or never. We use the amount of time since the last modification, and output the appropriate string.
- a "priority" attribute, which is a value between 0 and 1. We compute a value based on the depth in the site hierarchy, on the assumption that the most important pages are the top-level pages.
Once we have the Menu Manager template, we place the menu tag into a page. The page is set to be active, but not to show up in the site navigation.
The last step is to link this page to a standardized URL — according to the sitemap standard, search engines look for a filename "sitemap.xml" in the site root (although many search engines allow you to register a different location, if you choose to).
We use the magic of Apache's mod_rewrite
module to map the filename "sitemap.xml" to our page containing the sitemap tag. Specifically, the mod_rewrite
rules check to see if the incoming request is not for a directory or an existing filesystem file. If those tests pass, it checks to see that "sitemap.xml" was requested. If so, it rewrites the URL and drops out of further rewrite tests.
The rewritten URL has one special feature — it calls the page containing the sitemap, but also passes the parameter "showtemplate=false". This noteworthy parameter tells CMS Made Simple to output the page but suppress the page template that surrounds the page's {content} tag output. This allows us to output the unadorned XML of the sitemap.
There's more...
The sitemap generated by this recipe includes all active pages in your site, whether or not they show up in the menu. In some cases, you may want to use the menu visibility to determine what is included in the site map. It is easy to change this recipe to include only pages that are in the menu. In your "sitemap" page, change the "show_all='1'" to "show_all='0'".
The changefreq and priority attributes
There are two sitemap attributes that we're computing, and which might bear some further examination.
If you look at how we generate the "changefreq" attribute, we're looking at how long it's been since the page was last updated, and glibly asserting that that's the average time between updates. This, of course, is not necessarily accurate. CMS Made Simple doesn't track the number of updates to any one page, so it seemed like a reasonable compromise number, but you may wish to be more accurate for Search Engine Optimization (SEO) purposes. One way of doing that would be to use an Extra Page Attribute to store update frequency. If you do this, be sure to add "loadprops='1'" to your menu tag, so that Menu Manager knows to load the extra page attributes.
Similarly, we're computing page priority based upon the page's depth in the site hierarchy. We're saying page priority is inversely proportional to the page depth: top level pages get a priority 1, next level get a priority of 0.5, third level get 0.33, and so on. You may have a different formula for rating page priority, or you may wish to specify on a page-by-page basis using an Extra Page Attribute.
One thing to keep in mind about priority — this number represents relative importance on your site. You'll encounter overly enthusiastic people who think that rating all of their pages with a priority of 1 will improve their site's overall search engine performance. Unfortunately, this is not true; the number is relative to your own pages, not anybody else's.
For more information on the sitemap standard, visit the site Google set up to document it at https://www.sitemaps.org.
What if my site is not UTF-8?
Character encoding is one of the more confusing and troublesome problems of programming for the internet.
The sitemap standard requires the XML file to be encoded as UTF-8. In most cases, you'll want your CMS Made Simple site to create UTF-8 encoded output anyway.
However, in the event that your site is not using UTF-8 encoding, you may still be able to use this recipe. In fact, as long as your site don't use characters outside of the old ASCII alpha-numeric range in any URLs, the encoding won't matter. If, however, your URLs use extended characters and you are using a non-UTF-8 encoding, your sitemaps won't be handled properly by search engine indexers.
See also
- Creating a personnel directory using Menu Manager recipe
- Renaming the "Extra Page Attribute" fields in the CMS Admin recipe
Embedding JavaScript in your template without causing Smarty to throw a fit
Embedding JavaScript directly in a page is usually discouraged, but is also sometimes unavoidable. In a CMS Made Simple site, you may see errors when adding JavaScript to a page because Smarty confuses the JavaScript curly braces with its own delimiters. Embedding JavaScript is not impossible, though!
This recipe demonstrates adding JavaScript to a template that is parsed by Smarty. It also discusses incorporating Smarty variables within JavaScript.
Getting ready
For this recipe, you will need to access the site's Administration area with "Modify Templates" permissions. This recipe assumes you are using NCleanBlue as your default site template.
How to do it...
- Log in to your CMS Administration area.
- Using the top menu, go to "Layout" and click on "Templates".
- Click on "NCleanBlue" in the template list to edit the page.
- Find the line that reads "{* This is how all the stylesheets attached to this template are linked to *}", and paste the following code after it:
{literal} <script type="text/javascript"> function hi() { alert('hi'); } hi(); </script> {/literal}
- Load your site's home page in your browser. You will see a JavaScript alert pop up saying "hi".
How it works...
This recipe is a contrived example to illustrate the use of Smarty's {literal} tag. In the version of Smarty used in CMS Made Simple, curly braces are reserved for template directives. The fact that they are syntactically important in JavaScript becomes a problem: Smarty thinks that your curly braces are meant for it, so it throws template errors when it attempts to render the page containing JavaScript.
The {literal} and {/literal} tags tell Smarty to ignore curly braces within an area. This allows you to include your JavaScript without confusing Smarty.
An alternative approach
Smarty has an alternative to {literal}{/literal} tags. Instead of marking off the boundaries of your JavaScript, you replace all of your curly braces in your JavaScript with one of two Smarty-friendly tags: either {ldelim} or {rdelim}. These stand for "left delimiter" and "right delimiter," respectively, and will render into your template as a left curly brace and a right curly brace.
Since this approach is verbose, it is not frequently used.
Using Smarty Variables in your JavaScript
As we've seen, setting off JavaScript with {literal}{/literal} tags prevents Smarty from generating parse errors. But what about the cases when you want to pass a variable to your JavaScript from Smarty? This turns out to just be a matter of ending one literal block and starting another.
Here's an example of a script that will pop up an annoying confirmation box any time someone wants to leave a page. It uses the "Title" Smarty tag to customize the message for the current page, even if the script lives in the page template.
{literal} <script type="text/javascript"> function confirm_exit(e) { if(!e) e = window.event; e.cancelBubble = true; e.returnValue = 'You will be leaving the page "{/literal}{title}{literal}"?'; if (e.stopPropagation) { e.stopPropagation(); e.preventDefault(); } } window.onbeforeunload=confirm_exit; </script> {/literal}
Using Smarty loops to generate similar stylesheet constructs
You can use Smarty in your stylesheets for more than just setting up named variables, or computing sizes. With creative use, you can make Smarty do drudge-work for you!
This recipe shows you how you can use Smarty's built-in looping capabilities to create groups of similar CSS rules.
Getting ready
This recipe requires that you have access to the site's Administration area with permission to "Add Stylesheets", "Add Stylesheet Associations", and "Manage All Content".
How to do it...
- Log in to your CMS Administration area.
- Using the top menu, go to "Layout" and click on "Stylesheets".
- Click on the "Add a Stylesheet" button.
- Enter "color_boxes" into the"Name" field.
- Enter the following stylesheet into the "Content" area:
[[setlist var=boxes value='"red":"#df0000","green":"#00df00","blue":"#0000df"']] [[foreach from=$boxes key=current_box item=box_color]] #[[$current_box]] { background: #fff; color: [[$box_color]]; border: 1px solid [[$box_color]]; width: 200px; } #[[$current_box]] h1 { background: [[$box_color]]; color: #fff; font-weight: bold; text-align: center; } [[/foreach]]
- Under "Media Types", click the checkbox for "all: Suitable for all devices" and then click"Submit".
- Find "color_boxes" in the stylesheet list, and click on the CSS icon.
- In the drop-down, select "NCleanBlue" and click on the "Attach to this Template" button.
- Using the top menu, go to "Content" and select "Pages".
- Click on the "Add New Content" button.
- Fill in the name of the new page as "Boxes".
- Enter "Boxes" as the menu text.
- If you are using a WYSIWYG editor for the content area, uncheck the checkbox labeled "Turn WYSIWYG on/off".

- Enter the following HTML code into the content area:
<div id="red"> <h1>Red Box</h1> <p>This is a red box</p> </div> <div id="green"> <h1>Green Box</h1> <p>This is a green box</p> </div> <div id="blue"> <h1>Blue Box</h1> <p>This is a blue box</p> </div>
- Click"Submit".
- View your site from the user side. Click on the new "Boxes" page.

How it works...
This recipe uses built-in Smarty looping to generate separate, but related, CSS rule sets.
It starts with a CMS Made Simple-specific Smarty tag called "setlist", which allows you to create a Smarty variable that is an associative array. The tag takes two parameters: "var", that specifies the name of variable to use, and "value", that specifies the array to be stored. The syntax for the "value" parameter is similar to a JSON format, with colon-delimited name-value pairs in a comma-separated list.
So the first line creates an associative array named "boxes" with the color names as the keys and the color hex triplet as the value.
The next line is where we tell Smarty that we want it to loop through the array we just created. The foreach
command takes a number of parameters:
Our command says to loop through the array stored in the "boxes" variable. That means that for each element in the array, we'll be interpreting all of the code between the [[foreach]] and [[/foreach]], and within that scope, we will have a variable called "current_box", that will be the current array element's key, and a variable called "box_color", that will be the current array element's value.
From there, we have a set of CSS rules, where we substitute in our values. In this case, we're using our "current_box" for a CSS ID, and we're using the "box_color" to set the color attributes. In our template, we only need to write out the rules once, and the loop will generate one set of rules for each box in our array.
In our HTML, each of the divs we create is given an ID which will match the IDs generated in the CSS.
There's more...
You might worry that the generation of stylesheets with looping is computationally expensive. Never fear! The CMS Made Simple architecture caches stylesheets, so the computation will only happen once after each source change.
Also, keep in mind that the power of looping is not restricted to looping through sets of colors. You could use a loop along with some Smarty arithmetic to generate a set of header tags (H1 H10, say) and set the sizes programmatically. The kinds of things you can do with Smarty logic in your stylesheets is only limited by your imagination.
See also
- Using Smarty to create color sets in your stylesheet recipe
- Using Smarty to do the math in your stylesheet recipe
Displaying a block only for the Home page
Site designs often use identical or near-identical designs for all pages, with one or more extra elements on the Home page. It would be ideal if we could avoid duplicating templates to achieve this, as each duplicate increases the difficulty of maintenance and making changes. In this case, it would be best if we could use a single template for all pages and, fortunately, we can! This recipe shows you how to place conditional logic in your page template that will show some content only on certain pages.
Getting ready
This recipe requires access to the Admin area with "Add Global Content Blocks", "Modify Templates", and "Manage All Content" permissions. We also assume you are using the default NCleanBlue template.
How to do it...
- Log in to your CMS Administration area.
- Using the top menu, go to "Content" and click on "Global Content Blocks".
- Click on the "Add Global Content Block" button.
- Enter "welcome" into the"Name" field.
Enter the welcome message into the "Content" text area:<div class="sbar-top"> <h2 class="sbar-title">Welcome!</h2> </div> <div class="sbar-main">Welcome to {sitename}!</div>
- Click the"Submit" button.
- Using the top menu, go to "Layout" and click on "Templates".
- Click on "NCleanBlue" to edit the template.
- Find the end of the left column definition in the template, which will look like:
{* Start left side *} <div id="left" class="core-float-left">
- After the start of the div, insert the conditional code so it looks like:
{* Start left side *} <div id="left" class="core-float-left"> {if $page_alias eq 'home'} {global_content name='welcome'} {/if}
- Then click on the"Submit" button.
- View your site from the user side. Click on the "Home" page, and you will see your "Welcome" block:

- View any other page that uses the same template, and the "Welcome" block will not appear.
How it works...
This recipe uses Smarty logic in the template to display a block of content only on the Home page of the site. The comparison takes place in the template, with the condition comparing the variable $page_alias
to the string'home'
. If there is a match, everything within the conditional clause is rendered into the template.
In this example, we place our "welcome" content into a Global Content Block. You would typically do it this way so that an admin user would not need to edit the template to update the welcome block. This works out well, since users are usually more comfortable editing a Global Content Block than a template, and you as the site developer don't have to worry about someone accidentally messing up the entire site template by removing the {/if} tag or something similar.
You'll note that this example assumes that the Home page of the site has the alias "home." In the default content distributed with CMS Made Simple, this will be true. If your site is set up differently, you will need to compare the $page_alias
to whatever is appropriate for your site.
There's more...
There are several other Smarty variables that are set for each CMS Made Simple page. You could choose to compare with any of those variables when creating a conditional, including:
$page_id
— the internal ID of the current page$position
or$friendly_position
— the numerical representations of where the page is in the site menu hierarchy.
This is also a good place to use the Extra Page Attributes, although they're a little harder to get to. Extra Page Attributes can be found on the "Options" tab when editing content. They are stored in a Smarty variable called $content_obj
and you refer to them using a function called GetPropertyValue
. So, for example, if you wanted to use the Extra Page Attribute 2 to flag whether or not to show your "welcome" content, you could use:
{if {if $content_obj->GetPropertyValue('extra2') eq 'true'} {global_content name='welcome'} {/if}
Then, for any page where you typed the word "true" into Extra Page Attribute 2, your "welcome" content would be displayed.
More complex comparisons
Smarty supports not only simple "if" conditions, but also "if-else" conditions. You can use this to show one specific piece of content on the Home page and a different piece of content on all other pages:
{if $page_alias eq 'home'} {global_content name='welcome'} {else} {global_content name='something_else'} {/if}
It's a short step from doing page comparisons to doing comparisons based on other Smarty variables. For example, perhaps you would want your logo to have special decorations on special dates. With Smarty comparisons, this is easy:
{if $smarty.now|date_format:"%m%d" == '0101'} <img src="logo_newyears.jpg" /> {elseif $smarty.now|date_format:"%m%d" == '0314'} <img src="logo_einstein_birthday.jpg" /> {else} <img src="logo.jpg" /> {/if}
This example uses the built-in Smarty date variable ($smarty.now) and the built-in Smarty date format modifier to create a four-digit string representing the month and day of the month.
It compares the month to January ("01") and the day to the 1st ("01"), and if there is a match, outputs the New Year's version of the logo. If there isn't a match, it compares the date to March 14th, and, if it matches, it outputs the version of the logo for Einstein's Birthday. If neither condition matches, the everyday version of the logo is output.
See also
- Using Smarty "Capture" and conditionals to hide empty content blocks recipe
- Seeing what Smarty variables are available to your template recipe
- Renaming the "Extra" Page Attributes in the CMS Admin recipe
Using Smarty "Capture" and conditionals to hide empty content blocks
This recipe shows you how you can create multiple content blocks on a page, and have the site design automatically adapt when a content block is empty.
Getting ready
You will need to access your site Administration area with "Modify Templates" and "Manage All Content" permissions. This recipe assumes you are using the default NCleanBlue template, but will work with any template.
How to do it...
- Log in to your CMS Administration area.
- Using the top menu, go to "Layout" and click on "Templates".
- Click on "NCleanBlue" to edit the template.
- Find the end of the left column definition in the template, which will look like:
{* Start left side *} <div id="left" class="core-float-left">
- After the start of the div, insert the capture and conditional code so it looks like:
{* Start left side *} <div id="left" class="core-float-left"> {capture assign="special"}{content block="special"}{/capture} {if $special != ''} <div class="sbar-top"> <h2 class="sbar-title">Special!</h2> </div> <div class="sbar-main">{$special}</div> {/if}
- Then click on the"Submit" button.
- Using the top menu, go to "Content" and select "Pages".
- Click on the "Add New Content" button.
- Enter "Offer" into both the "Title" and "Menu Text" fields.
- Enter a short line of text into the "Special" text area, for example, "Tour the Pleiades in your Own Spaceship!" and add formatting if you'd like.
- Enter more content into the main "Content" text area.
- Click on the "Options" tab and make sure that the selected template is "NCleanBlue".
- Hit the"Submit" button.
- View your site from the user side. Click on the "Offer" page, and you will see your "Special" block displayed:


How it works...
This recipe focuses on two Smarty features, namely, capture and conditionals. Capture is the ability to trap the output of a tag or other content and store it into a Smarty variable. Conditionals, as shown in other recipes, give us the ability to output different content based upon variables.
We start by editing the NCleanBlue template, and we add a new content block. This is a powerful feature of CMS Made Simple, where it is possible to break out separate content areas on a page, give each area a name, and have them all available for editing together in the Page admin. We wrap the new content block in a Smarty Capture, where we assign it to the variable "special." Captured content suppresses display of anything between the opening capture tag and the closing capture tag. Instead, any output is stored in the variable.
Effectively, if a site editor has put anything into the "special" text area when editing a page, our logic places that content into a variable.
In our next line, we test the "special" variable to see if it does, in fact, contain anything. If it does, we wrap it in some formatting, and display it. If it is empty, however, we skip that.
There's more...
Once you have captured site content into a variable, you can do all kinds of creative things. You can run Smarty modifiers against it for all kinds of effects like capitalizing the first letter of each word, converting to upper or lowercase, running replacements with regular expressions, and so on.
There are other Smarty features that could be used for altering the layout of the page, such as the count_words
modifier. For example, if you want to alter the layout when there is a lot of text, you could do something like the following:
{capture assign="my_content"}{content}{/capture} <div class="{if $my_content|word_count < 10}narrow{else}wide{/if}">{$my_content}</div>
This little snippet will display the content block in a div with a class of "narrow", if there are fewer than ten words of content, or in a div with a class of "wide" if there are ten or more words.
See also
- Displaying a block only for the Home page recipe
Seeing what Smarty variables are available to your template
During development, it's sometimes useful to find out which Smarty variables are in scope and available for your use in a template. This recipe shows you how to do that.
Getting ready
This recipe requires you have Administration area access with "Modify Templates", "Modify News", and "Manage All Content" permissions. This recipe assumes you are using all of the default content and templates from a basic installation, but the principles will work regardless.
How to do it...
- Log in to your CMS Administration area.
- Using the top menu, go to "Content" and click on "Pages".
- Click on the edit icon for your home page.
- In the "Content" text area, place the following before any content:
{get_template_vars}
- Click on"Submit".
- View your site from the user side. Click to your home page, and observe the output in your main content area:

- Return to the Admin area of your site. Using the top menu, go to "Content" and click on "Pages".
- Click on the edit icon for your home page.
- In the "Content" text area, remove the {get_template_vars} tag.
- Click on the"Submit" button.
- Using the top menu, go to "Content" and click on "News".
- Click on the "Details Template" tab.
- Click on the edit icon for the "Sample" template.
- In the "Template Source" text area, place the following before any other template text:
{get_template_vars}
- Click on the"Submit" button.
- View your site from the user side. Go to your home page.
- In the "News" column on the left, click on the link to read more of the article "News Module Installed".
- Examine the output:

How it works...
The {get_template_vars} tag is a CMS Made Simple-specific wrapper to an underlying Smarty function that, as its name suggests, returns the entire set of template variables assigned to Smarty. The tag does some minimal formatting to give some idea if a variable contains an array (and if so, how many elements it comprises) or whether a variable contains an object.
The information presented may appear somewhat trivial, but it can save an enormous amount of time when working with a module that doesn't provide strong documentation on the variables it makes available to various templates.
There's more...
As you can see in the sample output, the {get_template_vars} tag provides only superficial insight into more complex variables. Sometimes, it's enough to know that something is an object or array, but sometimes you want to know the specifics.
One way of displaying this kind of detail is using the "print_r" Smarty modifier. For example, you may see a variable "$items" exists in your page, and contains an object. From experience, you may have an idea that it's defined by the News module, but you may be uncertain or you may want to know exactly which attributes it possesses. To get this information, you could put the following tag into a page or template:
{$items|print_r}
Then when you view the page, you will see output like the following:

As this demonstrates, the print_r
modifier is the same as the PHP function of the same name.
Risky recursion
The {get_template_vars} tag avoids running into termination problems by providing a shallow view of the variables that are available to the template. The print_r
modifier, on the other hand, delves much deeper into the contents of variables, and follows object pointers to try to provide a full description of the specified variable.
Like the underlying PHP function, print_r
has some protection against objects or variables that have pointers to themselves. Depending upon the data structures in question, however, an inopportune print_r
can create enormous amounts of output. Using, say, {$gCms|print_r} may well exhaust the memory PHP has allocated, and will not give you the information you desire.
Often, you will run into this problem via unexpected references. You may think that {$gCms->modules|print_r} would output less content than {$gCms|print_r}, but each module has a pointer to $gCms
, so the command gamely tries to show the entire data structure.
In a case where you are unable to display data structures using the print_r
modifier, adding specificity to your output statement can help. Instead of dumping out all of $gCms
, dump out the exact portion that interests you, for example, {$gCms->db|print_r}.
See also
- Chapter 1, How to use CMS in Debug Mode recipe