In the introduction to the guide on filtering content by articles, categories and components, I mentioned that to selectively bind the latest comments module to particular pages, it is required to create a certain number of copies of this module. On a site that is quite branched in structure and subject matter, and especially if the site is multilingual, this number can reach dozens. Each copy has its own settings specified in the Module Manager. If you need to change any parameters in all or even a few copies of the module, you will have to work painstakingly and tediously in the admin section opening a page of each copy and making manual changes. You can, of course, combine comments on several related topics into a single module, But is it not better to find a flexible solution that allows you to accurately filter out comments on each topic or even a subtopic, while at the same time reducing the number of copies of the module to a minimum, or even to a single one? It seems to me that I found such a solution, and this solution is based on the knowledge of the structure of application pages internal links generated by the Joomla engine.

As you know, Joomla modules are linked to menu items. In the case of mod_jcomments_latest, a binding to components is also introduced. There is also additional filtering by categories, but exclusively for the case when ONLY com_content is selected in the list of components. Accordingly, you need a separate copy of the module for each category of the basic Joomla component, as well as for each additional application. Within the copy, you can combine the com_content categories or a number of components. It is not possible to merge, for example, comments to categories and comments to applications other than com_content, into a single block. My method allows to distinguish between comments to categories and individual articles of com_content and at the same time - comments in other components and their structural elements; and all that - within the single copy of the module. Let me explain the principle of such a flexible filtration in detail and with examples.

Customizing the code of mod_jcomments_latest

Changes are made in the helper class of this module, which is standardly located in the file [site_root]\modules\mod_jcomments_latest\helper.php. We are considering build 3.0.4 - the newest at the time of writing.

All customizations are made in the code of the main and very first method of the class modJCommentsLatestHelper. This method's name is getList. You can download an archive containing two versions of the helper file - the original version and the customized one - and compare their contents using some suitable tool (eg, WinMerge):

Original & Customised helper.php of mod_jcomments_latest
Date 2016-06-23 System  Windows Filesize 7.5 KB Download 139 Download

Here in the code of the class, located just below in Expandable/Collapsible View, I will indicate only the modified code snippets. For each of these two snippets, there are comments containing the line numbers corresponding to the beginning and end of the replaced block of code in the original file. For clearness, there are also clickable screenshots of the comparison results in WinMerge with changes highlighting (on the right - the original code, on the left - the customized one).

Modified code of the getList function

public static function getList(&$params) {
/*
  cut 1
*/
 
//Customization #1 ==BEGIN== (lines 27 - 36 in original file)
  switch ($params->get('ordering', '')) {
    case 'vote':
      $orderBy = '(isgood-ispoor) DESC';
      break;
 
    case 'date':
    default:
      $orderBy = 'date DESC';
      break;
  }
//Customization #1 --END--
 
/*
  cut 2
*/
 
//Customization #2 ==BEGIN== (lines 100 - 134 in original file)
  $categories = '';
  $components = array();
  getCommentsFilter($categories, $components);
 
  $query = '';
  $union = false;
  for ($i = 0; $i <= 1; $i++) { //1st for com_content, 2nd for other components
    $joins = array();
    $where_i = $where; //resets $where_i for each iteration
    if ($i == 0 && !empty($categories)) { //1st iteration
      $joins[] = 'JOIN #__content AS cc ON cc.id = o.object_id';
      if ($categories != 'all')
        $joins[] = 'LEFT JOIN #__categories AS ct ON ct.id = cc.catid';
 
      $where_i[] = "(cc.publish_up = '0000-00-00 00:00:00' OR cc.publish_up <= '$now')";
      $where_i[] = "(cc.publish_down = '0000-00-00 00:00:00' OR cc.publish_down >= '$now')";
      $where_i[] = "(c.object_group = " . $db->Quote('com_content') . ($categories != 'all' ? " AND cc.catid IN (" . $categories . ")" : "") . ")";
    }
    elseif ($i == 1 && !empty($components)) { //2nd iteration
      $where_cmp = array();
      for ($j = 0, $nj = sizeof($components); $j < $nj; $j++)
        $where_cmp[] = '(c.object_group LIKE ' . $db->Quote($components[$j] . '%') . ')';
      $where_i[] = '(' . implode(' OR ', $where_cmp) . ')';
    }
    else
      continue;
 
    $union = $i == 1 && !empty($query); //2nd iteration, 1st query filtering by categories was built
    if ($union)
      $query = '(' . $query . ') UNION (';
 
    $query .= "SELECT c.id, c.userid, c.comment, c.title, c.name, c.username, c.email, c.date, c.object_id, c.object_group, '' as avatar" . ", o.title AS object_title, o.link AS object_link, o.access AS object_access, o.userid AS object_owner" . " FROM #__jcomments AS c" . " JOIN #__jcomments_objects AS o ON c.object_id = o.object_id AND c.object_group = o.object_group AND c.lang = o.lang" . (count($joins) ? ' ' . implode(' ', $joins) : '') . (count($where_i) ? ' WHERE ' . implode(' AND ', $where_i) : '');
  }
 
  $list = array();
  if (!empty($query)) {
    $query .= ($union ? ")" : "") . " ORDER BY " . $orderBy; //adds common ordering for both queries in union (if any) of just for one quey
    $db->setQuery($query, 0, $params->get('count'));
    $list = $db->loadObjectList();
 
    if (!is_array($list)) {
      $list = array();
    }
  }
//Customization #2 --END--
 
/*
  cut 3
*/
}

