Lullabot Ideas
We know stuff. We empower you to know stuff too.
Display Suite: Building Fancy Teasers Without Custom Templates
Article by Karen StevensonOctober 4, 2011 - 1:00pm
I've been working on a Fantasy Site for next week's Do It With Drupal conference, a Drupal version of the Meetup.com site. It's involved digging deep into the Drupal 7 versions of Organic Groups, Views, Panels, Date, and lots of other modules. I quickly identified the main content types I needed: Group, Meeting, Place, and RSVP, but looking around the site I soon realized it would need extremely complex 'teaser' views of all these types. Each one would require lots of information from the content itself as well as related content, and each content type would need several totally different 'teaser' views.
The Challenge
The site's 'Group' content type is a good example. It needs a square teaser like the following for display on the home page. It includes the title, image, and location of the Group along with information about the most recent meeting.

That one seems relatively straightforward, but there is a totally different three column teaser for the 'Group' content type used on the 'Find a Group' page. Note that this group teaser has group information, membership information, and even includes what looks like an embedded 'teaser' view of next upcoming meeting.

The 'Meeting' content type, in turn, needs multiple different teasers. In addition to the version embedded in the 'Group' teaser above, there is a different, square view used in a carousel at the top of the 'Find a Group' page that looks like the following.

And there is yet another iteration of the Meeting 'teaser' used in views of upcoming meetings:

Alternative Solutions
Drupal gives us quite a few ways to create those different teasers. Since they are all displayed in Views, I could try to construct them by assembling each individual fields in the view itself. That would require lots of relationships to join in all the required content, though, and a lot of custom rewrites to format them correctly. In addition, much of the information comes from Organic Groups, but the D7 version of Organic Groups doesn't yet expose all of the necessary fields via its Views Relationships. (There is an issue about this on the Organic Groups issue queue, and hopefully that will be rectified in the future). Even without the Organic Groups problem, adding each individual field to a view and getting them to display exactly as we want them to would be challenging.
Another way to approach the problem would be to create a custom node .tpl file for each content type, and manually add the html to each template to create the variations. This approach to creating the display variations would simplify the views considerably. Each view would be a simple 'content' view rather than a 'fields' view, and would use the the 'teaser,' 'square,' or 'carousel' view mode.
To do this manually, I would have to create a separate .tpl file for each variation, create a preprocess hook to prepare the values they all need, add some custom code to define the additional 'View modes' for each of my content types, finally add theme suggestions so Drupal will look for a different .tpl file for each view mode.
However, one of the requirements of the Meetup.com site is that each group be able to set its own theme. If I used custom .tpl files in the theme, I would have to replicate them all in every theme exposed to the groups. I really wanted a way to create the 'structure' of the teasers independently of the theme. What's the solution?
Enter Display Suite
That set of requirements led me to the Display Suite module. As its name implies, Display Suite provides a collection of tools to control the display of Drupal entities. It allows you to create as many custom 'View modes' as necessary; use a different layouts and place fields differently in each display mode; and use pre-build Display Suite layouts, existing Panels layouts, or build your own. In addition, it allows you to create custom 'fields' in addition to the node fields that would ordinarily be available, and place those fields wherever you need them.
When you enable Display Suite and visit its settings page at admin/structure/ds, you'll see a screen like the following:

The screen provides quick links to the 'Display Fields' screens for each entity and content type (some of which are otherwise pretty well buried in various places in the administration area). You can create custom View modes from this screen, and you can add custom fields.
The custom fields can be created in various ways; Display Suite can locate values that you've created in a custom preprocess function, or you can paste in custom PHP code directly. For the Fantasy site, I chose to create some of the complex values I needed in hook_node_preprocess(), then add them to Display Suite as 'Preprocess' fields for placement in my teasers.
For the complex group teaser illustrated above, I needed three values that weren't otherwise available: a count of the group members (member_count), a formatted verision of the city and state the group is located in (formatted_location_text), and a 'square' teaser view of the next meeting (next_meeting_view). As you can see from the screenshot above, if the group is private or there is no next meeting, that value needs to have some placeholder text to indicate that. The following code does the work of building those values in a preprocess function:
<?php
/**
* Implements hook_preprocess_nodee().
*/
function groupal_preprocess_node(&$vars) {
global $user;
$node = $vars['node'];
switch($node->type) {
case 'group':
// Get the group and its gid.
$group = og_get_group('node', $node->nid);
$gid = $group->gid;
// See if the group is public or private.
$access = field_get_items('node', $node, 'group_access');
$private = $access[0]['value'];
if ($private && og_user_access($gid, 'view meeting content')) {
$private = FALSE;
}
// Get a membership count.
$memberships = og_membership_load_multiple(FALSE, array('gid' => $gid, 'entity_type' => 'user'));
$vars['member_count'] = '<div class="groupal-member-count">' . t("@count members", array('@count' => count($memberships))) . '</div>';
// Get a view of the next meeting.
$next_meeting_id = // Logic to retrieve the nid of the next meeting for this group goes here;
$next_meeting = node_load($next_meeting_id);
if ($private) {
$vars['next_meeting_view'] = '<div class="private-meetings-text">' . t('Meeting details are available only to members.') . '</div>';
}
elseif (!empty($next_meeting)) {
$vars['next_meeting_view'] = '<div class="next-meetings-text">' . drupal_render(node_view($next_meeting, 'square')) . '</div>';
}
else {
$vars['next_meeting_view'] = '<div class="no-meetings-text">' . t('There are no upcoming meetings for this group.') . '</div>';
}
// Get formatted text to display the location the way we want it.
$vars['location_formatted_text'] = '<span class="group-location-formatted">' . t('@city, @state</span>', array(
'@city' => $node->locations[0]['city'],
'@state' => $node->locations[0]['province'],
));
}
}
?>Now that those values are available in the preprocess function, I can go into Display Suite and add my new 'Preprocess' fields.

For each one, I create a field that has a machine name matching the name of the variable from the preprocess function above.

Then I go to the Display Fields screen for the 'Group' content type and select the 'Teaser' view. It looks like it normally would, until I choose the Display Suite option to use a custom layout -- in this case a three column layout.
Once I select the three column layout, lots of new fields will become available. Some of them are the fields I always see, the ones added by Drupal's Field API tools. Others are new ones that Display Suite itself adds, like a customizable 'Title' field, a 'Read more' link, and the image of the node author, each of which can be placed and styled like standard fields. In addition, I see the preprocess fields I defined earlier.
In the screenshot below, you can see the way the 'Display Fields' screen looks for the Group teaser after setting it up to use Display Suite's 3 column layout, as well as adding custom fields like 'Location Formatted Text' and 'Next Meeting View' to the appropriate regions of the layout.

At this point the only custom code I have created is the preprocess hook and some custom css; I have't used any custom .tpl files, either. If I add the preprocess hook and css in a custom module rather than in my theme, it will be available across all themes. That way, when users are given the ability to switch themes, all the complex teasers will still contain the right information in the right layout. Better yet, all the Display Suite configuration is exportable, so it can be captured in a Feature and deployed elsewhere.
If you're interested in seeing more about how this was done, join me for the Meetup.com Fantasy Site session at Do It With Drupal.

