RelatedContent enables site maintainers to easily select on a per-node basis what nodes should be displayed along with it. Nodes that are available for selection are provided by one or more views, provided by the Views module. How the nodes are displayed is configurable, as well as themeable. The module also provide a small but powerful API for advanced users.

Some examples of what can be accomplished with the module:

  • The module can be used to embed into a node's body the teasers or bodies of nodes with related content.
  • In conjunction with the Simplenews module, RelatedContent can be used to compile newsletters out of already existing nodes.
  • Using its API, the module can made to display a block with content related to the node currently being viewed.

RelatedContent was developed by Thomas Barregren. The author can be contacted for paid customizations of this module as well as Drupal consulting, installation and development. The development of this module has been sponsored by

Requirements

To install RelatedContent you need:

Installation

Install RelatedContent as follows:

  1. Download, install and configure the Views module, following the instructions for that module.
  2. Download the latest stable version of RelatedContent from its project page.
  3. Unpack the downloaded file into sites/all/modules or the modules directory of your site.
  4. Go to Administer » Site building » Modules and enable the module.

Configuration

RelatedContent is enabled and configured for each content type individually as described below:

  1. If not already existing, go to Administer » Site building » Views and add at least one view that can be used to provide RelatedContent with nodes to select from.
  2. Go to Administer » Content management » Content types, and click on the content type for which you want to enable RelatedContent.
  3. Click on the RelatedContent configuration link to expand the settings.
  4. Tick the checkbox named Enable to allow related content of nodes for this particular content type.
  5. In the Views table, tick the views to be used as source of nodes to select from. (See below for more information.)
  6. Click on the Settings link above the table to review the settings. (See below for more information.)
  7. Click on the Save content type to save the settings.

The Views table

The table named Views lists all available views from which related content can be selected, as well as any deleted view for which nodes has been selected. The columns are described below:

  • A checkbox which is ticked for a view that is used by nodes of this content type.
  • View is the name of the view as seen in the Administer views page. If the view has been deleted, a generic name referring to the view's identity number is used instead.
  • Description is the description of the view as seen in the Administer views page. If the view has been deleted, a generic description is used instead.
  • Display name, if not empty, is the name displayed by RelatedContent instead of the name used by the Views module.
  • Weight is the weight by which the views are ordered by the RelatedContent module.

The settings

The settings are described below:

  • From the drop-down list named Length of node table, choose the number of nodes to be displayed on each page of the table with nodes to select from.
  • Among the radio buttons named Related content in teasers, choose whether the related content of a node should be included or excluded in the teaser of that node.
  • Among the radio buttons named Where to display, choose if the where the related content should be outputted.
  • Among the radio buttons named What to display, choose whether the related content should be displayed as teasers or bodies of the selected nodes.
  • Among the radio buttons named How to display, choose how the related content should be grouped.

Usage

When viewing a node of a content type with RelatedContent enabled, there is a tab named RelatedContent next to the usual View and Edit tabs. When you click on it, secondary tabs are displayed underneath it. You can also go directly to node/<nid>/relatedcontent where <nid> is the identity number of the node.

The secondary tabs are named after the views enabled for the node's content type. In addition there is also a secondary tab called "Overview", which is selected by default. Each tab has a table of nodes. The table on the Overview tab shows all selected nodes grouped by the view from which they were picked. That includes both nodes that are available from the view and those which are not available any more. The tables on the views' tabs show all nodes that can be selected from that view. That includes both nodes that are selected and those which are not. If there are more nodes than the table is configured to view, there is a Next-button on all pages except that last, and a Previous-button on all pages except the first. Use these buttons to navigate through the table's pages.

Each row in the table corresponds to a node. The node is presented with its title, content type, creation time and author. In front of each title is a checkbox. A node is related content if and only if it has a tick in the checkbox. To add nodes to the related content, tick them, and click on the Update button. To remove nodes from the related content, remove the tick, and click on the Update button.

Theming

The default theming of nodes with related content is appended at the beginning or end, depending on the settings, with a sequence of following HTML-blocks:

  <div class="relatedcontent-nodes $group">
    <h3>$group</h3>
    $contents
  </div>

where $group is, depending on the settings, the name of the content type or the author or 'all'; and $contents is, depending on the settings, the teasers or bodies of the related content after theming with node_view(). The <h3>-line is excluded if grouping is disabled.