end faq

Modification 1:

Unified filtering of the mod_jcomments_latest module output - customization 1

As you can see, all occurrences of a table alias prefix for the ORDER BY sorting clause of sql-query are simply removed here. Why? - it will be clear later.

Modification 2 - global:

Unified filtering of the mod_jcomments_latest module output - customization 2

I will not detail the code line by line; you can figure it out with a little help of the comments within the code. I'll just explain the idea and its implementation.

In the code, a query to the comment tables is formed, which should output a list of comments according to the binding conditions. These conditions are implemented through the string variable $categories and the $components, array, filled with their values in the function getCommentsFilter, which is described below. In the original, the result filters alternatively either comments to the articles and categories of the com_content component, or to all pages of applications other than it. In my version, these two results can be combined into a single common one, and in addition - for ALL applications, including third-party ones, it becomes possible to separate individual pages of these components using a more selective filter.

For this, two iterations are implemented: the first one for com_content only, and the second - for other components. Results of both queries are then combined using the UNION operator, after which the cumulative result is sorted in the ORDER BY clause. And here the goal of the first customization becomes clear: if table aliases present, MySQL produces an error, since they are assigned only in the scope of each subquery and are not available in the scope of the resulting query.

The condition of no binding either by com_content or by other components is also handled. In this case, the corresponding iteration is skipped and the union is not performed.

Function getCommentsFilter

Everyone creates it in accordance with their specific binding conditions. The principles described in both previous articles of the series are used for this purpose. First, global variables corresponding to the parameters of the output page are set and initialized. In my case, there are 5 such variables:

  • $cf_option - component name (prefixed with com_);
  • $cf_view - appearance of a particular component page;
  • $cf_art_cat_id - article or category ID, depending on the current view (see here);
  • $cf_cat_id - identifier of a category which related to the currently displayed article (in the case of cf_view==article);
  • $cf_lang - language of the current webpage (for a multilingual site).

As described here, code of the function can be placed in one of the existing libraries, best of all - in the functions.php file of an active template. The code for initializing global variables is best placed in the template index file templates\[active_template]\index.php below the line where the library is attached. The start of the code in my index.php file looks like this:

defined('_JEXEC') or die;
 
require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . 'functions.php';
 
$app = JFactory::getApplication();
$jinput = $app->input;
 
global $cf_option, $cf_view, $cf_art_cat_id, $cf_cat_id, $cf_lang;
 
$cf_option   = $jinput->getCmd('option');
$cf_view   = $jinput->getCmd('view');
 
$temp   = explode(':', $jinput->getCmd('id'));
$cf_art_cat_id   = $temp[0] + 0;
 
$temp   = explode(':', $jinput->getCmd('catid'));
$cf_cat_id = $temp[0] + 0;
 
