Set up the content structure

The first step is to create a new psite object. You can do this anywhere in Ariadne, but it is always a good idea to create a 'sites' directory first. If you create a site directly in the Ariadne root, it will be hard to later group some sites together. If all your sites are directly in the Ariadne root, it can quickly become crowded.

For the purpose of this tutorial we will create a new website for a fictitious company called 'Chocoluck', which sells chocolate candy.

In your new psite object you should leave the URL field empty untill the configuration of the webserver and nameserver is complete, otherwise you won't be able to preview the site. Configuration of the webserver to handle multiple websites in Ariadne is discussed in the 'configuring sites' tutorial.

One thing common to most sites is the use of graphical elements to liven up the design. These graphics are used throughout the site and are usually only touched by the designer of the site, not any editor. For this purpose create a new pphotobook object, with the filename 'graphics' for example, in the 'chocoluck' site.

The next step is to create the main sections of the website. If the website is expected to grow pretty large, say more than about 30 pages, it is a good idea to use the psection class for these objects. Another good reason to use psection objects is if the site has a slightly different look for each section. In this case the psection objects make it easier to determine in which section you are and change the layout accordingly.

The main sections of the Chocoluck website are 'assortment', 'about chocoluck', 'about chocolate', 'shop' and 'contact'. The site isn't going to be very large, so we will create these sections simply as directory objects. Keep the filenames simple, lowercase, without spaces or special characters. This makes it easier for people to remember URL's.

The structure we built for Chocoluck looks like this:

chocoluck.gif
Figure 1: Chocoluck content structure

Create the page layout

Your site needs a unique design, you don't want it to look like thousands of other chocolate sites. Fortunately Ariadne really doesn't mind how you want your site to look or even function. You can build it any way you like, even if you want it entirely in flash, its your choice. Ariadne doesn't output anything you don't tell it to. So go ahead, create a website design in your tool of choice. One thing though, since you are creating a site in a content management system, which makes it very easy to add and edit pages, make sure you make a consistent page design which can be reused in most, if not all pages. If every page in your site has its own unique design, perhaps you don't really want a CMS. You can have as many different page designs as you want, but just make sure there is a logic to your madness.

Define a template

Now its time to implement the design in Ariadne. Time to start with the templates. As you should already know, the default template called whenever you go to a site is the view.html template. This template defines how the site looks. The problem is that there are a lot of different objects in a site, and not every object has the same look. You can create a view.html template for each different class, but that means copying a lot of the same html layout for each template. Not only is this unnecessary work, it also means that later when you want to change the design, you'll have to change it in each and every view.html template.

Fortunately there is an alternative, you can 'split' the design in a few different pieces, and create a template for each piece. There are many possible ways to do this, but the following is the one we mostly use:

chocoluck_2.gif
Figure 2: A default template structure

This is where we get technical, so suit up. We'll start with the main template, view.html.

settings.pngYou can create a new template using the Ariadne backend. Each object in Ariadne allows you to create a new template on it, through the 'Set templates' option in the 'settings' block, shown here on the left. You'll want to create your templates on the site root object, called 'Chocoluck'. So go there first.

If you click on the 'Templates' option the template window opens, showing a list of all templates defined on this object. Defined here, mind you, not 'active' or 'known' here, as templates defined higher in the content tree will work on objects below it. But we'll get to that a little later. For now you'll just have to press the 'new' button in the menu in the top left. You'll see a the template editor, like this:

template.new.png

The Type is by default set to the type of the object you start with, in this case a Site. But you will want to define a template which works on all objects (pages) of the website, not just objects of type 'Site'. So instead, select 'Page' here. If you remember the type inheritance tree, you'll note that most object types inherit from this type. If you add a view.html template for the root class 'pobject' this will override the default views of all classes, including images and files. 

Any object that inherits from the Page class has a 'page' variable that may contain content for a webpage. Any object that doesn't inherit from the Page class is usually not meant to represent a webpage. The File and Photo classes for example represent files and images. The User class represents an account, etc. 