Template file

The easiest way to change this theming, is to copy relatedcontent.tpl.php from the folder with the RelatedContent module, e.g. site/all/modules/relatedcontent, to the folder containing the theme's page.tpl.php and edit it as needed. This works for all themes based on the built-in PHPTemplate theme engine.

For advanced themers, and themes not based on the PHPTemplate theme engine, it is possible to override the themeable function theme_relatedcontent() discussed below.

Themeable functions

The RelatedContent module provides following themeable functions:

theme_relatedcontent($output, $grouped = null, $node_type = null, $teaser = null, $page = null)

where:

  • $output is an associative array, whose keys are the names by which the output should be grouped, i.e. the names of content types, the names of the authors, the names of the views from which the node were selected, or 'all' if grouping is disabled. Each output buffer is an ordinary array with already themed nodes which should be outputted in index order as they are.
  • $grouped is the string 'type' if the related content should be grouped by their content types, the string 'author' if it should be grouped by their authors, the string 'view' if it should be grouped by their authors, and false if it should not be grouped at all.
  • $node_type is the content type of the node whose related content is themed.
  • $teaser is true or false depending on whether the node, whose related content is themed, will be displayed as a teaser or in full, respectively.
  • $page is true or false depending on whether the node, whose related content is themed, will be displayed as a page itself or embedded into another page, respectively.

The default implementation returns the default theming described in the previous subsection.

theme_relatedcontent_form($form)

where:

  • $form['intro'] is a introductory text.
  • $form['nodes'][<nid>] is the form element for a checkbox.
  • $form['title'][<nid>] is the form element for a title.
  • $form['name'][<nid>] is the form element for a content type.
  • $form['created'][<nid>] is the form element for a create time.
  • $form['username'][<nid>] is the form element for an author.

with <nid> being the identity number (nid) of the node in question. The default implementation returns the themed table used to select nodes whose teasers are going to be shown.

theme_relatedcontent_form_alter_node_type_views($form)

where:

  • $form['enabled'][<vid>] is the form element for a checkbox.
  • $form['name'][<vid>] is the form element for a name.
  • $form['description'][<vid>] is the form element for a description.
  • $form['title'][<vid>] is the form element for a display name.
  • $form['weight'][<vid>] is the form element for a weight.

with <vid> being the identity number (vid) of the view in question. The default implementation returns the themed table used to select views to be used with a content type.

Application Programming Interface (API)

RelatedContent provides a very simple API that can be used by themes and other modules, or within nodes or blocks with the PHP input filter, to get a node's related content and do something clever with it. The API consists of the following functions:

relatedcontent_get_nodes(&$node)

where:

  • $node is the node object, as loaded through node_load(), for which the related content is requested.

Returns an array of id numbers (nid) of nodes that are related to $node. The array is sorted as described in this documentation.

relatedcontent_set_nodes(&$node, $nodes)

where:

  • $node is the node object, as loaded through node_load(), for which the related content is to be set.
  • $nodes is an array of id numbers (nid) of nodes that are related to $node. The array should be sorted as described in this documentation.

Sets the id numbers (nid) of nodes that are related to $node.

relatedcontent($node, $output_grouped = false, $content_function = '', $content_function_args = array())

where:

  • $node is either a node id number (nid) or a node object, as loaded by node_load().
  • $output_grouped is the name of the node field by which the content should be grouped, e.g. 'type', 'name', 'uid' and 'status', or false if the output should not be grouped. If omitted or empty, grouping s not performed.
  • $content_function is a callback function that transforms a node object, passed in as its first argument, to the desired representation, e.g. the teaser or body. If omitted or empty, the default transform is just returning the node object itself.
  • $content_function_args is an optional array with values that are passed in as argument 2, 3, and so forth, when calling the callback function $content_function.

Returns an array whose keys are the names by which the output should be grouped, i.e. names of content types or authors, or 'all', depending on $output_grouped, and whose values are arrays with the return values of calling $content_function for the nodes with related content.

The function can for instance be used to display the related content in a bock instead of within the node. Just add a block, enable the PHP input format, and paste following code snippet in the text area:

  
    if (arg(0) == 'node' && is_numeric($nid = arg(1))) {
      $output = relatedcontent($nid, false, 'node_view', array(true));
      echo theme_relatedcontent($output);
    }
  