Comments
Looks interesting
We've been talking about giving Display Suite a go recently, but haven't been able to set aside time for it quite yet.
They way I would've solved this is with a custom module that creates one or more new display types (in addition to full and teaser). That way you can get different type of teasers, and won't need another layer of modules (the module required to add a custom display is only about 10 lines of code).
Do you see any big advantages from using Display Suite compared to custom displays?
One thing that I've been fearing is what you do when Display Suite isn't enough for the customization needs. Is it still compatible with tpl.php's that you need to do custom alterations to?
I noted writing custom code
I noted writing custom code as another alternative in the article above. Yes, it would only take a few lines of code to create a couple new views modes, but that would not eliminate the need for a lot of custom .tpl files, which would then have to be replicated in every theme. And that would be quite a lot more than 10 lines of code. In mind that's the beauty of the DS solution, the ability to create a lot of very complex alternative views without creating ANY custom .tpl files.
DS also gives you an easy way to break up the content of the node into all its individual bits. If you were writing custom code and wanted that level of control you would have to do additional work to break the $content value up into it components, or show/hide parts of it in different places, to get the same effect I have out of the box placing the pre-configured values provided by Display Suite.
It makes the process of creating these crazy teaser variations almost painless and very fast.
tpl files in modules
You could also provide those template file in modules, you just need to register them in hook_theme then.
Also Display Suite does
Also Display Suite does support using custom .tpl files, it just makes it possible to do quite a lot without them. The screen where you create the view modes gives you instructions on how to override the templates if you want to.
Entity View Mode
If all you want is custom display modes, there is a module specifically for that: http://drupal.org/project/entity_view_mode
It offers just a way to add new view modes, and gives you a template suggestion for each. (node--viewmode.tpl.php).
Not a replacement for DS, but if you need something lighter there's no need for a custom module. It's already there. :-)
Actually, I meant to mention
Actually, I meant to mention that module but forgot to. That is indeed a very easy solution if all you want is some additional view modes.
Using EVA
Nice approach and writeup!
For those not ready to wade into the (short but not entirely simple) hook_preprocess_node() work, an alternative might be to use the EVA module, which allows you to create views that you can attach to entities (nodes, users, taxonomy terms) and their "bundles" like a node type. The steps would be:
1. Create a new view using the EVA's "Entity content" display type and attaching it to the node type ("bundle") you want it to display with. Doing so makes the view available as a field on the content type.
2. Select this field when configuring the content type display.
My buns!
Cool, you used my picture of German Brötchen =)
I took the images from
I took the images from http://lorempixum.com/. I have no idea how they got your picture :) Also, I too love German Brötchen, I lived there for two years and spent half of that time in those wonderful bakeries.
Pics or it didn't happen, as they say
http://www.flickr.com/photos/robertdouglass/2326937740/in/photostream
There are a load of videos on
There are a load of videos on YouTube which are really useful in getting up to speed with Display Suite.
http://www.youtube.com/watch?v=MRZaCdkNIyI&feature=bf_next&list=PL7E361A...
Performance
What are the performance implications of using this? By using views on content mode instead of fields mode you make several more queries to the database. Do you think with caching this is to be ignored because it doesn't hurt that much?
It's the other way around
A view using a content mode makes fewer queries because it uses a heavily cached copy of the node rather than a fresh query of every field value. That was true in D6 but is even more true in D7. So doing it this way should be more performant.
RSVP in D7?
Thank you for the writeup! I had similarly fallen in love with Display Suite -- it's very powerful without causing much of a performance hit as far as I can tell.
Quick Question regarding the RSVP module: Did you use a D7 version of 'RSVP'? I only see it available for D6. (http://drupal.org/node/913560)
Sidenote: I tried using CCK Signup instead of RSVP for my project, but found it difficult to integrate it with Display Suite. The DS creator kindly tried to help, but I still couldn't get there in D7.
Really wish I could make the presentation...
Best,
Walter
There is a content type
There is a content type called 'RSVP', to match what it is called in Meetup.com. That does not mean I used the RSVP module. I used the CCK Signup module because I needed the RSVPs to be nodes. If that had not been a requirement I could have used the Signup module instead. Both of them have a D7 version.
hello Karen. Have you check
hello Karen.
Have you check the speed of a website using tpls and/or D.S. ?
Thanks
Oskar
Benchmarks are available at
Benchmarks are available at http://realize.be/benchmarks-display-suite-module