The next thing is the name of the template, in this case 'view.html'. You enter this in the text input box under 'Template'. The 'Language', 'Default' and 'Private' options you can skip for now. From now on, we'll simply state the classname and template name of templates like this:

ppage::view.html

<pinp>
ar::call('view.header.html');
ar::call('view.body.html');
ar::call('view.footer.html');
</pinp>

The code above doesn't look much like html, because it isn't. Its PHP, or actually almost PHP. Ariadne uses a subset of PHP called PINP (Pinp Is Not PHP) for templates. You can do most of the stuff PHP allows you, accept little things like accessing the filesystem to grab the password file or access the database directly to bypass Ariadne's security system.

The ar::call method, used three times here, instructs Ariadne to read and execute a different template. So in this case all that the view.html template does, is call three other templates in succession. These templates are executed on the same object that the view.html template is run on. In addition Ariadne automatically runs a special template called 'config.ini' on all objects once. This template can be used to setup some variables for later use, like this:

psite::config.ini (local)

<pinp>
$arConfig = ar::getvar('arConfig');
$arConfig['settings'] = array( 'site' => $this->path,
'siteURL' => ar('loader')->makeURL(),
'graphicsURL' => ar('loader')->makeURL('graphics/', false, false) );
return $arConfig;
</pinp>

This template must be defined with the 'default' checkbox unchecked. This means that this template is only available for the site object on which it is defined. Child objects won't have access to this template.

Since it will only be run on the site object, it can safely make the predefined variable $this->path available as the 'site' variable and generate a base URL for the site with the
ar('loader')->makeURL() method. Templates called later can now use ar::acquire('settings.site') to know what the site root path is.

ar('loader')
->makeURL
Every object has a path and a Site object may have a URL or at least domain. This method will make a clean URL out of the sites URL appended with the remainder of the path. So /sites/chocoluck/about/ will result in something like http://www.chocoluck.com/about/
ar::getvar This method retrieves a named argument for this template passed using ar::call() or from the arguments passed to the webpage through POST or GET or finally from arguments set globally with ar::putvar()

The next three templates to be written are:

pobject::view.header.html

<pinp>
$graphicsURL = ar::acquire('settings.graphicsURL');
</pinp>
<html>
<head>
<title><pinp> echo $nlsdata->name; </pinp></title>
</head>
<body>
<div id="header">
<img src="<pinp> echo $graphicsURL; </pinp>logo.gif"
alt="Chocoluck">
</div>

Finally some HTML, but just as with PHP you can include PINP code anywhere. This template shows the first part of the HTML page, including the Chocoluck logo and the title of the page. It uses the variable 'graphicsURL', which was defined in the config.ini template, to refer to the correct location for the Chocoluck logo.

It also uses the special variable $nlsdata->name. This is a built in variable, which is available on literally every object. $nlsdata is a reference to all language specific data which is stored for the current object. $nlsdata->name is required for all Ariadne objects, you cannot save an object without it. It is the name which you see displayed in the Ariadne tree and file overviews, and in this case also as the page title.

pobject::view.body.html

<pinp>
  $site = ar::acquire('settings.site');
  ar::get($site)->call('view.menu.html');
ar::call('view.content.html');
</pinp>

The body template starts with the main menu. Since the main menu is built up of objects directly under the site root, it must be called on the site object. Luckily we've save the location of the site root earlier, and using the built-in Ariadne method 'ar::get', we can instruct Ariadne to run the 'view.menu.html' template on it. Then it calls the main content template.

pobject::view.footer.html

    <div id="footer">
Site design by ACME Design Inc. - &copy; 2004 Chocoluck.
</div>
</body>
</html>

The footer template is very simple, it doesn't have any PINP code at all, just HTML. Ariadne doesn't mind.

Now we get to the menu and content templates. We'll start with the content, since its easier:

ppage::view.content.html

    <div id="content">
<h1><pinp> echo $nlsdata->name; </pinp></h1>
<p class="summary"><pinp> echo $nlsdata->summary; </pinp></p>
<pinp>
echo ar('content/html')->parse( $nlsdata->page );
</pinp>
</div>

