How Bean (Lucas) Discovered Bean (module)

-->|        YOU (beanluc) have joined #drupal-support

ChanServ    [#drupal-support] For #drupal-support logs please see http://druplicon.info. For a pointer to today's log, ask: Druplicon: log pointer?

beanluc     Hi folks, I have sort of (what seems to me) a novel requirement [...] In short: it seems I need a block which is a fieldable entity

beanluc     Any ideas? Thanks!

timplunkett bean?

Druplicon   bean is http://drupal.org/project/bean Beans are Block Entities. Fieldable, pluggable, block entities.

timplunkett beanluc: ^

beanluc     ummm

beanluc     too good to be true?

beanluc     I mean

beanluc     it's *really* called "bean"? heh

timplunkett beanluc: it was written and named just for you

beanluc     no doubt

beanluc     go bean, its your birthday... go bean... etc

I thought it was comical. Full explanation is:

The above happened when I had sort of (what seemed to me) a novel requirement and I wanted some thoughts/brainstorms about how to implement it. I needed a block which had a nodereference. Idea was: Content-manager person would edit the block, put new content in it, and when the block displayed it would be linked to the nodereferenced node. Block content would be like standard block (HTML), however, I also wanted to include an image-attach *field* on this block. So, it seemed I needed a block which is a fieldable entity.

I knew I could use a node and one of the nodes-in-blocks modules to solve this problem functionally, but, I wanted the management of this block’s content to be done from the regular old Blocks page.

Problem solved! timplunket++ as we say.

Omega’s out-of-the-box job – it isn’t about Design.

Omega is the new Drupal 7 theming framework from Jake Strawn, Development Geeks and Sebastian Siemssen. They also created the Delta module to extend the power of Omega’s theme layout customizations to the Context module. Between Omega and Delta, they represent something like a year and a half’s development.

Many people already appreciate what Omega’s benefits are, but, I feel like I’ve been seeing skepticism of Omega here and there. I hope to show why Omega is totally kick-awesome.

Well, it seems to me that I commonly hear of people who install Omega and maybe Delta too, and discard and disregard them after a short spin. My guess is that they don’t see a slick, ready-to-go site-design, when they turn Omega on and look around their site. Omega alone, and the Omega sub-themes which come with it, are pretty blank, style-less, and image-less and color-less.

It’s like Zen in that way – you still will have to sub-theme it to get the look you want. Omega is not supposed to save you from doing regular old global-layout/design/style/images theming.

That’s not what Omega’s for. Omega has two primary jobs – the most useful of which is letting people who already can do that job, easily do the jobs of customizing layout without writing many additional TPL files, and Delta is for allowing even further per-Context customizations, again, without writing hyper-complex *_template_*process() and TPL logic.

Omega’s other job is providing a responsive design foundation in HTML5. While there are plenty of responsive themes already available, and HTML5 ones too, Omega offers this

I predict that, like what has happened with Zen, Fusion, 960, and other popular base-theme contributions, there will begin to become available a whole mess of Omega sub-themes with great, complete designs baked in. Omega alone (and its subthemes) is like Stark with killer features. Same with Zen, Fusion, etc. Zen and Fusion have had years to behefit from additional contributors of subthemes. Omega is just getting started.

Comparing Panels, DisplaySuite, PageManager, and Context

There is a persistent perception that Panels and DisplaySuite are two alternatives for solving one kind of layout-control problem.

Panels and DisplaySuite don’t really overlap that much, in my opinion. They really do two different jobs.

On the other hand, very frequently overlooked is the module PageManager, which is very closely associated with Panels, does overlap with DisplaySuite – and it overlaps with Context too.

The reason I mention it is that if you have Panels, you have PageManager already. It comes with the CTools suite. They each supplement the other. For that matter, DisplaySuite itself depends on CTools, so, again, it’s something you already have, which you might elect to ingore in favor of DisplaySuite! This is not a supplementary relationship.

What do I mean by “two different jobs”?
Panels is for getting multiple kinds of content into one page, and potentially for defining a whole page worth of layout (if you elect to have Panels “erase” certain Block regions). DisplaySuite is for customizing how one main-content element is laid out in a page.

Panels lets you bring many elements into your main-content area – you can have one or many nodes, one or more Views, one or more Blocks, Forms, or really any other bit of Drupal output – all inside a custom layout in the main content area. Panels also lets you expand your main-content area to fill the page (eliminating other block regions). Finally, (Mini) Panels lets you do the same thing just described, in any block region instead of in a Main Content region.

DisplaySuite lets you re-define how a Node, a View, a Form, or other single piece of main-content element is displayed. Now, this can be integrated with Panels, but it doesn’t do the same job as Panels – at least, not without a pretty good amount of contrivance.

With this in mind, here’s how Panels and DisplaySuite can work together: Your Panel may have a region with a node in it, and other regions with (let’s say) a View, a Block, and a Reference-driven User Profile. The job of Panels is to pull all those into the content-area. Now, if you had used DisplaySuite to customize how a Node is laid out, how a View is laid out, and how a user Profile is laid out, these customizations will be available inside the Panels panes. That’s DisplaySuite’s job.

What does PageManager do for layout which is unique?
The “normal” usage of Panels is to define a path which will use Panels for presenting an aggregation of different Drupal elements, and lay them out according to your wishes. PageManager lets you “take over” an existing path, and turn it into a Panels page, or even something else entirely. While PageManager has all the functionality of DisplaySuite, this feature (in addition to the third one, described below) is the real reason it’s named PageManager.

What does PageManager do for layout which is the same?
While DisplaySuite lets you create “view modes”, PageManager does the same exact thing but calls them “variants”.

What about the “third job”?
Interestingly, PageManager will handle both of the above jobs. PageManager will additionally do the job which Context does! And, wouldn’t you know it, once again, it’s a situation of electing to ignore an available solution in favor of installing something additional. Yes, Context also is a CTools-dependent module. My opinion is that the reason DisplaySuite and Context were developed is that they are somewhat easier for newbies to use than PageManager is.

Wrap-up
PageManager and Panels have both been around for a long time. There still is nothing else which can do what Panels does, and there’s that one unique feature of PageManager which still can’t be done anything else. PageManager is an extremely powerful presentation solution which has not been “made redundant” by Context and DisplaySuite. Quite the contrary – I argue that it’s Context and DisplaySuite which are the redundant newcomers! Nevertheless, I do support their use by anybody who is learning to implement these features for the first time, or, who might simply prefer their interfaces – though, they are mostly quite similar. I predict that anybody who uses DisplaySuite once, and uses Contect once, will then be able to switch to PageManager with no trouble at all.

Deep nesting with Drupal’s format_xml_elements($array) function

I needed to build some XML, such that certain elements which were deeply nested required attributes on them.

Drupal comes with a format_xml_elements() function, which is fantastic for module developers who can’t know whether some end-user’s PHP environment will have XML extensions installed. In fact, Drupal’s built-in RSS formatter uses this function, and for just this reason.

So, I investigated the API page: http://api.drupal.org/api/drupal/includes–common.inc/function/format_xml_elements/7. Right away I saw how to use this function to format either an element like <tag>content</tag>, or an element like <tag attribute="value">content</tag>.

Well, nice. Keep in mind: My goal was formatting an XML tree with multiple levels of nested elements, some including attributes.

Those above two elements are done differently with the format_xml_elements() function.

I had elements like:
<mykey>myvalue</mykey>,
which I could format like
format_xml_elements(
array(
'mykey' => 'myvalue',
)
);

and I had elements like
<mytag myattr="someval" />,
which I could format like
format_xml_elements(
array(
'key' => 'mykey',
'value' => '',
'attributes' => array(
'myattr' => 'someval',
),
)
);

and I had one more element like
<thistag attr="valuehere">somecontent</thistag>
which I could format like
format_xml_elements(
array(
'key' => 'thistag',
'value' => 'somecontent',
'attributes' => array(
'attr' => 'valuehere',
),
)
);

Pretty quickly, I also tried multiple elements at once, like
<item>stuff</item>
<item>morestuff</item>
<item>yourstuff</item>
<anotheritem att="specialval">mystuff</anotheritem>
,
which I could format like
format_xml_elements(
array('item' => 'stuff'),
array('item' => 'mystuff'),
array('item' => 'yourstuff'),
array(
'key' => 'anotheritem',
'value' => 'mystuff',
'attributes' => array(
'att' => 'specialval',
),
)
);

OK, great. Learned that, fast. Formatting one element, or even a bunch of sibling elements all at once, turns out to be very straightforward.

Very quickly I updated my module from concatenating a whole mess of strings together to build XML, to using calls to format_xml_elements() on each item which I had previously been wrapping in ugly tag-string concatenations.

It still wasn’t very satisfying, though. This gave me a collection of elements, but I still had not nested them properly yet. Naturally I tried to just see if passing nested arrays in would yield an XML tree out.

To get
<element>
<subelement>nicevalue</subelement>
</element>

I tried
format_xml_elements(
array(
'element' => array(
'subelement' => 'nicevalue',
),
)
);

Great! Worked. format_xml_elements() clearly works recursively. In fact you can see that from the sourcecode of the function, linked above. No surprise.

However, when I tried creating child-elements with attributes, using the longer-format specification, I ran into big trouble.

To get
<mykey>
<mysubkey attrib="goodvalue" />
</mykey>

I tried
format_xml_elements(
array(
'mykey' => array(
'key' => 'mysubkey',
'value' => '',
'attributes' => array(
'attribs' => 'subvalue',
),
),
)
);

Well. That got all ‘splodey.

I wound up with a whole mess of un-intended elements – elements like <attributes />, <attribs />, <key />, <value />… and no attributes on any of these elements. OK, maybe recursion only can work with atrribute-free elements… I don’t know… seems plausible…

I really have to get child elements with attributes on them! And it didn’t work with one format_xml_elements() call with a big, deep array for an argument.

I found the two Drupal-core functions which call format_xml_elements(), to see how it was used in core itself. It’s used by two RSS-formatting functions, and, the element arrays passed into it are just not as deep or attribute-laden as mine, so, those real-life usage examples were no help.

So, what next? How about – manually passing output of one format_xml_elements() call into a new format_xml_elements() call? Do my own nesting without trying to let the function recurse on itself. A-like so:

To get
<mykey>
<mysubkey attrib="goodvalue" />
</mykey>

I tried
$subkey = format_xml_elements(
array(
'key' => 'mysubkey',
'value' => '',
'attributes' => array(
'attrib' => 'goodvalue',
),
)
);
return $format_xml_elements(
array(
'mykey' => $subkey,
)
);

That actually worked. However, the special characters present in the $subkey string wound up escaped, so I wound up with
<mykey>
&lt;mysubkey attrib="goodvalue" /&gt;
</mykey>
.
Not cool!

So I changed to
return htmlspecialchars_decode(
format_xml_elements(
array(
'mykey' => $subkey,
)
)
);

in order to un-escape this string back to normal XML.

Whoa. Whatever.

After all that, I wound up with a working module which included many calls to format_xml_elements() and many htmlspecialchars_decode() calls too. Ugly but working. Sigh.

This was just fine, until I got ready for a code-review on the part of drupal.org project application volunteer reviewers. I *really* wanted to polish up this module so that format_xml_elements() was only present once in my XML-builder function. Plus, I thought that the very presence of htmlspecialchars_decode() just stunk. I was convinced that I should discover whether format_xml_elements() could be used on a large array all at once, or not, once and for all. I would either learn to use it the right way, or, I would write my own recursive function to wrap around format_xml_elements() so that I could use one explicit function call on my big array, instead of repetitively spamming my module full of format_xml_element() and htmlspecialchars_decode() calls.

Over about four days, I asked about format_xml_elements() in #drupal-support and in #drupal-contribute on Freenode IRC. No bites at all. It was starting to become comical, seeming that nobody was familiar with this function. Well, someone joined #drupal-support today, announcing their arrival with “Hello Drupalers ! Anyone got a good problem for me?”

I mean, that’s just asking for it, isn’t it?

So I said, “Yes, I do”! At first, FrobinRobin agreed with me, that it appeared format_xml_elements() would turn multidimensional arrays into simple XML elements, recursively, but that it wouldn’t set attributes on elements deeper than the zero’th level, and that calling format_xml_elements() multiple times explicitly was the easiest way to manage an array like mine. Well, lucky for me, FrobinRobin took it as a personal challenge, to put more effort into investigating this (after all, they asked for a “good problem”, didn’t they?)

Anticlimax: FrobinRobin decided that explicit numeric array-keys were required, and that if this were done, format_xml_elements() could successfully fully recurse an array which described an XML result of any complexity, either or without attributes on arbitrary elements. With this in mind, they worked on making an array intended to produce the output from format_xml_elements() which I needed. After several tries, they arrived at one.

Now: FrobinRobin succeeded at building an array which would produce the right XML output. However, I removed the explicit numeric key assignments, because if you don’t specify a key, PHP automatically sets them to numeric index keys. In other words, setting the numeric keys explicitly was a useful way to highlight what was going on in the function (its sourcecode contains a check for a numeric key-index), but, it’s not necessary to be explicit about the numbers. In fact, setting them explicitly would make our array-building harder, especially in cases where the arrays are constructed programmatically and are very large. Who wants to write extra code for not only setting the numeric key-index, but, for keeping track of the current index position?

So, if spelling out the numeric key-index didn’t actually have to do with the success of the array-to-XML conversion, what did?

Basically the crux was that it’s necessary to read the documentation of this function extremely carefully, in order to understand that the “long form” element specification (the kind which includes attributes) requires more arrays than it actually seems!

Specifically, while
format_xml_elements(
array(
'mykey' => array(
'key' => 'mysubkey',
'value' => '', 'attributes' => array(
'attribs' => 'subvalue',
),
),
)
);

yielded
<mykey>
<key>mysubkey</key>
<value></value>
<attributes>
<attribs>subvalue</attribs>
</attributes>
</mykey>
,
it was necessary to put the subkey element inside one more array. So
format_xml_elements(
array(
'mykey' => array(
array(
'key' => 'mysubkey',
'value' => '',
'attributes' => array(
'attribs' => 'subvalue',
),
),
),
)
);
yields
<mykey>
<mysubkey attribs="subvalue" />
</mykey>
.
Just what the doctor ordered.

It’s not easy to tell from the documentation that when an element (array member) is the child (value) of another element (array member) (parent), the child itself must be an array which contains its own complete definition.

For a simple, attribute-free element, that means it can be described as a key/value pair, and that’s the end of it: array('nameofelement' => 'valueofelement'). But for an attribute-containing element, it’s described not as a key/value pair, but as an array with named keys for the element name, the element value, and the element attributes – which are the members of yet another associative array. So by definition it’s an array within an array, just to describe the element, with yet another array for all the attributes. A-like so:

array(
array(
'key' => 'nameofelement',
'value' => 'valueofelement',
'attributes' => array(
'nameofattribute' => 'valueofattribute',
),
),
)
.

It’s subtle, and, because you don’t construct this apparent doubly-nested array when passing an attribute-bearing element to format_xml_elements() directly, it’s very easy to forget, or overlook, or fail to grok, that you must do this when such element isn’t being directly passed to format_xml_elements() but is instead being assigned as the value of a parent element.

When setting a parent element which is already described by an array to contain such a child, the element is either “a key/value pair” or “an associative array” (see the function’s page on api.drupal.org). In other words, it’s easy to see that the key/value pair needs to be wrapped by an “array()”, but when the element isn’t a bare key/value pair but is itself already an array, it still must be wrapped in an “array()” just like the key/value pair of the simpler, attribute-free element.

Tabbed menus in Drupal

I found that the modules which were explicitly developed for the purpose of providing a tabbed interface would either require ongoing webmaster intervention, or, be almost completely unsuitable for navigating between normal node-view pages, or both. That’s why I make my tab menus the following way instead of using any of the “*tabs” modules.

I have used a regular menu, put it in a menu-block at the top of the content region, and just copied the CSS styles for “local-tasks” type tabbed pseudo-menus for this custom block.

The HTML markup is almost exactly the same, between a regular Menu and a local-tasks pseudo-menu, so, this really can be as simple as a style job. No special modules needed (other than Menu Block, which you’re probably using already and which is extremely simple compared to the modules mentioned above).

Menu block really is just as simple as putting all or part of a completely normal site-menu into a block region (“content” is a block region just like any other). On the other hand, I’ll leave it to you to decide whether to put all your content into one node (which the jQuery Tabs module will do, and only in D6 at that), or, whether to build a views display for every single node you want in the Quicktabs.

To see this solution in action, look at Products pages on www.genesyslab.com. Each product has a collection of related pages, accessible in the tabbed menu above the product node content. All of these nodes are in the MainMenu tree. The Menu Block module automatically presents the correct sub-menu for each product “landing-page” node and that node’s “children” (or siblings, it can be done that way too).

It takes a Drupal developer ten minutes to configure the Menu Block module correctly. It takes a CSS person less than half an hour to style the menu with the same rules from the built-in Drupal “local-tasks” tabs. And, once these are set up, training for content-authors is extremely minimal because it’s extremely intuitive and obvious to them how to just add the nodes to the menu tree in the right tree structure. The solution continues to “just work” with no further webmaster involvement.

*UPDATE* The www.genesyslab.com website has been migrated off Drupal into ColdFusion since this was written. (Don’t ask me.) However, the illustration is still valid because the site still looks and behaves exactly the way it did back when I did the development on it 3 years ago. The important part is to look at the pages and understand how the “tabs” on the top of each Product page were implemented in Drupal.

500 error? That’s not what usually happens… AJAX vs. normal page

I recently chatted with Xano in #drupal-contribute about some tests which were failing – in an unusual way! At least, not in any way which Xano was expecting.

The failure was a simple “table not found” SQL error. In Drupal, normally this is not a fatal error, and will get printed to the page $messages where you can read it.

In Xano’s case, the “table not found” error was fatal, and yielded a 500 error. Whoa! Xano checked the HTTP message body to find that the error was about the missing table.

Well, Xano was looking at an AJAX request. This is a very different kind of Drupal request than a full page request. A missing table might be a fatal error in a specific function, but, that function’s breaking usually isn’t enough to wreck the whole page. But in an AJAX call, the response is much, much closer to the content-generating functions, and, it’s a lot more likely that a function’s breaking in an AJAX call is going to completely wreck the entire response output. When the webserver can’t send any content at all back to the browser due to a script error, status 500 is the result.

In Drupal, most of the time a PHP error like a missing table isn’t enough to completely break the HTTP response. Usually the page comes across with HTTP 200 status, and a PHP error visible in the $messages area. So Xano wanted to know why 500 was the result this time – it’s a completely different result for the same kind of error.

Drupal Prometheus? OOP?

Prometheus is a new Drupal 6 module which brings MVC and OOP paradigms to Drupal development.

Prometheus hasn’t yet been created as a project on drupal.org (the wait time for beginning the necessary code-review process is very long, and the process itself can be very long, especially for complex modules.)

So, who might want to use Prometheus?

You might, if you have some developers on-staff who:

  • already are on the hook for writing a lot of custom code, whether functional or presentational,
    and who
  • already have expressed dis-satisfaction with the non-OOP, non-MVC nature of Drupal,
    and who
  • are willing to study a whole new module which appears designed to let you “do whatever you want” but requires you to program it yourself – in addition to simply studying the available, extremely-well-documented “Drupal way” of doing these things.

A comparison: the Prometheus module is not remotely comparable to any Drupal module which is a specific ready-solution including user-interfaces and documentation. I’m making this comparison to modules which are already complete solutions to a specific set of presentation problems (like Panels) or functional problems (like Views). Prometheus is more comparable to CTools, which is a library of tools to help you develop such solutions, and others, by yourself.

To extend the comparison, CTools makes Panels and Views possible. Prometheus might make an alternative to Panels or Views possible, but, the alternative remains to be written, documented, and taught to your users.

If there were a library of available features, written with Prometheus, which site-builders or developers could use, Prometheus would be a far more attractive option at this stage. As it is, it only provides an alternative way to do your own Drupal development. I have no idea how extensive the coverage of the full Drupal API is, using Prometheus.

Compare Panels and DisplaySuite

Many webmasters use Panels for customizing the node part of a node-layout page, and nothing else. For this use-case, I feel that DisplaySuite is far, far better because:
* it’s easier,
* it doesn’t change the nature of the content (it’s still just a plain-old node),
and
* it still has all the advantages mentioned above:
* * it lets you divide content into sections – without views-wrangling required,
* * it’s end-user accessible without webdeveloper required.
* It has the additional advantage of controlling the layout of nodes in other contexts besides just a node-display page: You can customize how Teasers appear, and you can create other custom “View Modes” for when the node is shown in context of (for exmaple) blocks, views, taxonomy listings, user content listings, or other contexts you can think of.

In the above use-case, what Panels “adds” to the equation is, functionally-speaking, nothing, and practically-speaking, an unnecessary layer of complexity (in my opinion) – this impacts both performance and (probably more important) your staff’s learning-curve. But if you have *additional* use-cases for Panels in your requirements, then, it might be worth re-using Panels even for the above simple cases.

So, what *does* Panels add?

* If you want blocks inside your content-area, Panels can wrap the content around the blocks (of course, there’s always the “insert block” module too – extremely lightweight).
* If you want to implement a View with a chain of relationships, not all of which are available as path arguments, Panels introduces a “handler” which can define at least one level of the chain (of course, there’s always the “insert view” lightweight module too, as well as the argument validator PHP option – even more lightweight).
* If you want a page to display a very specific collection of individual nodes, but not use Views to do so, put them in Panels (after all, there *is* the odd site here and there which doesn’t have Views installed, and installing Views just for this one task, if Panels is already present and could be re-used, might not be what some webmaster wants to do).

© Bean Lucas