CTLModuleMaker FAQ

Can I put smarty code in the item's fields?

Yes.

Let's say you have products which have a description field, and in that description you would like to put some smarty code. If you try and put, say, {title} in an attempt to display the page title, you should notice that "{title}" will be displayed exactly like that in the product's description. That is because items' fields aren't evaluated.

However, in your template, you may choose to evaluate the fields. For example, in your template, if instead of the simple {$item->description} you write:

{eval var=$item->description}

You will then notice that {title} is evaluated, and the page's title is displayed.

Back to top
How can I retrieve images in the display templates?

You put {$item->image} in your template, and the image doesn't show up, it just displays /folder/nameofimage.jpg ...

...

That's pretty much normal.

Not all people want to simply "display" the image in the old fashioned way. For this reason, what is given is the path to the image, starting from your upload dir. If you want to display the image, you'd have to put something like this in your template:

<img src="uploads/somefolder/{$item->image}" alt=""/>

Back to top
I'm trying to display a list of items, but the detail template is used instead. What's going on?

If you're on the final level and there's only one item to show, the module won't display the list view but will immediately show the details of this item, so that the user doesn't have to click twice for no reason.

If you want to display the list view anyway, there is now a "forcelist" parameter that will do the job (see the help of your module). Your tag should become:

{cms_module module="products" what="product" forcelist="1"}
Back to top
Can I change the field inputs that are displayed in the module's advanced search form?

Yes.

The search form for the module's search action are generated from templates that can be found in the "templates" folder of your module.

The "search.tpl" file is the template for the search form when the "simple" searchmode is chosen.

The "search_generalresults.tpl" file is the template for the search results when we are searching in more than one level at the same time. Otherwise, the level's default list module is used.

For each level of your module, there should be a file named "search_NAMEOFTHELEVEL.tpl". This contains the template for the advanced search form for this level. If you want to remove a field input from the search form, you can simply delete the appropriate section of the template. (I would recommand to make a backup of the original template, if you ever change your mind!)

Back to top
Can I change the edit form for a level?

Yes.

The edit forms for each level are generated from templates that can be found in the "templates" folder of your module. For each level of your module, the template for the edit formre should be in a file named "editPREFIXOFTHELEVEL.tpl".

You can modify the template, but keep two things in mind:
- If you remove an input, the form might not work anymore.
- If you wish to change the field labels, you should preferably do so in the language file.

Back to top
How are the defaultadmin panels generated?

If you open the /templates/ folder, you will notice that there's only one template for the defaultadmin : adminpanel.tpl. This is used for all levels. So the difference is the information passed to the template. Before taking a look at it, let's see that information.
Open the action.defaultadmin.php of your module. You will notice that it is divided into tabs (delimited by echo $this->StartTab("nameoflevel"); and echo $this->EndTab();). For each level tab, the list of its items is fetched and stored into $itemlist, which is then passed to smarty. $itemlist is an array of objects having your fields as attributes ($item->field).
Just below that, you will notice lines that look like this:

		$adminshow = array(
			array($this->Lang("name"),"editlink"),
			array($this->Lang("alias"),"alias"),
			array($this->Lang("active"),"toggleactive"),
			array($this->Lang("nbchildren"),"nbchildren"),
			array($this->Lang("reorder"),"movelinks"),
			array($this->Lang("Actions"),"deletelink")		
			);
		$this->smarty->assign("adminshow", $adminshow);
		echo $this->ProcessTemplate("adminpanel.tpl");

The $adminshow variable contains what should be shown in the adminpanel of that level. We then pass this variable to smarty, and the last line tells smarty to display the template "adminpanel.tpl".
$adminshow is an array, where each element represents a column of the adminpanel. Each column, in turn, is an array containing two values: the first (position 0) is the title of the column, and the second (position 1) is the name of the object's field that should be shown in this column.

For example, array($this->Lang("name"),"editlink") means, here, that we take $this->Lang("name") ("name" in the language that is currently displayed) to be the title of the column, and that for each row of the table, whatever is in $oneitem->editlink will be displayed (this particular value is not strictly speaking a field of the level, but an attribute created by the addadminlinks() function).

Now, let's take a look at the adminpanel.tpl file:

#1		<div>
#2		<table cellspacing="0" class="pagetable">
#3		<thead><tr>
#4			{foreach from=$adminshow item=column}
#5				<th>{$column[0]}</th>
#6			{/foreach}
#7		</tr></thead>
#8		<tbody>
#9		{cycle values="row2,row1" assign=rowclass reset=true}
#10		{foreach from=$itemlist item=oneitem}
#11		{cycle values="row2,row1" assign=rowclass}
#12		<tr class="{$rowclass}" onmouseover="this.className='{$rowclass}hover';" onmouseout="this.className='{$rowclass}';">
#13			{foreach from=$adminshow item=column}
#14				{assign var=oneval value=$column[1]}
#15				<td>{$oneitem->$oneval}</td>
#16			{/foreach}
#17		</tr>
#18		{/foreach}
#19		</tbody>
#20		</table>
#21		</div>

Now, I will assume that everyone understands the html tags, and will leave alone the code concerning the classes. I will explain lines #4 to #6, which write the table header (the titles of the columns), and lines #9 to #18, which write the table rows.

Line #5 is executed for each element of the $adminshow variable; in other words, for each column of the table. Now, remember that each element of the $adminshow variable is an array of two elements, so what line #5 does is display the first (position [0]) of these two elements as the column header.

Lines #11 to #17 are repeated for each item in $itemlist (for each row of the table). For each row, for each element of the $adminshow variable, lines #14 and #15 are repeated. Line #14 fetch the name of the object's attribute that should be displayed (position [1] of the adminshow element), and line #15 displays this attribute of the current object.

Alright... how can I change this?

If you want to make major changes, the best thing to do would be to create an individual template for your level. In the templates/ folder, create a file (like "adminpanel_nameoflevel.tpl"), put the template you wish to use in it, and in the according tab of the action.defaultadmin.php file, change
echo $this->ProcessTemplate("adminpanel.tpl");
to
echo $this->ProcessTemplate("adminpanel_nameoflevel.tpl");

Now, let's say you wish to make a very simple modification. For example, the adminpanel shows either a "1" or a "0" for some field (which we'll call "myfield"), and you would like to display "Yes" and "No" instead. What you could do is this:
In the adminpanel.tpl template file, you could change line #15 to something like this:

			<td>{if $oneval == 'myfield'}
				{if $oneitem->$oneval == 1}Yes{else}No{/if}
			{else}
				{$oneitem->$oneval}
			{/if}</td>

This means that smarty checks if the field we're displaying is 'myfield', and if it is, instead of simply displaying it, it will check if it is equal to "1", display "Yes" if it is, and "No" if it isn't.

Back to top
Could I show the name of the selected item in, say, the page's <title> tag?

[*** See update below... ***]

The short answer: no.

The long answer: yes, most of the time.

The name of the selected item or category is retrieved only when the {cms_module module="yourmodule"} tag is encountered in the template - which is quite always way after the page's head. However, in most cases in could be retrieved by a user-defined tag (UDT).

Note that this will no work when the tag is directly called on the page, and will only work when we are following a module action (when you have clicked on a link)

Create the following UDT, replacing NAME_OF_YOUR_MODULE with your module's name:

$modulename = 'NAME_OF_YOUR_MODULE';
global $gCms;
if( isset($gCms->modules[$modulename]) &&
    $gCms->modules[$modulename]['active'] &&
    isset($params['assign']) ){
	global $smarty;
	$instance = $gCms->modules[$modulename]['object'];
	$glob = $instance->get_moduleGetVars();
	$wantedlevel = false;
	$modlevels = $instance->get_levelarray();
	if(isset($glob['alias'])){
		$wantedlevel = isset($glob['what'])?$glob['what']:$modlevels[count($modlevels)-1];
		$alias = $glob['alias'];
	}elseif(isset($glob['parent'])){
		if(isset($glob['what'])){
			$wantedlevel = $instance->get_nextlevel($glob['what'], false);
		}else{
			$wantedlevel =$modlevels[count($modlevels)-2];
		}
		$alias = $glob['parent'];
	}
	if($wantedlevel){
		$getfunction = 'get_level_'.$wantedlevel;
		$item = $instance->$getfunction(array('alias'=>$alias));
		if(isset($item[0]))	$smarty->assign($params['assign'],$item[0]);
	}
}
	