There is one new method here: ar('content/html')->parse(). This method parses a piece of html as edited through the WYSIWYG editor and makes sure all the internal URL's are correct. Since the $nlsdata->page variable is only available in objects of class 'ppage', the template is defined for that class and not the more generic 'pobject'.

pobject::view.menu.html

    <ul id="menu">
<pinp>
ar::ls()->call('show.menu.html');
</pinp>
</ul>

The menu template could be defined just for psite (Site), since it is only used to display the main menu and is always run only on the site object itself. But we're going to re-use it later, so it's defined on the root object class pobject (Object) here. There is a new Ariadne method in here, called 'ar::ls', which stands for 'list'. This method will execute the given template on all immediate children of the current object, in this case the site.  

pobject::show.menu.html

       <li>
<a href="<pinp> echo ar('loader')->makeURL(); </pinp>"><pinp>
echo $nlsdata->name;
</pinp></a>
</li>

This template is called on any object immediately below the site root, so by defining it on pobject it will be available on any type of object. 

The first version of the site is now ready. Open the site, using the 'View webpage' button and you should see a fairly sparse webpage, complete with a functioning menu.

Ariadne also provides a menu library, which makes it much easier to generate different kinds of menu's. But as you see above, you can always write your own menu templates. The same code above using the menu library would be:

pobject::view.menu.html

<pinp>
echo ar('html')->menu( 'ul', array( 'id' => 'menu' ) )->bar();
</pinp>

Using the template above, you don't need to specify a show.menu.html template anymore. The menu() method returns an HTML node object, containing an accessible menu. The root node is an unordered list (UL). You can manipulate the result using DOM methods like getElementById, appendChild, setAttribute, etc. Or access DOM properties like firstChild, nextSibling, etc. But you can also just echo it, like above. Any key - value pairs passed in the options array are automatically turned into attributes, just like the id attribute above.

Experienced PHP programmers have probably noticed a distinct lack of loops. There is no for, foreach or while anywhere. This is a deliberate design choice of Ariadne.

A normal PHP program will first fetch data, usually through a SQL query to a database, and loop through the results printing the correct HTML.

Ariadne uses an approach almost the reverse. Using methods like ar::ls() and ar::find(), it finds the correct objects, instantiates them and 'asks' each object to run the given template. One advantage of this approach is that the templates are usually short, simple and to the point.

If you don't want this, but just want a list of objects and loop through them, you can still do this by either calling the predefined system template 'system.get.phtml'.

This will return the entire object. Passing this template to ar::ls() and ar::find() will result in an array of objects. You can also create your own template which returns just a specific value. The result will be an array of those values. e.g.

 

pobject::get.value

<pinp>
  return $data->value;
</pinp>

Add some styling

So the site has a menu, you can add content through the WYSIWYG editor, but there's no reasonable layout yet. Lets fix that: create a new template called 'style.css', like this:

pobject::style.css

<pinp>
ar('loader')->content('text/css');
</pinp>
body {
background-color: #6D5434;
font: normal .72em/1.5em 'Trebuchet MS', sans-serif;
text-align: center;
}

#header, #content, #footer {
background-color: white;
color: #444;
width: 800px;
margin: 10px auto;
text-align: left;
}

<pinp>
echo ar('html')->menu( 'ul', array( 'id' => 'crumbs') )
->style('crumbs')->rules;
echo ar('html')->menu( 'ul', array( 'id' => 'menu') )
->style('tree')->rules;
</pinp>

The first line makes sure the correct HTTP Content-type header is sent. If you skip this, Firefox will not like the CSS.

The menu library has a few default styles which you can easily include like above. You can always override the generated CSS after it or skip it it and write your own.

Finally add a link to it in the view.header.html template like this:

pobject::view.header.html

