Creating an Ariadne class
- OLD Ariadne >
- Library >
- Ariadne 2.6 >
- Howto's >
- Extending Ariadne >
This tutorial describes all the steps needed to create a new Ariadne class with new functionality through either PHP/PINP functions or templates.
PObject stands for 'Persistent Object'. This class knows how to store itself in the Ariadne Store and how to reinitialize from the store. It implements all the neccessary functions for the Ariadne Store to revive it. It also has a lot of extra code used in the Class Templates and in the Object Templates.
When you need to create a new Ariadne Class, all you need to do is inherit from PObject and you won't need to worry about storing/initializing your newly defined class yourself.
Creating a new 'minimal' Ariadne Class thus takes only a few steps:
1. Class definition
For a minimal Ariadne Class, all you need to do is create a new PHP Class that inherits a pre existing Ariadne Class. As an example we will create a new Class PURL, which can contain a link to an internet resource (webpage, ftp, etc.)
The definition for this class must be available to Ariadne in the 'lib/objects/' directory. As the type of the class is 'purl', the filename must be 'purl.php '.
Besides the URL it would be nice if you could enter a description and a name to the link. So perhaps it is a good idea to inherit from PPage instead of PObject. In that case the minimal class definition will be:
class purl extends ppage { // class definition } // end class purl
But to make sure that 'ppage' is known when Ariadne initiates this class, you must make sure that ppage is loaded, so purl.php becomes:
<?php debug("purl: Load","object"); include_once($this->code."objects/ppage.phtml"); class purl extends ppage { // class definition } // end class purl ?>
Make sure you leave no white space before '<?php' or after '?>', as that will break the Header() function of PHP.
The debug statement insures that later when debugging some code you can see that the purl class definition was loaded, which sometimes is helpfull information :) The include_once statement tells PHP to include the class definition for ppage only if wasn't loaded already. $this->code points to $store->code in this context, which contains the base directory for the PHP code library for this store.
As PObject, and therefore via PPage also PURL, expects class templates at a certain location which create the actual output for Ariadne calls, you must now define a minimal set of Class templates.
2. Class templates
The class templates are located in the directory 'lib/templates/'. Each class has its own subdirectory in which the a class can define or redefine a certain class template. For PURL this directory will be 'lib/templates/purl/'.
The class templates contain a lot of the code that makes the Ariadne management interface work, e.g. edit forms, the input checks on the form entry values, etc.
2.1. Protected and Unprotected Class Templates.
There are two classes of class templates, protected and
non-protected. Protected templates can not be overriden by object templates
(PINP), non-protected templates can. A protected template must end on
'.phtml' and either should not contain a call to $this->CheckConfig or leave out the $arCallFunction and $arCallArgs arguments. Non protected templates can
end
on anything (except '.phtml') but must always call $this->CheckConfig fully.
Examples:
cheap-protected.phtml:
<?php // do something ?>
protected.phtml:
<?php if ($this->CheckLogin("read") && $this->CheckConfig()) { // do something } ?>
nonprotected.html:
<?php if ($this->CheckLogin("read") && $this->CheckConfig($arCallFunction, $arCallArgs) { // do something } ?>
You should not use the 'cheap-protected.phtml' for anything that outputs data. As it has no CheckLogin check, anyone can call this template on any object. It is usually only used for those templates that are called often and only return data to be used by another template, e.g. system.get.name.phtml.
2.2. Minimal Set of Class Templates
Actually there is no minimal set. PURL would function fine without any new templates defined. However this is usually not very usefull, as that would mean that PURL would contain exactly the same data as it's parent class. In some cases that can be enough, e.g. the pnewspaper directory is almost empty, and only has it's own manage.html and view.html template. This is because it is only a container class for the particle class, and all special functionality is contained in the class definition.
In this case however we want to save some extra data in PURL that's not in PPage.
So, the minimal set of class templates that should be defined for this class are:
- classic.form.phtml
- This template contains the HTML form used for editing or creating a new object of this class, in the classic interface.
- edit.object.data.form.phtml
- This template contains the HTML form used for editing or creating a new object of this class, in the new interface, using the wizard widget.
- system.save.data.phtml
- This template contains all the checks on the input given in the form templates, and calls the actual save routine.
If you want to create a new wizard instead of using the default wizard, you'll need to redefine these templates:
- object.new.flow.phtml
- This template contains the flow of steps used when creating a new object of this class.
- edit.object.data.phtml
- This template contains the flow of steps used to edit an object of this class. It also calls the wizard.
You will then have to add templates according to the definitions in these two templates.
Since usually you will want to view each object of a class according to it's own data fields, you'll also have to change the 'view.html ' template.
One last thing you'll need to remember: if you need to use new nls terms, you will have to create nls files too. E.g. in this case we need an extra string 'type', 'url' and 'editurl'. For that reason we'll nedd to create the following nls files: purl.en, purl.es, etc. in ariadne/lib/nls/. You can skip this and use fixed strings instead of language specific ones if you're not planning on distributing your new class.
2.2.1. classic.form.phtml
<?php include($this->store->code."nls/purl.".$this->reqnls); ?> ... </tr><tr> <td align="right"> <font face="helvetica, sans-serif"><b> <?php echo $ARnls["type"]; ?> : </b></font> </td> <td> <select name="url_type"> <?php $this->ls("/system/purl/types/", "show.option.value.phtml", Array("selected" => $this->getdata("url_type","none"))); ?> </select> </td> </tr><tr> <td align="right"> <font face="helvetica, sans-serif"><b> <?php echo $ARnls["url"]; ?> : </b></font> </td> <td> <input type="text" name="url_location" maxlength="255" size="50" value="<?php $this->showdata("url","none"); ?>"> </td> ...
2.2.2. edit.object.data.form.phtml
<?php include($this->store->code."nls/purl.".$this->reqnls); ?> ... </tr><tr> <td> <span class="required"><?php echo $ARnls["type"]; ?></span> </td> </tr><tr> <td colspan="2"> <select name="url_type"> <?php $this->ls("/system/purl/types/", "show.option.value.phtml", Array("selected" => $this->getdata("url_type","none"))); ?> </select> </td> </tr><tr> <td> <span class="required"><?php echo $ARnls["url"]; ?></span> </td> </tr><tr> <td colspan="2"> <input type="text" name="url_location" size="35" maxlength="255" value="<?php $this->showdata("url_location", "none"); ?>"> </td> ...
2.2.3. system.save.data.phtml
... include($this->store->code."nls/purl.".$this->reqnls): $url_location=$this->getdata("url_location","none"); $url_type=$this->getdata("url_type","none"); $valid_types=$this->ls("/system/purl/types/","system.get.value.phtml"); if (!in_array($type, $valid_types)) { $this->error=sprintf($ARnls["err:invalidtype"],$type); } else if (strlen($url)>255) { $this->error=$ARnls["err:urltoolong"]; } else { $properties["url"]["type"]="'".AddSlashes($type)."'"; $properties["url"]["location"]="'".AddSlashes($url)."'"; $this->data->url_type=$url_type; $this->data->url_location=$url_location; } ...
3. Class installation
Creating a Class definition and Class templates doesn't make Ariadne aware of the existence of your new class. For that you will need to create an installation script that enters some vital information in the Ariadne Store.
You will need to add the new class name to Ariadne's list of known types in the Ariadne Store directory at /system/ariadne/types/ .
You will also need to tell Ariadne which interfaces it implements. If you want to use special properties, you'll need to define those too. And finally you'll need to enter the typetree information in the /system/ariadne/typetree/normal/ directory in the Ariadne store.
The install script is a script that must only be run on install time (obviously), from the www/install/ directory. So it is a standalone script that must instantiate the Ariadne Store itself. To access the functions needed for installation, it calls a special version of the Ariadne store, the Install-Store. Thus the script starts as follows:
#!/usr/local/bin/php -q <?php // try to find out where ariadne is installed if (!@include_once("../ariadne.inc")) { chdir(substr($_SERVER['PHP_SELF'], 0, strrpos($_SERVER['PHP_SELF'], '/'))); if(!include_once("../ariadne.inc")){ echo "could not open or find ariadne.inc"; exit(1); } } // load configuration files require($ariadne."/configs/ariadne.phtml"); require($ariadne."/configs/store.phtml"); require($ariadne."/includes/loader.web.php"); include($ariadne."/nls/".$AR->nls->default); // instantiate the install store $inst_store = $store_config["dbms"]."store_install"; require($ariadne."/stores/".$inst_store.".phtml"); $store=new $inst_store(".",$store_config);
In the main Ariadne install script the next line initializes the Ariadne store for use. As this script should be run after the main install script, this should not be repeated here.
The first line (before '<?php') is a way to tell unix systems that this script is a sort of batch file and that it uses PHP as an interpreter.
Now we add the interfaces list to the Ariadne Store. For historic reasons the function used is called 'add_type'. The first argument is the name of the class, the second is the name of the interface it implements. Each class name also doubles as an interface name. The reverse is not true.
$store->add_type("purl","pobject"); $store->add_type("purl","ppage"); $store->add_type("purl","purl");
The last line should not be forgotten. Each class always implements itself.
The next step is to define the properties. Suppose we defined a new property with the name 'url' and the name value pairs 'type' and 'location'. 'type' with a max string length of 16, and 'location' with a max string length of 255. Then the full property definition is:
$url["type"]["string"]=16; $url["location"]["string"]=255; $store->create_property("url", $url);
The alternative values for "string" are "boolean" and "number". Instead of the size as value, a 1 will suffice for those types.
For the typetree list we'll assume that you want to be able to add a purl to a pdir, a puser and a psite. You cannot add another object below a purl.
Now you only need to add the class name to the list of available classes and to the typetree list:
// first set the default nls values $nls=new object; $nls->default='en'; $nls->list['en']=$AR->nls->list['en']; $nls->list['nl']=$AR->nls->list['nl']; // now set the data for the type information of the class $data=new object; $data->en=new object; $data->en->name='URL'; $data->nl=new object; $data->nl->name='URL'; $data->value='purl'; $data->nls=$nls; // now save the data in the ariadne store $store->save('/system/ariadne/types/purl/', 'pobject', $data); // add links to the type information in the typetree $store->link('/system/ariadne/types/purl/', '/system/ariadne/typetree/normal/purl/'); $store->link('/system/ariadne/types/purl/', '/system/ariadne/typetree/normal/pdir/purl/'); $store->link('/system/ariadne/types/purl/', '/system/ariadne/typetree/normal/puser/purl/'); $store->link('/system/ariadne/types/purl/', '/system/ariadne/typetree/normal/psite/purl/');
In this case we need some default information about url types too. So we create a directory and some objects (ftp and http) for that:
// now save the data in the ariadne store $store->save('/system/purl/', 'pdir', $data); // now set the data for the url types directory $data=new object; $data->en=new object; $data->en->name='Types'; $data->nl=new object; $data->nl->name='Types'; $data->nls=$nls; $store->save('/system/purl/types/', 'pdir', $data); $data=new object; $data->en=new object; $data->en->name='HTTP'; $data->nl=new object; $data->nl->name='HTTP'; $data->value="http:"; $data->nls=$nls; $store->save('/system/purl/types/http/', 'pobject', $data); $data=new object; $data->en=new object; $data->en->name='FTP'; $data->nl=new object; $data->nl->name='FTP'; $data->value="ftp:"; $data->nls=$nls; $store->save('/system/purl/types/ftp/', 'pobject', $data);
Finally all you need to do is close the store.
$store->close(); ?>
After running this script, you should be able to create and edit new objects of class purl.
There's just one step left: creating an icon to show the object in directory lists etc. You need two icons; a small icon (18x18), and a small icon with the shortcut symbol on top. These should both be named purl.gif, but the first must be placed in ariadne/www/icons/ and the other in ariadne/www/icons/pshortcut/ .