Let's say you've named your tag "retrievemodinfo". Then, at the beginning of your page's template, you retrieve the module informations by using the tag {retrievemodinfo assign="currentelement"}. This means that the information about the current item are stored in the variable $currentelement (which could have been anything). So in the <title> tag of your template, you could write: {if $currentelement}{$currentelement->name}{/if}, which would display the name of the current element if it could have been retrieved.

UPDATE: since version 1.8, you can do the same thing much more easily just by using the "breadcrumbs" action and its parameters.

Back to top
Can I change how the pretty urls are generated and interpreted?

Yes, to some extent.

I'll try to make this very comprehensive. Before talking of modying it, let's see how this works.

Basically, the module has two kinds of urls:
Type A: http://www.mydomain.com/nameofmodule/detail/nameofobject/5/
and
Type B: http://www.mydomain.com/nameofmodule/nameoflevel/nameofparent/5/
(if you are separating elements into pages, you will notice that type B has some variations... we will discuss this later.)

Type A is used when we are looking at the details of a last-level object. Type B is used when we display a list of items. Now, remember that the url has to contain all the information about what should be displayed. So let's take a look about what's common to both types of urls:

The nameofmodule/ tells us that the module should be called.
The 5/ (that annoying number) is the returnid of the page. In other words, it tells us on what page of the cms the informations of the module should be shown. Even though the page content is replaced by what the module has to display, this is very important as it determines, for instance, what page template should be used.

When the /detail/ is in the url, the module knows that we're displaying the details of a last level item. All we need to specify is which item to display, and thats the nameofobject part.

When the /detail/ isn't in the url, the module concludes that we are displaying a list of something*. The elements of what level are displayed is determined by whatever is in the place of "/detail/": in our case, nameoflevel/. A url like http://www.mydomain.com/nameofmodule/product/5/ would be valid, and would tell the module to display all elements of the "product" level.
The /nameofparent/ part tells the module to display only the items of the chosen level that have "nameofparent" as a parent.

The pretty urls are registered in the SetParameters() function of the main module file (which should be something like NAMEOFYOURMODULE.module.php). Open this file, and do a search to find the function. What matters in there is the lines that look like this (without the numbers on the left) :

#1	$this->RegisterRoute("/[nN]ameofmodule\/([Dd]etail)\/(?P[^\/]+)\/(?P[0-9]+)$/");
#2	$this->RegisterRoute("/[nN]ameofmodule\/(?P[^\/]+)\/(?P[0-9]+)$/");
#3	$this->RegisterRoute("/[nN]ameofmodule\/(?P[^\/]+)\/(?P[^\/]+)\/(?P[0-9]+)$/");
#4	$this->RegisterRoute("/[nN]ameofmodule\/(?P[^\/]+)\/(?P[0-9]+)\/(?P[0-9]+)\/(?P[0-9]+)$/");
#5	$this->RegisterRoute("/[nN]ameofmodule\/(?P[^\/]+)\/(?P[^\/]+)\/(?P[0-9]+)\/(?P[0-9]+)\/(?P[0-9]+)$/");

Each line here register a different url structure.
Line #1 registers the Type A urls.
All the other lines register Type B urls:
Line #2 is for when no parent is specified, like in http://www.mydomain.com/nameofmodule/nameoflevel/5
Line #3 is used when a parent is specified, like in http://www.mydomain.com/nameofmodule/nameoflevel/nameofparent/5
Line #4 is a variation of line #2 that handles separation into pages (when we separate into pages, we need to know which page we're looking at). For example, http://www.mydomain.com/nameofmodule/product/2/10/5 would mean that we are looking at the list of all items of the "product" level, that we are separating in pages with "10" elements per page, and that we are looking at the page "2".
In pretty much the same way, line #5 is a variation of line #3.

The <paramname> thing means that whatever is in that place in the url will be interpreted as the "paramname" parameter. In other words, each url structure output different parameters that will tell the module what to do.

Modifying all this...
There are some things that you can change, some that you can't, and some that you can at a price.