<pinp>
$siteURL = ar::acquire('settings.siteURL');
$graphicsURL = ar::acquire('settings.graphicsURL');
</pinp>
<html>
<head>
<title><pinp> echo $nlsdata->name; </pinp></title>
<link rel='stylesheet' type='text/css' href='<pinp>
echo $siteURL; </pinp>style.css'>
</head>
<body>
<div id="header">
<img src="<pinp> echo $graphicsURL; </pinp>logo.gif"
alt="Chocoluck">
</div>

Now the site starts to look more like a website.

Template inheritance

The templates above are defined on pobject, ppage and psite respectively.

Why bother with these classes at all? Well, there are many different objects in any given site in Ariadne. These objects can be of a number of types or classes. An average site has one psite object, a few pdirectory objects and a number of ppage objects, as well as a pphotobook and a few pphoto's.

By defining templates for a specific class, like ppage, all objects of that class (and subclasses) will use that template, but others will not. I've created a view.html template for ppage, but not for pobject. This means that any ppage, but also a pdir and a psite, will use our new template as the default view template, but not pphoto.

This is a good thing, because the default view template for a pphoto will display the image, although it is internally called view.html it may actually output a gif or jpeg image, including correct headers. Your browser won't mind, although it can be a bit confusing.

Another thing you can easily do is create a slightly different layout for your homepage. All you need to do is to create a new view.content.html template for psite. e.g.:

psite::view.content.html

<div id="content">
<pinp>
echo ar('content/html')->parse( $nlsdata->page );
</pinp>
</div>

This template skips the H1 title and summary, so you can use the page content to fill the entire front page (except for the menu and header and footer ofcourse). You can do a lot more ofcourse, like include a news section on the main page, or latest changes, etc. But we'll come to that later.

If you only want a different header for the homepage, you can do the same thing for the view.header.html template.

Add dynamic navigation

The menu that the view.menu.html template builds lists all the children of the object on which it is called. In this simple system it is always called on the psite object, no matter which page you are viewing. There are lots of different ways to handle navigation in a site, this one is only usefull for a very small site. A more complex menu might show a list of pages in the current section, as well as the current location in the site, with a way to go back. Such a navigation system is easily built in Ariadne with only a few lines of html and pinp code:

pobject::view.body.html

<pinp>
ar::get($parent)->call(
'view.menu.html',
array( 'current' => $path)
);
ar::call('view.content.html');
</pinp>

pdir::view.body.html