The following code snippet is a more elaborated example of how the API can be used. Pasted into block with the PHP input filter enabled, it provides a list with links to the related content of the current viewed node.

if (arg(0) == 'node' && is_numeric($nid = arg(1))) {
  $host_node = node_load($nid);
  $grouped = relatedcontent_variable_output_grouped($host_node->type);
  if($groups = relatedcontent($host_node, $grouped)) {
    foreach($groups as $group => $nodes) {
      if ($nodes) {
        $links = array();
        foreach($nodes as $node) {
          $links[] = l($node->title, "node/$node->nid");
        }
        $title = relatedcontent_group_title($group, $grouped, $host_node->type);
        print theme('item_list', $links, $title);
      }
    }
  }
}

More examples can be find in the source comments of the function.

Background

It is common for websites to present collections of related content, such as teasers of promoted content, sidebars with facts and news issues. There are many great solutions for doing this in Drupal: taxonomy, views and queues, to mention a few. In spite of these possibilities, there is still need for an alternative way of compiling nodes to be shown.

In this section, a quick review of some possibilities are given, and the need for an alternative is explained in the context of the actual use case that motivated the development of this module in the first place.

Alternatives

Special pages with teasers are provided out of the box for nodes that are categorized by the core Taxonomy module. If your needs are more elaborative, you can compile a such page (or block) yourself with the wonderful Views module. In both cases, you cannot directly select which teasers to show. Instead you are reduced to setup conditions for the viewed teasers to meet.

To directly select teasers to show, you can resort to the Node Queue module. With the Node Queue module, you can create a "queue", which is a named set of references to nodes. You add references by visiting the nodes one by one or en masse by using the Node Queue Builder module. Once created, you can embed a PHP snippet into a node or a block to view teasers of the nodes referenced by a particular queue.

Although powerful, the Views and Node Queues are not always suitable. To understand why, consider the cause for writing the RelatedContent module in the first place:

Use case

The Simplenews module can be used to provide newsletters for visitors to subscribe to. The module provides a particular content type called Newsletter issue. When editing a newsletter issue, the editor has a single text-area for the content. It is suitable for self-contained newsletters. However, it cannot easily be used to accomplish newsletters made up of an introductory text followed by teasers to articles already published on the website. RelatedContent was originally developed to extend the Newsletter issue content type (and other content types) with this possibility.

So why are Views and Node Queues less appropriate for this particular purpose? There are at least two reasons: (i) it would be cumbersome, and (ii) the content would not be fixed.

Let us take a closer look on the Views module to see why it is so. This module can be configured to provide a single page (or rather a single URL) with teasers or full bodies of nodes that pass through its filter. A naive approach would be to create a view for each newsletter. But that is of course not a tenable solution in the long run.

A more sophisticated solution would be to build a single view that uses arguments to control which nodes are passed through, and embed PHP-code to output its content within the newsletters. The programming necessary can be reduced to just a function call by using the Component module, or completely avoided by using the Viewfield module or the Insert view module. But nevertheless the argument handling makes this a very cumbersome solution.

Another problem with the Views solution is that there will be a coupling between the content embedded into the newsletter and the view itself. Depending on the filter criteria, the view might return different sets of nodes over the time. That is of course not acceptable for a newsletter. It should be possible to craft the filter and argument handling such that the returned nodes are consistent over time, but that would be a very brittle solution.

Let us now take a closer look at the Node Queue module. This module requires PHP-code to be embedded. Hence, it too is cumbersome to use for non-programmers. And worse, giving all editors permission to use the necessary PHP code input format would pose a severe security risk.

The very idea of Node Queue is to provide a coupling between the output and the queue itself. Thus, any changes in a queue is propagated to wherever the queue is used. For a newsletter to not change on the web after it has been published, the queue must not be altered. The implication is that it is necessary to setup a unique queue for each issue, which of course is not feasible in the long run.

These problems are overcome by using the RelatedContent module.

Rationale

The very idea of the RelatedContent module is to provide means to

  • associating an individual node with other nodes, and
  • keeping that association independent of any changes in the source from which the nodes were selected in the first place.

In this section, some of the technical considerations are examined in some detail. RelatedContent is very easy to use, so you do not have to read this section to make full use of the module. It is merely included for those who are interested in why something work as they do.