$cf_lang = substr(JFactory::getLanguage()->getTag(),0,2);

The definition of getCommentsFilter function is as follows:

function getCommentsFilter(&$categories, &$components)

The function arguments are the two above-mentioned variables: string $categories and array $components which are passed from the modified method getList of the module helper class by reference. In the function, they are filled with values: the first - a list of category identifiers, separated by a comma. These values are then passed back to the getList method and used as the filtering conditions in the WHERE clause of the corresponding sql-query.

Here is a code of the function getCommentsFilter used on this site at the time of this writing:

Code of the getCommentsFilter function

function getCommentsFilter(&$categories, &$components) {
  global $cf_option, $cf_view, $cf_art_cat_id, $cf_cat_id, $cf_lang;
 
  if ($cf_lang == 'ru') {  // Russian part of the site
    $hebro_cats = array(69, 71, 72, 73, 75, 76, 77, 83, 84, 85);
    $reg_cats = array(25, 26, 27, 48, 86);
    $tech_cats_str = '26,27';
 
    if ($cf_option == 'com_content') {
      if ($cf_view == 'article') {
        if (in_array($cf_art_cat_id, array(783, 307))) // Glavnaya, obratnaya-svyaz
          $categories = 'all';
        elseif (in_array($cf_cat_id, $reg_cats)) // Blog, chinim-kompy, stroim-sajty, trudoustrojstvo-zapiski-posvyashchennogo, sovety-doktora-aleny
          $categories = $cf_cat_id;
        elseif ($cf_cat_id == 28) // guitar
          $categories = 'guitar';
        elseif (in_array($cf_art_cat_id, array(614, 454, 603, 694)) || in_array($cf_cat_id, $hebro_cats)) // Hebroman
          $categories = 'hebroman';
        elseif (in_array($cf_art_cat_id, array(433, 434, 724, 957))) //video
          $categories = 'all';
      } elseif ($cf_view == 'category') {
        if (in_array($cf_art_cat_id, $reg_cats)) // Blog, chinim-kompy, stroim-sajty, trudoustrojstvo-zapiski-posvyashchennogo, sovety-doktora-aleny
          $categories = $cf_art_cat_id;
        elseif ($cf_art_cat_id == 28) // guitar
          $categories = 'guitar';
        elseif (in_array($cf_art_cat_id, $hebro_cats)) // Hebroman
          $categories = 'hebroman';
      }
    } elseif ($cf_option == 'com_muscol') // guitar application
      $categories = 'guitar';
    elseif ($cf_option == 'com_bt_portfolio') { // portfolio
      $categories = $tech_cats_str; // fixing-comps, building-sites
      $components[] = 'com_bt_portfolio';
    } elseif ($cf_option == 'com_joomgallery') // fotogalereya
      $components[] = 'com_joomgallery';
    elseif ($cf_option == 'com_jdownloads') {
      if (in_array($cf_cat_id, array(8, 9, 11))) // Hebroman
        $categories = 'hebroman';
      elseif ($cf_cat_id == 3) // Attachments
        $categories = $tech_cats_str; // chinim-kompy, stroim-sajty
      elseif ($cf_cat_id == 4) // Comp
        $categories = '26'; // chinim-kompy
      elseif ($cf_cat_id == 5) // Joomla
        $categories = '27'; // stroim-sajty
    } elseif ($cf_option == 'com_xmap') {
      if ($cf_art_cat_id == 1) // karta-sajta
        $categories = 'all';
      elseif ($cf_art_cat_id == 4) // karta-hebroman
        $categories = 'hebroman';
    }
 
    if ($categories == 'guitar') {
      $categories = '28';
      $components[] = 'com_muscol';
    } elseif ($categories == 'hebroman')
      $categories = implode(',', $hebro_cats);
  } elseif ($cf_lang == 'en') { // English part of the site
    $reg_cats = array(42, 43, 54);
    $tech_cats_str = '42,43';
 
    if ($cf_option == 'com_content') {
      if ($cf_view == 'article') {
        if (in_array($cf_art_cat_id, array(784, 306))) // home, feedback
          $categories = 'all';
        elseif (in_array($cf_cat_id, $reg_cats)) // fixing-comps, building-sites, employment-notes-of-the-initiate
          $categories = $cf_cat_id;
      } elseif ($cf_view == 'category') {
        if (in_array($cf_art_cat_id, $reg_cats)) // fixing-comps, building-sites, employment-notes-of-the-initiate
          $categories = $cf_art_cat_id;
      }
    } elseif ($cf_option == 'com_muscol') // guitar application
      $components[] = 'com_muscol';
    elseif ($cf_option == 'com_bt_portfolio') { // portfolio
      $categories = $tech_cats_str; // fixing-comps, building-sites
      $components[] = 'com_bt_portfolio';
    } elseif ($cf_option == 'com_joomgallery') // photogallery
      $components[] = 'com_joomgallery';
    elseif ($cf_option == 'com_jdownloads') {
      if ($cf_cat_id == 3) // Attachments
        $categories = $tech_cats_str; // fixing-comps, building-sites
      elseif ($cf_cat_id == 4) // Comp
        $categories = '42'; // fixing-comps
      elseif ($cf_cat_id == 5) // Joomla
        $categories = '43'; // building-sites
    } elseif ($cf_option == 'com_xmap') {
      if ($cf_art_cat_id == 2) // site-map
        $categories = 'all';
    }
  }
}

