File-Based Content Templates
John Forsythe recently posted that Contemplate is one of the most favorited modules on his DrupalModules.com site, attributable to the learning curve of creating template files.
With no offense to Jeff Robbins who created this very clever module, I've never been a big fan of Contemplate. I don't like it when people store code and markup in the database because it makes it impossible for me to find where their markup comes from when I search the files. It also puts it out of the reach of version control, and creates the possibility of a white-screen situation which can only be fixed by going into the database directly.
But contemplate fills a real void created by the difficulty in per-field theming in Drupal 5. I posted about my method of handling per-field theming in content types recently and have been refining that method since. The purpose of my technique is to allow per-field custom theming without requiring additional work every time a new field is added. Essentially you print out any fields you would like to custom theme, and then you loop through all the other fields and print them all out in the order and with the 'Display Fields' settings as set in CCK. Although this method requires additional setup up front, it pays off when you add new fields (or modules that add new node content of their own) and you don't have to readjust your template (or contemplate).
A client recently asked me "I'm using both contemplate and template files. Which one overrides which?" I don't have much experience with contemplate so I told him that basically "If you have to ask that question you know you're in trouble!" and advised to remove contemplate module. With further investigation, I see that contemplate seems to actually only control the $content variable of a node template (Is this correct? Hence its name no doubt.) Therefore it would not be completely nonsensical to use both contemplate and node templates so long as you left the $content variable in tact in your node templates.
This observation led me to the further observation that overriding the $content variable is really the only thing that people want to do in their node templates in general. I don't create a separate node-story.tpl.php and node-page.tpl.php and node-crazyform.tpl.php because I want to separately switch around the taxonomy or links sections. Much more likely I copy node.tpl.php and then override the $content variable, leaving everything else in tact. So in fact I don't ideally want to create a node-story.tpl.php- what I really need to create is a content-story.tpl.php. Such a template file would serve the purpose of contemplate while avoiding its markup-in-the-database pitfall.
To create a file-based content template system, you need to override the $content variable from within your template.php. This is done from the function _phptemplate_variables. Because I usually use Zen theme (you're back on top jjeff!) I override the variable within its phptemplate_preprocess_node function instead of from _phptemplate_variables. (You will find this function ready to be uncommented in your zen subtheme's template.php)
<?php
function MYTHEMENAME_preprocess_node(&$vars) {
$vars['content'] = _phptemplate_callback('content', $vars, array('content', 'content-'. $vars['type']));
}
?>Here you see I have overridden the node template's usual $content variable with a phptemplate callback. The callback will look for content-TYPE.tpl.php pages, defaulting to a content.tpl.php.
In my content.tpl.php I can put my per-field content theming code. I have improved it (i.e. made it even more complicated) since my first post on the idea.
My content.tpl.php contains:
<?php
$fields = $node->content;
unset($fields['#children']);
?>
<?php
/*
* add custom content field theming here
* unset each field after printing
* for example
* print '<div class="special">';
* print $fields['field_special']['#value];
* unset($fields['field_special']['#value]);
* print $fields['field_another']['#value];
* unset($fields['field_another']['#value]);
* print '</div>';
*/
?>
<?php
//loop through all remaining fields and print their values
if (is_array($fields)) {
foreach($fields as $field) {
//print field values
if ($value = $field['#value']) { print $value; }
//loop through groups
elseif (is_array($field)) {
unset($field['#children']);
foreach($field as $groupfield) {
if ($value = $groupfield['#value']) { print $value; }
}
}
}
} ?>This code loops through the pieces of $node->content, printing them all out with respect to the order and display settings you defined with CCK. It should handle fields that are within groups as well. With this template your nodes should look exactly as they did without it and when you add new fields and groups they will appear without further adjustment.
The portion of the template that is commented out is an example of how to use the template to do a custom output of certain fields. In the example two specific fields are put inside of a new div (a frequent need). Each field has to be unset after it is printed so that it is not printed again during the foreach loop that prints out each field. When setting up these custom per-field outputs you will want to be looking at the "Devel Render" tab of your node (use Devel module) for reference.
Unfortunately there is no really simple way to do these things in Drupal 5 and while I find this technique saves me time I realize that it may look less than simplistic to a non-coding themer. I hope it gives others further ideas of how to better simplify node content theming.