Loading and viewing nodes

For each node of a content type for which the module is enabled, RelatedContent keeps an ordered list of nodes with related content. When Drupal loads a such node, the RelatedContent module hooks into the loading and add the list to the node. That list can then be used by other modules or themes to accomplish things. The RelatedContent module itself use it, during rendering of the node, to inject the listed nodes' teasers or bodies at the beginning or the end of the node's body.

Selecting related content

The RelatedContent module provides a user interface for selecting nodes with related content. To provide for different needs, RelatedContent use configurable views of the Views module as its source of nodes to select from.

A user with permission to update a node, will see a RelatedContent tab next to the the View and Edit tabs. When clicked on, a secondary tab called Overview is unveiled along with secondary tabs for each view that are selected as source of nodes. By default, the overview tab is selected.

All tabs display a table of nodes. In the overview tab, the table contains all nodes that are already on the list. That includes both nodes that are available from the view and those which are not available any more. In a view tab, the table contains all nodes that can be selected from that view. That includes both nodes that are selected and those which are not.

Each row in the table corresponds to a node. The node is presented with its title, content type, creation time and author. In front of each title is a checkbox. A node is related content if and only if it has a tick in the checkbox. To add nodes to the related content, tick them, and click on the Update button. To remove nodes from the related content, remove the tick, and click on the Update button.

Sorting and grouping

One of the benefits of using a view as the source of nodes to select from, is that views can be configured to sort the nodes to suit your demands. The RelatedContent module therefore keeps the nodes in the same order on its lists as they had in the view last time the Update button in the view's tab was clicked.

When a node with a list of related content is to be viewed, the nodes on the list are optionally partitioned into groups before themed. The nodes can be grouped by its content type, author, view they were picked from or not at all. The nodes within a group keep their order among themselves.

When grouping by view, the groups are ordered in accordance with views' weight assigned in the RelatedContent configuration on the content type form. For grouping by content type or author, the order of the groups are undefined. You can however sort them yourself by overriding the themeable function theme_relatedcontent().

Decoupling

The view is only used to provide a filtered and sorted collection of nodes from which the user can pick nodes. It is not used to further filter or sort the selected nodes. The selected nodes are decoupled from the source from which they were selected. As a consequence, the nodes' order or state of being selected remain unaffected of changes in the view from which they were picked. It is important to remember this.

Caveat

A node in the table on a view tab, might not be available later, due to the view's filter conditions or changes in the view definition, or even because the view has been replaced or turned off. If a such node has previously been included on the list of nodes with related content, it will turn up in the table on the overview tab, but not in the table on the view tab. That might first look a little strange, but is in fact just how we want it to work. A change in the view should not have an effect on which nodes are displayed along with the node once it has been stored. But these orphans cause some trouble is some very special situations. This is best exemplified:

Assume your source of nodes is a view that lists nodes created the last ten days in alphabetic order. Suppose you select some nodes from this view. They will be displayed in the same order as they had in the view when they were picked, that is alphabetic. Ten days later you select some more nodes from the same view. Since the nodes selected last time has lapse from the view, they will not be included when the newly selected nodes are ordered by the view. As a consequence, the former and the latter nodes are sorted alphabetic within respective group, but not as a whole. Thus, the related content is not more alphabetic ordered.

This is an effect of the desirable decoupling of the nodes from the view. Since this happens only if the previously selected nodes are not any more in the view, it is easily avoided by using a view that includes all nodes for as long as it is plausible to expect more nodes to be added. In practice that should not be a problem. But if you find yourself constantly updating the related content of a node, and has trouble with orphans, you should probably consider the Node Queue module instead.

Plans for the future

The RelatedContent module will be further developed. Following issues are known and will be dealt with in future versions of the module:

  • The order of the nodes cannot be altered manually. In a future it will be possible to manually changing the sort order by drag'n'drop the nodes in the overview table and/or changing an ordinal number presented next to the nodes in the overview table.
  • An idea for the future is to make it possible to show the related content in a block instead of within the node.
  • Another idea for the future is to provide an input filter, e.g. [RELATED CONTENT GOES HERE], which can be used to output the related content within the body instead of only at the beginning or end as today.

Comments

pdeclarens’s picture

Hello,

Your module is just great... thanks

Do you know if a tutorial has been made to use it with simplenews?

thanks