end faq

As can be seen from there, the first step is a binding by languages. For each language, the comments to articles and categories of the com_content component are filtered, and then - to pages of other components. Some of these components (in particular, com_jdownloads and com_xmap) also have categories, and the binding is carried out separately to each of them in accordance with their subjects. In the case of a multi-level (tree-type) structure of categories, the comments to child categories are merged to bind to the parent category. An example is the processing of categories of the com_jdownloads component. In the case of a guitar app, there is the combining of the comments to the pages of the app itself ( com_muscol) component) with the comments to the category 'Guitar' (id==28) of com_content - in both views: category blog as well as each category article.

For the occurrence when the latest comments to ALL pages of the component com_content are to be output, the function passes to the calling code the value 'all' for the variable $categories. In my case, for example - for the Home page, Feedback page, and also for all site maps. When there is no need to filter comments to pages of other components, the $components array remains empty.

Thus, in my case, all sections of the site for each language are processed:

  • Individual pages - Home, Feedback.
  • 8 sections in the Russian part and 3 in the English part of the site. Each section includes pages in the views of article and blog of one or more categories.
  • 5 other applications for each language:
    • Guitar application com_muscol - it's comments are combined with those to the category 'Guitar' of the com_content components;
    • The portfolio component com_bt_portfolio - comments to the pages of the application itself and to the pages of the two technical categories are merged;
    • Component for downloading files com_jdownloads - 4 categories in the Russian part and 3 in the English part are processed;
    • Site map com_xmap - 2 maps in Russian and 1 - in English are treated;
    • Image gallery com_joomgallery - the usual binding to all pages of the application is implemented.

As a result: prior to the customization, I used around 20 (twenty!) copies of the module mod_jcomments_latest. At the same time, there was no option to merge comments to the pages of the com_content component with those to other applications' pages. Now I can merge comments to ANY pages of ANY applications. At the moment I have only two copies of the module - for each of the site's languages. Both of them are assigned by default in the Admin panel - the real assignment of the comments to the site pages is implemented in the modified code.

What is the disadvantage? When you change the structure and binding conditions, you need to make changes to the module code. For me personally, this has not long been a problem, nor is it a problem to restore customizations when developers update their extensions. It is possible, of course, to improve the administrative part of the module by adding functionality for such a flexible binding, thereby obtaining filter conditions from the module parameters on the front end. But this is already going to be a whole project, not worth the trouble. But understanding the structure of internal Joomla links and utilizing of the acquired knowledge to increase the flexibility of binding code to selected pages of the site is very useful. This article, like the previous (and hopefully follow-up) articles of the series "Joomla Internal Links and examples of their usage", is designed to help you with this.

Add comment


By working with this site, you agree to our use of cookies necessary to keep the settings you select, as well as for the normal operation of Google services.
More Information OK