Control modules based on page content/keywords/description

(20 votes, average 3.85 out of 5)

Using this awesome recipe you can control which modules appear on the page by placing keywords in the content of the Joomla article, the Meta Keywords field, or the Meta Description. You just set up the recipe with a list of keywords to detect, the module IDs to be displayed when those keywords appear, and where MetaMod should look for them (content, description or keywords). A variation of this recipe allows you to enter the module IDs directly into the Meta Keywords.

It was first created for someone with a website of historical photos — each article contained a photo, and many articles contained a list of names of people in the photo. For a number of the main historical figures, the designer had also created modules with their photo and links to more information about that person. The designer wanted the corresponding module to show up on the page any time that person was mentioned in the main article.

You might also want to use it in a situation where each article relates to a particular manufacturer/company/person, and you want a module about that manufacturer to show up next to the relevant articles.

One of the great things about the recipe is that it allows you to control which module appears next to an article by editing the article itself, rather than having to edit the modules (or MetaMod).

This is a pretty long recipe... but just copy and paste the whole thing into the PHP box of your MetaMod, then customise the "$source", "$case_sensitive", "$allow_as_substrings", "$max_num_modules" and "$names" (most important!) and you’re good to go.

Note: the recipe does not search the entire content of the page, only the main “component”, e.g. article, contact, web link etc. Therefore it cannot detect the content of other modules on the page.

// To set it up, just edit the "$source", "$case_sensitive",
// "$allow_as_substrings", "$names" and "$max_num_modules"
// variables below. The "$names" array contains the
// words/phrases that you want to search for, and the module
// ID or module position to use when any one of them is found.
 
$source = "content"; /* either "keywords", "description" or "content" */
 
$case_sensitive = false; /* set to true or false */
 
/* if you set $allow_as_substrings to true, then the search
 * will pick up "billybob thornton" even if you are trying
 * to search for "bob thornton". Set it to false to make
 * sure that there's an least 1 non-alphabet character on
 * each end of the string you are searching for.
 */
$allow_as_substrings = false; /* set to true or false */
 
$max_num_modules = 3; /* it will just give you the first 3 it finds */
 
/* In this array, the first part is the text to search for, and
 * the last part is the module id to include if that text is
 * found. Add as many as you like.
 * Remember every line has to end in a comma EXCEPT the last one!
 */
$names = array(
  "bob jones" => 29,
  "jim baker" => 22,
  "john major" => 27,
  "tony blair" => 56,
  "CREATIVE" => 19,
  "global action" => "right-metamod",
  "joomla" => 39,
  "CMS" => 33,
  "graphical user interface" => 40
);
 
/*****************************************/
/* you shouldn't need to edit below here */
/*****************************************/
$document = JFactory::getDocument();
 
switch ($source) {
  case "keywords":
  $content = $document->getMetaData('keywords');
  break;
 
  case "description":
  $content = $document->getMetaData('description');
  break;
 
  default:
  $buffer = $document->getBuffer();
  $content = $buffer['component'][''];
}
 
$names_escaped = array();
$names_case = array();
foreach ($names as $key=>$module) {
  if (!$case_sensitive) {
    $key = strtolower($key);
  }
  $names_escaped[preg_quote($key)] = $module;
  $names_case[$key] = $module;
}
$names = $names_escaped;
$names_only = str_replace('/','\/',implode("|", array_keys($names)));
 
$word_boundary = $allow_as_substrings ? '' : '\b';
 
$result = preg_match_all("/" . $word_boundary
  . "(" . $names_only . ")"
  . $word_boundary . "/"
  . ($case_sensitive ? '' : 'i'), $content, $all);
 
if ($result > 0) {
  $modules = array();
 
  foreach ($all[0] as $matched_name) {
    $module = $names_case[$case_sensitive ?
      $matched_name : strtolower($matched_name)];
    if ( !in_array( $module, $modules) ) {
      $modules[] = $module;
    }
  }
  if ( count($modules) > $max_num_modules ) {
    return array_slice( $modules, 0, $max_num_modules );
  }
  return $modules;
}

Variation:

This shortened version allows you to specify the module id by making keywords like LINK_101 in your Meta Keywords. The recipe will pick up these keywords, extract the module ID, then include that module.

e.g. in your Meta Keywords for an article you could use this: travel,flights,buy online,LINK_102,LINK_right and the recipe would include module 102 and the modules from the "right" module position on this page.

$document = JFactory::getDocument();
$content = $document->getMetaData('keywords');
$result = preg_match_all('#LINK_([0-9a-z_\-]+)(?:[^0-9a-z_\-]|$)#', $content, $matches);
if ($result > 0) {
  return $matches[1];
}