Reason #5 for Choosing TYPO3: Custom Content Elements with Extbase
One of our goals in writing 100 posts on why we continue to stick with TYPO3 over other content management systems is to reach a number of different audiences. Choosing a CMS often involves a number of stakeholders including folks from the Marketing group, IT, and the web developers and designers who will actually build the system. To connect with these different audiences, we're trying to write a variety of posts, some more technical, some less technical. So, fair warning: this post will be one of the technical ones.
Before getting too technical, however, I want to provide a little background. For those of you who don't know, ExtBase is TYPO3's Model-View-Controller framework that was introduced in TYPO3 4.3. In the old days (prior to about 2009), TYPO3 extensions were based on a class called piBase. Extensions that rely on piBase tend to be procedural and difficult to modify and maintain. Take any popular extension in the TER as an example; many of them will contain thousands of lines of code in a single pi1 class. ExtBase follows the lead of other popular MVC frameworks that are on the market now and forces extension developers to write much cleaner, more object-oriented code that is easier to scale and maintain. Before ExtBase came out, developers at Cast Iron Coding realized that piBase extensions were simply too difficult to write and there was too much infrastructure code that had to be written each time. We developed our own MVC framework in-house back in 2008, and we've used it on numerous projects since then. As ExtBase becomes more stable and more feature-complete, we're slowly phasing out our own MVC extension and replacing it with ExtBase.
The goal of this post is to describe and document how we currently use ExtBase (and other parts of TYPO3) to provide users with a customized, more intuitive backend interface for editing content. It's an approach that we've only recently started using, and I expect we'll be improving it and iterating over it on future projects. When you're done working through this post, you should be able to accomplish the following:
1. Take advantage of recent additions to TYPO3 that allow users to customize the new content wizard in order to provide content editors with a cleaner interface
2. Provide backend editors with content elements that are tailored to a specific client. We think of these elements as replacements for TemplaVoila Flexible Content Elements (FCEs), which we tend to avoid because (a) the flexform editing interface tends to be confusing and is not consistent with the rest of the TCEForm look and feel, which confuses users; (b) in our experience, maintaining FCEs is tedious and time consuming because of the need to remap whenever changes are made; and (c) implementing any kind of looping logic or conditional logic in an FCE is generally either not possible or is difficult because dealing with repeating elements in FCE is, put crudely, a PITA.
3. Come up with an approach that makes rolling out a new content element a relatively trivial task. We don't want to write a new plugin every time a client needs a content element that contains some programatic logic; that's too expensive and too burdensome.
I'm going to use a project we have in the works—a redesign of the Tides Foundation website—as an example. The site hasn't launched yet, so I'll be keeping screenshots of the frontend to a minimum, but you'll get the idea.
We accomplish the first goal, cleaning up the content wizard, by utilizing TSConfig options that have been available in TYPO3 for the last couple major releases (see Steffen Kamper's blog post on the subject for more information). As you no doubt know, without any customization, the TYPO3 new content wizard looks like this:
This interface needs improvement on a number of levels. Some of the labels, such as "Special Elements" don't convey much meaning. Moreover, the descriptions for each element can use some cleaning up. This can all be accomplished with TSConfig. When we're done, this interface will look like this:
We've accomplished a few things in this cleanup. For one, we've turned the long list of content elements into tabs, which we believe is easier for editors to use. Second, we've regrouped the content elements so that instead of "typical page content" and "special elements," which I view as a distinction that doesn't convey a lot of meaning, we now have "TYPO3 content elements," "Tides Content Elements," and "Advanced Content Elements." However you group elements, you should try to find a grouping that will make sense to your editors and be relatively intuitive. The third thing we've accomplished here is the removal of certain kinds of content elements from the wizard. For example, we don't really want our editors using the "Table" content element; for a variety of reasons, we feel they're going to be better served by inserting tables in the RTE. We've also inserted a couple of FCE elements in this list (Tabbed Content Container and Accordian Content Container). Finally, we've separated out "advanced" elements that most content editors won't need into their own tab, and we've added our custom content elements on the second tab.
How do we do it? Simple! This is all accomplished using the following TSConfig:
This TSConfig isn't as complicated as it looks. Let's walk through it quickly:
Line 1: We tell the content element wizard to use tabs instead of one long list
Line 3: This is a bit of a hack. My changes to the default wizard configuration are comprehensive enough where I want to simply unset the default conf and overwrite it entirely. However, to save time, I want to refer to the original configuration in places. To accomplish this, I copy the default wizard configuration to a tmp object and then unset the wizard configuration. As I recreate it, I can use this tmp.wizards typoscript object to reference the original configuration (see, for example, line 9 and line 10, where I'm pulling the default configuration back in).
Line 7: We begin configuring the groups. The three tabs in the screenshot above correspond to the three groups here: common, client, and plugins.
Line 11: The configuration for the "Tabbed Content Container" element begins here. Configuring an element in the wizard is pretty straight-forward. You include a title, description, and an icon. You also need to set the default field values on the tt_content record (lines 16, 17, and 18). All the wizard really does is trigger the creation of a new tt_content record with default values; those values are set here.
Line 35: The show property contains the items that we want to show in the wizard.
Lines 40-88: These lines contain configuration for the custom content elements that we'll be creating in the rest of this tutorial. Notice that they behave just as a regular TYPO3 content element; there is a unique key for the element that shows up in the "CType" field on the tt_content record, which tells TYPO3 what kind of content the record is and how to render it.
(Keep in mind that this code is not something you can just copy and paste into your TSConfig. This example contains custom content elements that you won't have setup; it's meant to serve as an example and an explanation on customizing the new content element wizard)
Ok, so we've met our first goal and provided the backend content editor with a more usable interface. On to the second goal of creating a nice custom content element. Let's start with the first custom content example from the screenshot above: "Main Content Area Callout Box." On the frontend of the site, this content element looks like this:
Now, I realize that there are a number of ways we could implement this content element. We could, for example, provide the user with classes in the RTE that could generate this. I don't see that as a good solution because it requires too much work of the content editor. They would have to remember to first create a text element, then they would have to remember how to structure the content in the RTE and which classes would need to be wrapped around which parts. That approach might work for a developer, but it's unrealistic to expect editors to manage that successfully. We could also make an FCE, of course, and that would probably work fine. The problem with FCEs is that they require a separate interface and additional fields in the tt_content record. Users have to know to first select FCE from the type field, then they have to set the data structure field before they see the correct flexform. In many cases, they need to then know to enter header and content in the flexform, which is on a different tab than what they're used to in the regular text element. It's not that people can't figure this out. However, they shouldn't have to! I want my editors to be able to use the same interfaces for the same tasks, and from an interface standpoint, FCEs contain an additional layer of interface clutter.
So, we want to give the user the most intuitive content editing interface that is possible with TYPO3. For this content element, I want them to use the header field for the title of the box and the RTE field for the content. I want them to be able to set the color of the box as well, since this site contains callouts that are blue, green, and copper. In other words, I want the interface to look like this:
Notice how clean this interface is. No flexform here to confuse the user. No extraneous tab or additional fields. The type field is being used for exactly what it's meant for, which is to signify what kind of content element we're looking at. So, how do we do it?
This is where things start to get technical. This content element is being rendered from a Fluid template and an ExtBase extension. In the case of this content element, it seems like overkill, but when you have a site with nine or ten custom content elements, some of which contain logic (eg, render a list of dam records or generate some navigation element), it starts to be clear why ExtBase/Fluid is a good approach. The other nice part about this approach is that most of the labor comes with setting up the first content element. After you've done that, the rest are easy.
Stay Tuned for PART 2 of this post, in which I get into the nitty-gritty of implementing custom content elements with ExtBase/Fluid.
- Author's email: zach@castironcoding.com


3 Comments
I've written most of it and plan on posting it either tomorrow or Wednesday. It's coming soon! :)
-Zach