Let's say you don't like the word "detail" in the url, and would like to change it to something else**. First, let's not that the "[Dd]" part in the url structure says that there can be either a "D" or a "d" here. In other words, http://www.mydomain.com/nameofmodule/detail/nameofobject/5/ is the same thing as http://www.mydomain.com/nameofmodule/Detail/nameofobject/5/.
You can change the word "detail" for something else, but you have to be careful and remember that the url carry all necessary information about what should be shown. For example, if one of your level is named "product", you couldn't replace the word "detail" by the word "product". If you did, the module couldn't know if the word "product" it encounters means that we're watching the details of an item, or if it means that we are watching a list of the items in the "product" level.

So let's say we want to change "detail" for "view" (provided that you don't have a level called "view"). In line #1, we would replace ([Dd]etail) with (view) (or ([Vv]iew), if you want it to accept both "View" and "view").

But this is not enough. You've changed the way the urls are interpreted, but not the way they are created. In the same file, search for the BuildPrettyURLs function. It should contain something like this:

#1		$prettyurl = "nameofmodule/";
#2		if(isset($params["alias"])){
#3			$prettyurl .= "detail/".$params["alias"];
#4		}elseif(isset($params["parent"])){
#5			$prettyurl .= $params["what"]."/".$params["parent"];
#6		}else{
#7			$prettyurl .= $params["what"];
#8		}
#9		if(!isset($params["alias"]) && isset($params["pageindex"]) && isset($params["nbperpage"]))	$prettyurl .= "/".$params["pageindex"]."/".$params["nbperpage"];
#10		$prettyurl .= "/".$returnid;
#11		return $prettyurl;

This function transforms the parameters into a url.
Line #1 starts with the module's name.
In lines #2-#3, if we want to watch the details of an item, we add the "detail/" block to the url.
If this isn't the case, we add the level of the items we want to show and, if specified, the parent (lines #4 to #8). In line #9, if we're separating into pages we add the page we wish to look and the number of elements per page. Finally, in line #10, we add the id of the page in which it should be displayed.

If we changed "[Dd]etail" to "[Vv]iew" in the SetParameters() function, we must also change the "detail/" in line #3 for "view/".
Now, this should do the trick.

Can I get rid of the returnid?
No, not really. However, if you the module is always displayed in the same page (i.e., if it's always the same number), we could remove it from the url and provide it in another way. Here's how to do it:

Let's say the id of the page in which the module is always called (the number we're trying to get rid of) is 5.
1. in the SetParameters() function of your module, remove all \/(?P[0-9]+) parts. (it would be a good idea to copy and paste the original lines just in case... you can comment lines using //).
2. in the BuildPrettyURLs() function of your module, comment (add // before it) the line #10 (// $prettyurl .= "/".$returnid;).
3. we now need to provide the id. Go back to the SetParameters() function, and for each line, just after the $"/ part (and before the closure of the parenthesis), add , array("returnid"=>5) (with the comma), where "5" is your id. For example, this:
$this->RegisterRoute("/[nN]ameofmodule\/([Dd]etail)\/(?P[^\/]+)$/");
should become:
$this->RegisterRoute("/[nN]ameofmodule\/([Dd]etail)\/(?P[^\/]+)$/", array("returnid"=>5));
(Remember not just to copy and paste lines from this help, because your module is probably not named "nameofmodule")
This should do the trick, but the module will never use another page to display its content.

Final notice: If you change the pretty urls and make use of the "is_selected" attributes in your templates, you might also have to change the get_moduleGetVars() function (in the main module file).


* if there is only one item to display, and if you aren't using the "forcelist" parameter, the details will be displayed instead of a list.
** no, you can't just get rid of it, unless you change all registered urls... for example, instead of having a "/detail/", all other urls could have a "/list/" block.

Back to top
Can I make my module always act as if the "forcelist" parameter was enabled?

Yes.

Open the action.default.php file. Around the line #16 you should see something like this:

		$forcelist = isset($params["forcelist"])?$params["forcelist"]:false;

What this says is that if the forcelist parameter has been set, we use this value, and if it hasn't, we use the value "false".
Changing this false to a true would mean that the forcelist mode is always enabled, unless specified otherwise.

Notice that two lines below, you could do the same thing for the "inline" parameter...

Back to top