<pinp>
ar::call('view.menu.html', array( 'current' => $path );
ar::call('view.content.html');
</pinp>

ppage::view.content.html

<div id="content">
<h1><pinp> echo $nlsdata->name; </pinp></h1>
<pinp>
echo ar('html')->menu( 'ul', array( 'id' => 'crumbs' ) )->crumbs();
</pinp>
<p class="summary"><pinp> echo $nlsdata->summary; </pinp></p>
<pinp>
echo ar('content/html')->parse( $nlsdata->page );
</pinp>
</div>

pobject::view.menu.html

<pinp>
echo ar('html')
->menu( 'ul', array( 'id' => 'menu' ) )
    ->current( ar::getvar('current') )
    ->bar();
</pinp>

In view.body.html we pass an extra argument 'current' to the view.menu.html template. In the view.menu.html template we retrieve the variable 'current' just like we would if it was an argument set in the config.ini template. In fact, even form input variables can be retrieved like this. The difference is that variables passed directly to a template like this are only available to retrieve in that template and no other. The menu library uses the current variable to set the current menu item in the menu.

All this looks like a lot of code when you look at the list of templates, but each template is very simple and does just one thing. In addition it is now very easy to create new templates which differ in just one way but use the existing templates for most of the layout, e.g. to show a sitemap or search results, etc.

Changing the look of a site for a specific section of the site is also easy, just redefine the templates that have a different layout, usually the header and footer, in the directory that has a different look. You don't need to change all the navigation and content templates.

Ordering a menu

The menu you've just made works, but suppose you would like to have it ordered alphabetically. There's an extra Ariadne method which allows you to define that, among other things:

pobject::view.menu.html

    <pinp>
echo ar('html')
->menu( 'ul', array( 'id' => 'menu') )
        ->current( ar::getvar('current') )
->fill(
ar::find("object.parent='$path'")
->order('name.value ASC')
);
</pinp>

Instead of the bar() method, we now use the fill() method and pass it the result of the ar::find() method. The find method is really the core of Ariadne's power. It allows you to search your entire site for objects matching a specific query and order it.

In this case we specify that we only want objects which have as a parent the current object, which is exactly what ar::ls() does. But we've also specified that the objects should be called in order, based on the value of the name property.

Ariadne stores the data in objects in a very generic way. This makes it hard for a database to search through it. So it also stores specific information for each object in indexes called 'properties'. One of these properties is called 'name', and it stores the $nlsdata->name value for each object. These properties can be used to search for a specific object, or as in this case, to order the results.

Suppose you want to have a specific ordering of the menu, e.g.:

  • Shop
  • Assortment
  • About Chocolate
  • About Chocoluck
  • Contact

In this case ordering based on the name won't work. You could create an array with the exact menu items in it, but this isn't very maintainable. Every time the menu needs changing, you will need to change the template and not everyone is a programmer.

Ariadne has a special property just for this, called 'priority'. Every object has this property, the default value is always 0. You can change the value through Ariadne's management interface, in the 'settings' section under the name 'priority'.

So set the following priorities:

  • 50 Shop
  • 40 Assortment
  • 30 About Chocolate
  • 20 About Chocoluck
  • 10 Contact

And change the menu template to this:

pobject::view.menu.html

    <pinp>
      echo ar('html')
->menu( 'ul', array( 'id' => 'menu' ) )
        ->current( ar::getvar('current') )
->fill(
         ar::find("object.parent='$path'")
             ->order('object.priority DESC')
      );
    </pinp>

Now the menu is just the way you like it. In addition any new menu entry is automatically added at the end, but anyone can change the order simply by entering a higher number in the priority field.

Hiding an object from the menu

One other thing we can improve on is the graphics folder. It is currently also shown in the menu, which isn't really what you want. You could create a new template pphotobook::show.menu.html and keep it empty. This will remove the graphics folder from the menu, but it also means you'll never see any photobook in the menu, ever. This may come to haunt you when you want to add a gallery of the chocoluck factory.

So we use the priority value again. Instead of a high value for the graphics folder, set it to -1. Now change the menu template to this:

pobject::view.menu.html

    <pinp>
      echo ar('html')
->menu( 'ul', array( 'id' => 'menu' ) )
        ->current( ar::getvar('current') )
->fill(
         ar::find("object.parent='$path' and object.priority>=0")
             ->order('object.priority DESC')
      );
    </pinp>

Now the graphics folder is gone, but you can still add your gallery later. Just add a few images and some text styling and the site is almost done. Except for one thing:

Make your work public

Ariadne is secure by default, it won't allow just anyone to see its contents, unless you tell it to. If a visitor hasn't logged on yet, Ariadne calls him or her Joe Public. This is the generic user account you can find in /system/users/. Joe Public has no rights to read anything in Ariadne by default. You can assign some grants to this user though. Just go to the psite object Chocoluck and press the  'grants' button in the 'settings' section. You'll see a dialog like this:

grants.empty.png

Click on the '...' button in the middle pane and select the user or group 'Public' (either will do). Then select only the checkbox next to 'Read' and press 'Apply'. The site is now public, no need to enter a username or password anymore.

You still have a rather unwieldy URL though, it probably looks something like this:

http://www.chocoluck.com/ariadne/loader.php/sites/chocoluck/

When what you really want is just this:

http://www.chocoluck.com/

For this to work you need to tell your webserver where to redirect requests for this URL to. This can be tricky and is different for each webserve, but this tutorial tells you how to configure Ariadne sites for Apache.

If that works, edit the psite object and in the URL value enter 'http://www.chocoluck.com/'. Make sure to include the 'http://' part and end it with a '/'. If your webserver is configured correctly, the site should use sparkling clean URLs. In fact, if you didn't know better you could never tell that the site uses Ariadne. 

Read more about templates in the next chapter.