This is a little confusing
_phptemplate_variables exists only in Drupal 5 and earlier;
MYTHEME_preprocess_node exists only in Drupal 6 (and theoretically later).
_phptemplate_callback() also only exists in Drupal 5 and earlier.
I'm not sure how you've got something working like that.
Zen theme uses _preprocess_node in D5
Zen theme defines _preprocess_ functions in its _phptemplate_variables function in Drupal 5. It is part of its subtheme system.
Merlinofchaos -- she's using
Merlinofchaos -- she's using the Zen theme, from whence the _preprocess_node came from for D6 in the first place, right? Hence it's strange appearance in Drupal 5!
Jody, I started on this path, of printing all remaining even if in groups, after your original post. I got sidetracked before I tested it, but my one innovation was to wrap everything in functions, so that
phptemplate_print($fields['field_special']['#value]);would do the unsetting automatically, and then the code at the bottom was just a single function call also, like drupal_render is for forms.Thanks for continuing to refine this very cool method!
benjamin, Agaric Design Collective
mm.
Merlinofchaos -- she's using the Zen theme, from whence the _preprocess_node came from for D6 in the first place, right?
I can say with authority that they didn't originate with the zen theme. Zen may have used something similar but it seems more likely it later adopted (or changed over to) the mechanism put into Drupal core. A quick perusal of zen's template.php shows that it does support this, though it should be pointed out that in D5 this is something specific to zen.
Yes
No offense, but I think I did attempt to clearly point out that I used a different function than normal specifically because I use Zen theme.
I didn't follow it that closely
but I thought some of the work that went into the Zengine concept instead ended up in the phptemplate engine in D6.
For me the important issue for Jeff Robbins to settle is which approach is better-- the method here enabled by this innovation or the contemplate approach -- since I suspect he had a hand in both -- or if a fusion is possible.
benjamin, Agaric Design Collective
Nice
Including such a function was my thought for a next step as well and I even think that was the same thing I had in mind for namespace! I just wasn't sure whether that would make things more or less confusing overall. A lot of times people actually need to print the field out in other ways such as using
$node->field_foo[0]['view']as opposed to$node->content['field_foo']['#value']which further complicates the ability to define a printing/unsetting function.Contemplate can be disk based
I think contemplate can be disk based.
I have not tried it, but according to commit #77260 templates can be disk based. The README.txt file has some info on that too.
Oh, nice
Ok then I take back my contemplate critique. I will have to check that out.
I moved from using
I moved from using Contemplate to disk-based, but then wondered why I was bothering as I could just move the output of contemplate to my node-type.tpl.php files replacing the $content variable and be done with it.
So now, I just use contemplate to get the field info I want and move it straight into the tpl.php fields and disable contemplate for the content types. I can then easily go back to contemplate when I add a module that affects the $content for my content type and get the extra field info. Works for me, whether this is the *correct* way to do things or not.
Is there any benefit to sticking to disk-based contemplate over my way of working? Is it also not quicker to just have everything in my tpl.php files?
I think that method is fine
It's just that it drives me insane that I have to keep on going back and adding in new field info. That's what's driving me into these crazy templates.
Why do I have access to the
Why do I have access to the Full HTML filter?
Please see this movie:
Disk based
Contemplates can be disk based, I use the module in this mode normally.
I guess it comes down to preference though!
FYI
I'm noticing that you're using a lot of php in your .tpl file. Theming best practices call for there to be as little php as possible in the .tpl file: if() and _maybe_ a few else() statements. Anything more should go in template.php. This makes your theme much more scalable as you add more node-types, fields, and anything else.
Oh hey, I can add full html here. A bit of javascript that takes advantage of the latest Drupal security bugs and you're toast.
There's no need to exploit
There's no need to exploit the latest security bug in Drupal if you enable Full HTML for anonymous users. As one can easily steal the admin cookie, or change your password:
**** I deleted this anonymous posters tips for how to mess up Drupal sites with input format errors. I thought we don't necessarily want to share these kinds of tips and techniques publicly. - Jody****
Oops....
I just turned on anonymous comments and forgot to turn off full html as the default. Thanks, it's off now.
My fault
I think this was really my fault. Alex had the site set up one way and I made a hasty change without thinking about the filters or doing any testing.
Agreed, but....
I certainly agree with you about avoiding php in template files, but have not yet figured out a way to get this combination of custom and automatic templating without resorting to a lot of php. That same concern is really part of why I decided I might be better off keeping the node templates clean and moving my mucking into specific content.tpl.php files where it can at least be segregated.
no $messages
There's no $messages in your theme so I have no idea if the comment is successfully submitted.
Must be some other issue
This is like having a free QA department. We do have $messages in the theme though. I think perhaps because comments are not moderated that the comment should appear directly and no message is given?
Post new comment