Tuesday, 21 February 2017 16:18

RSFeedback Komento Plugin and Integration Tutorial

Written by

This article will go through a pratical example of adding Komento (v3.0.4) to a component, in this case RSFeedback (v1.5.13). I will cover the various aspects of the Koneto Plugin structure and then go into the actual integration and how all of these parts interact.

Useful Information and Resources

These are not easy to find unless you are looking for them but combined they really helped me make this article and my plugin.

  • Creating Own Integration Files - Advance - Komento Documentation - This is the primary document from Stackideas for making your own Komento plugin file for integration
  • Triggers - Advance - Komento Documentation - Due to the nature of Komento's multiple component support system, triggers in Komento comes in 2 form: Component Plugin and Joomla Plugin. Not 100% what I would use this for but the link to this in the primary document is broken
  • In the Komento (once installed)
    • components/com_komento/komento_plugins/abstract.php - This is the class that all other plugins extend and it includes all the functions (Basic and Advanced) with descriptions
    • components/com_komento/komento_plugins/com_sample.php - It is supposed to be a sample to help you make your own plugin but it is incomplete
    • components/com_komento/komento_plugins/com_content.php - Uses an advanced function to add extra configurability in the Komento Integrations Admin Tab for this plugin
    • components/com_komento/komento_plugins/{All the Others} - Have a look through the other plugins, they might help you.
  • QW_com_sample.php - This is my upgraded com_sample.php with all annotations present with some extra code to make things easier
  • administrator/components/com_komento/includes/komento.php - This holds the class KT (or Komento) and all of these functions (ie. getCount() and commentify() ) can be used if called correctely.

QW_com_sample.php

This is my upgraded com_sample.php and I highly recommend reading through it as it explains alot of things in context.

<?php
/**
* @package      Komento
* @copyright    Copyright (C) 2010 - 2016 Stack Ideas Sdn Bhd. All rights reserved.
* @copyright    Copyright (C) 2017        Jon Brown @ QuantumWarp.com
* @license      GNU/GPL, see LICENSE.php
* Komento is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*/

// No direct access
defined('_JEXEC') or die('Restricted access');

// Always load abstract class - This file also includes extra information about the functions here and the advanced functions not covered
//require_once( JPATH_ROOT . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'com_komento' . DIRECTORY_SEPARATOR . 'komento_plugins' . DIRECTORY_SEPARATOR .'abstract.php' );  
require_once( JPATH_ROOT . '/components/com_komento/komento_plugins/abstract.php' );

class KomentoComsample extends KomentoExtension
{
    
    /******************************************************
     *
     * START
     * [BASIC FUNCTIONS (METHODS)]
     * These functions are mandatory
     *
     ******************************************************/
     
    // This property (object) stores all the required properties by Komento
    public $_item;

    // This property (array) stores all the key mappings of the required item properties to map from Komento's default key to your component's custom key
    public $_map = array(
    
        // not needed with custom getContentId()
        'id'            => 'id_field',

        // not needed with custom getContentTitle()
        'title'         => 'title_field',

        // not needed with custom getContentHits()
        'hits'          => 'hits_field',

        // not needed with custom getAuthorId()
        'created_by'    => 'created_by_field',

        // not needed with custom getCategoryId()
        'catid'         => 'catid_field',

        // not needed with custom getContentPermalink()
        'permalink'     => 'permalink_field'

        );

    // Constructor - Add all required files for your component here and run its constructor
    public function __construct( $component )
    {
        // Load all required files by component
        // $this->addFile( your component's files );
        // $this->addFile(JPATH_ADMINISTRATOR . '/components/com_sample/config.php');

        // This must be left at the end of this constructor function
        parent::__construct( $component );
    }

    // This method should load the article's main properties based on article ID
    public function load( $cid )
    {
        static $instances = array();

        if( !isset( $instances[$cid] ) )
        {
            // populate $this->_item with:
            // id_field
            // title_field
            // hits_field
            // created_by_field
            // catid_field
            // permalink_field

            // Create a Database Object
            $db = KT::getDBO();
            
            // Create SQL query to load a single article
            $query = 'SELECT `id_field`, `title_field`, `hits_field`, `created_by_field`, `catid_field` FROM `#__ARTICLE_TABLE` WHERE `ARTICLE_ID` = ' . $db->quote( $cid );
            $db->setQuery( $query );

            // Run the single article query and if there are no objects to load call the onLoadArticleError event 
            if( !$this->_item = $db->loadObject() )
            {
                return $this->onLoadArticleError( $cid );
            }

            // Generate the permalink for this article
            $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field;

            // Call the prepareLink function and leave the rest to us
            // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite
            $this->_item->permalink_field = $this->prepareLink( $this->_item->permalink_field );

            $instances[$cid] = $this->_item;
        }

        $this->_item = $instances[$cid];

        return $this;
    }
    
    // This method should load all the article IDs filtered by category IDs
    public function getContentIds( $categories = '' )
    {
        // Create a Database Object
        $db = KT::getDBO();
        
        // Make sure the query is empty
        $query = '';

        
        // If no categories are supplied then load all article IDs
        if( empty( $categories ) )
        {
            $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` ORDER BY `id_field`';
        }
        
        // If categories are supplied then load all article IDs for articles belonging to those categories
        else
        {
            if( is_array( $categories ) )
            {
                $categories = implode( ',', $categories );
            }

            $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` WHERE `catid_field` IN (' . $categories . ') ORDER BY `id_field`';
        }
        
        // Run the query and return the results as an array
        $db->setQuery( $query );
        return $db->loadResultArray();
    }

    // This method should load all the category IDs of the component
    // Make sure you select 'Single Level' or 'Nested' categories
    public function getCategories()
    {
        // Create a Database Object
        $db = KT::getDBO();
        
        // Single Level Categories
        //$query = 'SELECT `id`, `title` FROM `#__CATEGORY_TABLE`';
        
        // Nested Categories
        $query = 'SELECT `id`, `title`, `level`, `parent_id` FROM `#__CATEGORY_TABLE`';
        
        // Run query and return categories
        $db->setQuery( $query );
        $categories = $db->loadObjectList();

        // Populate category tree for Komento Admin Integration Tab for this plugin (optional)
        // This is used in Komento where you select which categories the plugin should be active on etc...
        foreach( $categories as &$row )
        {
            
            // Single Level Categories
            //$row->level = 0;
            
            // Nested Categories
            $repeat = ( $row->level - 1 >= 0 ) ? $row->level - 1 : 0;            
            
            // Build and Add the Category Tree entry
            $row->treename = str_repeat( '.&#160;&#160;&#160;', $repeat ) . ( $row->level - 1 > 0 ? '|_&#160;' : '' ) . $row->title;
            
        }

        return $categories;
    }

    // This method lets Komento know if this is the front page or category layout (Determine if is listing view)
    public function isListingView()
    {
        $views = array('featured', 'category', 'categories', 'archive', 'frontpage' );

        return in_array(JRequest::getCmd('view'), $views);
    }

    // This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view)
    public function isEntryView()
    {
        return JRequest::getCmd('view') == 'article';
    }

    // This method is the main method that appends Komento on the article
    public function onExecute( &$article, $html, $view, $options = array() )
    {
        // $html is the html content generated by komento (includes listing and form)
        
        // Select 1 of the following outputs

        // This appends the HTML to the article object
        //$article->text .= $html;
        //return;

        // Return the Komento HTML code
        return $html;
    }
    
    /******************************************************
     *
     * END
     * [BASIC FUNCTIONS (METHODS)]
     *
     ******************************************************/
     
}

com_rsfeedback.php

This is the actual Komento plugin for RSFeedback and is very useful for comparing with my QW_com_sample.php to see what the differences are.

<?php
/**
* @package      Komento
* @copyright    Copyright (C) 2010 - 2016 Stack Ideas Sdn Bhd. All rights reserved.
* @copyright    Copyright (C) 2017        Jon Brown @ QuantumWarp.com
* @license      GNU/GPL, see LICENSE.php
* Komento is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*/

// No direct access
defined('_JEXEC') or die('Restricted access');

// Always load abstract class - This file also includes extra information about the functions here and the advanced functions not covered
//require_once( JPATH_ROOT . DIRECTORY_SEPARATOR . 'components' . DIRECTORY_SEPARATOR . 'com_komento' . DIRECTORY_SEPARATOR . 'komento_plugins' . DIRECTORY_SEPARATOR .'abstract.php' );  
require_once( JPATH_ROOT . '/components/com_komento/komento_plugins/abstract.php' );

class KomentoComrsfeedback extends KomentoExtension
{
    
    /******************************************************
     *
     * START
     * [BASIC FUNCTIONS (METHODS)]
     * These functions are mandatory
     *
     ******************************************************/
     
    // This property (object) stores all the required properties by Komento
    public $_item;

    // This property (array) stores all the key mappings of the required item properties to map from Komento's default key to your component's custom key
    public $_map = array(
    
        // not needed with custom getContentId()
        'id'            => 'id',

        // not needed with custom getContentTitle()
        'title'         => 'title',

        // not needed with custom getContentHits()
        'hits'          => 'hits',

        // not needed with custom getAuthorId()
        'created_by'    => 'user_id',

        // not needed with custom getCategoryId()
        'catid'         => 'cat_id',

        // not needed with custom getContentPermalink()
        'permalink'     => 'permalink'

        );

    // Constructor - Add all required files for your component here and run its constructor
    public function __construct( $component )
    {        
        parent::__construct( $component );
    }

    // This method should load the article's main properties based on article ID
    public function load( $cid )
    {
        static $instances = array();

        if( !isset( $instances[$cid] ) )
        {
            // populate $this->_item with:
            // id_field
            // title_field
            // hits_field
            // created_by_field
            // catid_field
            // permalink_field

            // Create a Database Object
            $db = KT::getDBO();
            
            // Create SQL query to load a single article
            $query    = 'SELECT `id`, `title`, `hits`, `user_id`, `cat_id` FROM `#__rsfeedback_feedbacks` WHERE `id` = ' . $db->quote( $cid );
            $db->setQuery( $query );

            // Run the single article query and if there are no objects to load call the onLoadArticleError event 
            if( !$this->_item = $db->loadObject() )
            {
                return $this->onLoadArticleError( $cid );
            }

            // Generate the permalink for this article
            $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field;

            // Call the prepareLink function and leave the rest to us
            // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite
            $this->_item->permalink = $this->prepareLink( $this->_item->permalink );

            $instances[$cid] = $this->_item;
        }

        $this->_item = $instances[$cid];

        return $this;
    }
    
    // This method should load all the article IDs filtered by category IDs
    public function getContentIds( $categories = '' )
    {
        // Create a Database Object
        $db = KT::getDBO();
        
        // Make sure the query is empty
        $query = '';

        // If no categories are supplied then load all article IDs
        if( empty( $categories ) )
        {
            $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` ORDER BY `id`';
        }
        
        // If categories are supplied then load all article IDs for articles belonging to those categories
        else
        {
            if( is_array( $categories ) )
            {
                $categories = implode( ',', $categories );
            }

            $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` WHERE `cat_id` IN (' . $categories . ') ORDER BY `id`';
        }

        // Run the query and return the results as an array
        $db->setQuery( $query );
        return $db->loadResultArray();
    }

    // This method should load all the category IDs of the component
    // Make sure you select 'Single Level' or 'Nested' categories
    public function getCategories()
    {
        // Create a Database Object
        $db = KT::getDBO();
        
        // Single Level Categories
        $query = 'SELECT `id`, `name` FROM `#__rsfeedback_categories`';        
        
        // Run query and return categories
        $db->setQuery( $query );
        $categories = $db->loadObjectList();

        // Populate category tree for Komento Admin Integration Tab for this plugin (optional)
        // This is used in Komento where you select which categories the plugin should be active on etc...
        foreach( $categories as &$row )
        {
            
            // Single Level Categories
            $row->level = 0;                    
            
            // Build and Add the Category Tree entry
            $row->treename = str_repeat( '.&#160;&#160;&#160;', $repeat ) . ( $row->level - 1 > 0 ? '|_&#160;' : '' ) . $row->name;
            
        }

        return $categories;
    }

    // This method lets Komento know if this is the front page or category layout (Determine if is listing view)
    public function isListingView()
    {
        $views = array('category', 'categories', 'feedbacks');

        return in_array(JRequest::getCmd('view'), $views);
    }

    // This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view)
    public function isEntryView()
    {
        return JRequest::getCmd('view') == 'feedback';
    }

    // This method is the main method that appends Komento on the article
    public function onExecute( &$article, $html, $view, $options = array() )
    {
        // $html is the html content generated by komento (includes listing and form)

        // Return the Komento HTML code
        return $html;
    }
    
    /******************************************************
     *
     * END
     * [BASIC FUNCTIONS (METHODS)]
     *
     ******************************************************/
     
}

Dissecting the Komento Plugin

If you have gone through the resources above (recommend) you will see that the functions are grouped into:

  • Basic Functions - These are mandatory
  • Extended Functions - These functions are for doing more advanced integrations. We will not needed them.
  • Trigger Functions - These are functions that are triggered when various events happen.

The plugin can also be placed into either of the following locations and note that the name is changed depending where you put it.

  • components/com_komento/komento_plugins/com_rsfeedback.php - The name is the same as the component.
  • components/com_rsfeedback/komento_plugin.php - Komento searches all component folders to see if this file exists and this mechanisim allows 3rd Party developers to put Komento integration into their software without getting the plugin inserted into Komento's distribution package.

Objects

The objects declared at the top of the plugin $_item and $_map are self explanatory.

  • $_item
    • This just stores all of the information that is built or collected such as the article_id and permalink for the article that we want to dispaly comments for.
  • $_map
    • This property (array) stores all the key mappings of the required item properties to map from Komento's default key to your component's custom key. In other words this allows Komento to use its own internal naming convention for the mandatory items when the 3rd party component has other names for these items.
    • All of these fields have 'Extended Functions' that can override the fields when more advanced building is required. (com_jdwonloads.php plugin files is an excellent example)

Functions

I will now go through the functions in the order they appear in the integration plugin. My notes will give you more of a practical insight into how they work. where an example is needed I will use my RSFeedback itegration code.

The QW_com_sample.php is fully annotated so if I missed anything just use that as a reference.

public function __construct( $component )

This function is where you include and files/dependencies that you need from the component, if any. This function then runs KT class constructor with the component name as a option so it has the relevant objects and resources for that component.

The parent::__construct( $component ); must be run at the end of the function so all the relevant assets have been called before it is intialised.

public function load( $cid )

This function loads a single article into the $_item object using the mappings we defined in $_map.

This is where the configuration fun begins. There are several parts to this function that need to be dealt with.

QW Sample Version

// This method should load the article's main properties based on article ID
public function load( $cid )
{
    static $instances = array();

    if( !isset( $instances[$cid] ) )
    {
        // populate $this->_item with:
        // id_field
        // title_field
        // hits_field
        // created_by_field
        // catid_field
        // permalink_field

        // Create a Database Object
        $db = KT::getDBO();
        
        // Create SQL query to load a single article
        $query = 'SELECT `id_field`, `title_field`, `hits_field`, `created_by_field`, `catid_field` FROM `#__ARTICLE_TABLE` WHERE `ARTICLE_ID` = ' . $db->quote( $cid );
        $db->setQuery( $query );

        // Run the single article query and if there are no objects to load call the onLoadArticleError event 
        if( !$this->_item = $db->loadObject() )
        {
            return $this->onLoadArticleError( $cid );
        }

        // Generate the permalink for this article
        $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field;

        // Call the prepareLink function and leave the rest to us
        // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite
        $this->_item->permalink_field = $this->prepareLink( $this->_item->permalink_field );

        $instances[$cid] = $this->_item;
    }

    $this->_item = $instances[$cid];

    return $this;
}

Create SQL query to load a single article

This query needs to be changed to load a single articles record for the components database. You can change the field names to match the components which can be found by looking at your database, locating the article table for your component and then rading the fild names from the top.

// Create SQL query to load a single article
$query = 'SELECT `id_field`, `title_field`, `hits_field`, `created_by_field`, `catid_field` FROM `#__ARTICLE_TABLE` WHERE `ARTICLE_ID` = ' . $db->quote( $cid );
$db->setQuery( $query );

So in the case of RSFeedback the code above will be changed to

// Create SQL query to load a single article
$query    = 'SELECT `id`, `title`, `hits`, `user_id`, `cat_id` FROM `#__rsfeedback_feedbacks` WHERE `id` = ' . $db->quote( $cid );
$db->setQuery( $query );

You will note that #__ is a Joomla substitution string for the table prefix and is absolutely needed. You now have configured the article to be loaded as needed except for the permalink. There might be some components that store their permalink in the articles record and if so just add the extra clause into the SQL statement above.

The Komento plugin for com_content.php has a more advanced article lookup where the various items are stored in different tables.

Generating a permalink

A permalink is the Non-SEF URL that Joomla uses to load pages and pretty much do most things.

To see a components permalink you need to turn off Search Engine Friendly URLs and Use URL Rewriting in Joomla's config and then browse to an article or category page for that component.

Menu Item Type Examples:

RSFeedback - Add Category
https://quantumwarp.com/index.php?option=com_rsfeedback&view=category&layout=edit&Itemid=1027

RSFeedback - Add Feedback
https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedback&layout=edit&Itemid=1027

RSFeedback Feedbacks View (List all Feedbacks)
https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedbacks&Itemid=1027

RSFeedback - List Categories
https://quantumwarp.com/index.php?option=com_rsfeedback&view=categories&Itemid=1027

RSFeedback - Single Feedback
https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedback&id=2&Itemid=1027

Browsing Examples:

RSFeedback Single Category
https://quantumwarp.com/index.php?option=com_rsfeedback&view=feedbacks&cat_id=1&Itemid=1027

The Itemid=1027 - This is the menu item that my RSFeedback is displayed under. The other varibles are all straight forward.

Now we know what we need to build for Komento to understand what page to dispaly the comments on lets get on with it. there are several different methods that I have come across for build the permalink and I will list them below.

Method 1 - Simple

This is bar far the easiest and is the one that is in QW_com_sample

// Generate the permalink for this article
$this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field;

// Call the prepareLink function and leave the rest to us
// Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite
$this->_item->permalink_field = $this->prepareLink( $this->_item->permalink_field );

As you can see it is literally is the permalink written out with the articles ID dynamically added at the end and then processed through a Komento function. The RSFeedback version lools like:

// Generate the permalink for this article
$this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field;

// Call the prepareLink function and leave the rest to us
// Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite
$this->_item->permalink = $this->prepareLink( $this->_item->permalink );

As you can see they are exactly the same. RSFeedback uses simple permalink for its feedbacks. You will see we did not have to add the menu's Itemid as the compoenent already takes care of this. Not all compoenents will have a simple permalink like RSFeedback.

Method 2 - function getContentPermalink()

This example is taken from the JDownloads Komento integration plugin that ships in the Komento package and is in the folder /komento_plugins/ .

This function's output will override $_map->permalink but as you can see it allows for a more complex permalink to be built from various data sources much the same as JDownloads will build the links itself.

JDownloads Plugin Version

public function getContentPermalink()
{
    $link = 'index.php?option=' . $this->component . '';

    $pieces = array(
        'option=' . $this->component,
        'Itemid=' . $this->getItemId(),
        'view=download',
        'catid=' . $this->getCategoryId(),
        'id=' . $this->getContentId()
    );

    $link = $this->prepareLink( 'index.php?' . implode( '&', $pieces ) );

    return $link;
}

Example permalink:

JDownloads Single Download permalink
https://quantumwarp.com/index.php?option=com_jdownloads&view=download&id=41:stackideas&catid=19&Itemid=859

I have not used this method so you will need to experiment, but it should not be that difficult now that you know what this 'Extended Function' does

So the function for RSFeedback is:

// This method should load the article's main properties based on article ID
public function load( $cid )
{
    static $instances = array();

    if( !isset( $instances[$cid] ) )
    {
        // populate $this->_item with:
        // id_field
        // title_field
        // hits_field
        // created_by_field
        // catid_field
        // permalink_field

        // Create a Database Object
        $db = KT::getDBO();
        
        // Create SQL query to load a single article
        $query    = 'SELECT `id`, `title`, `hits`, `user_id`, `cat_id` FROM `#__rsfeedback_feedbacks` WHERE `id` = ' . $db->quote( $cid );
        $db->setQuery( $query );

        // Run the single article query and if there are no objects to load call the onLoadArticleError event 
        if( !$this->_item = $db->loadObject() )
        {
            return $this->onLoadArticleError( $cid );
        }

        // Generate the permalink for this article
        $this->_item->permalink_field = 'index.php?options=com_sample&view=article&id=' . $this->_item->id_field;

        // Call the prepareLink function and leave the rest to us
        // Unless you have custom SEF methods, then use "getContentPermalink" function to overwrite
        $this->_item->permalink = $this->prepareLink( $this->_item->permalink );

        $instances[$cid] = $this->_item;
    }

    $this->_item = $instances[$cid];

    return $this;
}
Method 3 - Using the Joomla router

Again I have not used this method but I noticed that it used a router file (route.php). This example uses the com_content.php (Joomla Content) integration plugin that ships with Komento and is in the folder /komento_plugins/ .

Firstly the router file route.php for the component (com_content is actually a component in Joomla that is responsible for your articles)

public function __construct($component)
{
    // Add com_content's router file
    $file = JPATH_ROOT . '/components/com_content/helpers/route.php';
    $this->addFile($file);

    parent::__construct($component);
}

It has a much more advanced getContentPermalink() function that utilises the route.php (router file for the com_content component)

public function getContentPermalink()
{
    $slug = $this->_item->alias ? ($this->_item->id.':'.$this->_item->alias) : $this->_item->id;
    $catslug = $this->_item->category_alias ? ($this->_item->catid.':'.$this->_item->category_alias) : $this->_item->catid;
    $parent_slug = $this->_item->category_alias ? ($this->_item->parent_id.':'.$this->_item->parent_alias) : $this->_item->parent_id;

    $link = ContentHelperRoute::getArticleRoute($slug, $catslug);

    $link = $this->prepareLink($link);

    return $link;
}

public function getContentIds( $categories = '' )

This function will load all article IDs for the specified categories. I am not 100% this is used in RSFeedback but anyway.

We need to update the SQL so it loads article IDs for the specified catgories.

QW Sample Version

// This method should load all the article IDs filtered by category IDs
public function getContentIds( $categories = '' )
{
    // Create a Database Object
    $db = KT::getDBO();
    
    // Make sure the query is empty
    $query = '';

    
    // If no categories are supplied then load all article IDs
    if( empty( $categories ) )
    {
        $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` ORDER BY `id_field`';
    }
    
    // If categories are supplied then load all article IDs for articles belonging to those categories
    else
    {
        if( is_array( $categories ) )
        {
            $categories = implode( ',', $categories );
        }

        $query = 'SELECT `id_field` FROM `#__ARTICLE_TABLE` WHERE `catid_field` IN (' . $categories . ') ORDER BY `id_field`';
    }
    
    // Run the query and return the results as an array
    $db->setQuery( $query );
    return $db->loadResultArray();
}

Dont forget to get the new field names by using phpMyAdmin to find out where the categories are stored and then work out the new SQL statement.

RSFeedback Version

// This method should load all the article IDs filtered by category IDs
public function getContentIds( $categories = '' )
{
    // Create a Database Object
    $db = KT::getDBO();
    
    // Make sure the query is empty
    $query = '';

    // If no categories are supplied then load all article IDs
    if( empty( $categories ) )
    {
        $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` ORDER BY `id`';
    }
    
    // If categories are supplied then load all article IDs for articles belonging to those categories
    else
    {
        if( is_array( $categories ) )
        {
            $categories = implode( ',', $categories );
        }

        $query = 'SELECT `id` FROM `#__rsfeedback_feedbacks` WHERE `cat_id` IN (' . $categories . ') ORDER BY `id`';
    }

    // Run the query and return the results as an array
    $db->setQuery( $query );
    return $db->loadResultArray();
}

public function getCategories()

This function is used to build the category tree in Komento Integration admin so you can select what categories you want Kommento to work on.

This is quite an important function and you need to know whether your component has a 'single level' of categories or 'nested' categories (most joomla components do). The sample function below is rigged up for nested categories and might require the help of an additional function such as setLevel( $pid, $level, $categories, &$result ) to determine level. See the integration plugin com_jdownloads.php on how it was done as I dont think this example in QW_com_sample.php is complete.

QW Sample Version

// This method should load all the category IDs of the component
// Make sure you select 'Single Level' or 'Nested' categories
public function getCategories()
{
    // Create a Database Object
    $db = KT::getDBO();
    
    // Single Level Categories
    //$query = 'SELECT `id`, `title` FROM `#__CATEGORY_TABLE`';
    
    // Nested Categories
    $query = 'SELECT `id`, `title`, `level`, `parent_id` FROM `#__CATEGORY_TABLE`';
    
    // Run query and return categories
    $db->setQuery( $query );
    $categories = $db->loadObjectList();

    // Populate category tree for Komento Admin Integration Tab for this plugin (optional)
    // This is used in Komento where you select which categories the plugin should be active on etc...
    foreach( $categories as &$row )
    {
        
        // Single Level Categories
        //$row->level = 0;
        
        // Nested Categories
        $repeat = ( $row->level - 1 >= 0 ) ? $row->level - 1 : 0;            
        
        // Build and Add the Category Tree entry
        $row->treename = str_repeat( '.&#160;&#160;&#160;', $repeat ) . ( $row->level - 1 > 0 ? '|_&#160;' : '' ) . $row->title;
        
    }

    return $categories;
}

JDownloads Plugin (com_jdownloads.php)

public function getCategories()
{
    $sql = KT::sql();
    $sql->select( '#__jdownloads_categories' )
        ->column( 'id', 'id' )
        ->column( 'title', 'title' )
        ->column( 'parent_id' )
        // ->where( 'published', 1 )
        ->order( 'ordering' );

    $categories = $sql->loadObjectList();

    $result = array();

    $this->setLevel( 0, 0, $categories, $result );

    return $result;
}

private function setLevel( $pid, $level, $categories, &$result )
{
    foreach( $categories as &$category )
    {
        if( (int) $category->parent_id === (int) $pid )
        {
            $category->level = $level;

            $category->treename = str_repeat( '.&#160;&#160;&#160;', $level ) . ( $level > 0 ? '|_&#160;' : '' ) . $category->title;

            $result[] = $category;

            $this->setLevel( $category->id, $level + 1, $categories, $result );
        }
    }
}

RSFeedback has a very simple category storage as it is only a single level and not nested. If categories are in a nested format look at the com_content.php plugin or any of the other pluign files for examples.

I have altered the sample code just by

  • removed the nested categories code
  • enabled the Single Level category code
  • change the SQL queries to mathc RSFeedback tables
  • on Build and Add the Category Tree entry I change $row->title to $row->name as the field holding the title of the category in RSFeedback is name. This is only for display and does not affect the ampping mentioned earlier.

$row->level = 0; - This is to always set a level because this does not exist in a single level category system and it keeps the category tree building routine the same. You could in theory remove it but would make the code a bit messy ans should RSFeedback ever be upgraded to nested categories it would make sthings a lot easier to figure out.

RSFeedback Version

// This method should load all the category IDs of the component
// Make sure you select 'Single Level' or 'Nested' categories
public function getCategories()
{
    // Create a Database Object
    $db = KT::getDBO();
    
    // Single Level Categories
    $query = 'SELECT `id`, `name` FROM `#__rsfeedback_categories`';        
    
    // Run query and return categories
    $db->setQuery( $query );
    $categories = $db->loadObjectList();

    // Populate category tree for Komento Admin Integration Tab for this plugin (optional)
    // This is used in Komento where you select which categories the plugin should be active on etc...
    foreach( $categories as &$row )
    {
        
        // Single Level Categories
        $row->level = 0;                    
        
        // Build and Add the Category Tree entry
        $row->treename = str_repeat( '.&#160;&#160;&#160;', $repeat ) . ( $row->level - 1 > 0 ? '|_&#160;' : '' ) . $row->name;
        
    }

    return $categories;
}

public function isListingView()

This method lets Komento know if this is the front page or category layout (Determine if is listing view)

This is very simple to understand. We need to define what pages are categories and what are articles. This particular function tells Komento what are category or listing style pages and basically dont show commenting system on these.

QW Sample Version

// This method lets Komento know if this is the front page or category layout (Determine if is listing view)
public function isListingView()
{
    $views = array('categories', 'category', 'feedbacks');

    return in_array(JRequest::getCmd('view'), $views);
}

All you need to do is find the permalinks (addressed earlier) for all of the pages you do not want to show comments on and add them into the views array. I would do this by creating a menu item for every type of option available for RSFeedback and make a list of the permalinks into 2 groups, those you want comments on (should just be single feedback page) and a list of those you so not want comments on.

Of these you do not want comments on look at the permalink and you will see a GET parameter view=xxxx where xxxx is the view type. Add all of these view types into the $views = array() statement. Does not matter if there are duplicates just add the view type in once.

RSFeedback Version

// This method lets Komento know if this is the front page or category layout (Determine if is listing view)
public function isListingView()
{
    $views = array('category', 'categories', 'feedbacks');

    return in_array(JRequest::getCmd('view'), $views);
}

public function isEntryView()

This is the opposite of above. This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view).

Basically follow the procedure from above but reverse it selecting only the views that you want comments on. I dont know why you need to specify view types you want comments on and not on rather that just the pages you want them on but i am sure there is a reason. There usually is only type of page(view) you want the comments to be displayed on.

QW Sample Version

// This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view)
public function isEntryView()
{
    return JRequest::getCmd('view') == 'article';
}

RSFeedback Verison

// This method lets Komento know if this is the page that the comment form should be displayed on (Determine if is entry view)
public function isEntryView()
{
    return JRequest::getCmd('view') == 'feedback';
}

With RSFeedback there is only 1 page type you want to display comments on, the Single feedback page.

public function onExecute( &$article, $html, $view, $options = array() )

This function outputs the HMTL code upon execution. You can also use it to append code to an article object. Tehre is not much more to this fuction.

QW Sample Version

// This method is the main method that appends Komento on the article
public function onExecute( &$article, $html, $view, $options = array() )
{
    // $html is the html content generated by komento (includes listing and form)
    
    // Select 1 of the following outputs

    // This appends the HTML to the article object
    //$article->text .= $html;
    //return;

    // Return the Komento HTML code
    return $html;
}

I uses the straight HMTL output for RSFeedback because the code in RSFeedback is geared up to recieve the HTML rather being added to the article object.

RSFeedback Version

// This method is the main method that appends Komento on the article
public function onExecute( &$article, $html, $view, $options = array() )
{
    // $html is the html content generated by komento (includes listing and form)

    // Return the Komento HTML code
    return $html;
}

RSFeedback Component Integration

We have now been through the basics of the Komento plugin with an emphasis on RSFeedback but these instruction like those before can be easily applied to other components.

Create and install the Komento integration plugin

  • You can either work through the instructions above making your plugin (if not RSFeedback) or copy and paste the code from the top.
  • Save the file as components/com_rsfeedback/komento_plugin.php

Examine RSFeedback code for changes that we need to make

RSFeedback supports several commenting systems out of the box (Inbuilt Comment System / RSComments / JComments / Jom Comments) but not Komento. Komento will have to be added into RSFeedback by altering some of it's core code.

Make sure you have backups and do all of this work on a test website first before working on your live site.

Because RSFeedback already has these comment systems installed I extracted the files and then did a text search to search for 'rscomments' and this return the following 3 files.

Extracted Installation Package Location

  1. admin/models/fields/commentingsystems.php
  2. site/helpers/rsfeedback.php
  3. site/models/feedback.php

Location when installed in Joomla

  1. administrator/components/com_rsfeedback/models/fields/commentingsystems.php
  2. components/com_rsfeedback/helpers/rsfeedback.php
  3. components/com_rsfeedback/models/feedback.php

Purpose of files

  1. This file is used in order to display the commenting systems in the configuration page of the component
  2. This file is used in order to include the commenting form and the comments
  3. This file is used in order to include the commenting form and the comments - The public function getComments() is not actually used anywhere is RSComments so it is redundant and has been reported to RSFeedback. You can add the code here aswell if you want, it will not harm.

NB: File 3 does not make anydifference but is include for completeness

Making the Changes

We now have the location in the code where we need to make changes you should go and have a quick look at the files to see how the comment systems are integrated. With systems like this is it is failry easy to add another commenting system as there will be standard that adds each commenting system in the same way, there will just be a slight difference in the final call to the commenting system and the included files that are need to call the commenting system.

commentingsystems.php

This file adds the commenting systems as selections in the RSFeedback Joomla admin.

{add picture here}

Add the bottom of each of the groups of code I have simply added an entry for Komento giving it the option number 5, this is important for the next changes as it is the reference number we will be using for the Konto commenting system in RSFeeedback.

The following line is added to the bottom of the first group and is used as a boolean check to see if Komento is installed. You could probably use bootstrap.php instead but I am not sure about this and is not that important. Using koment.php certainly makes it easier to follow

$komento    = file_exists(JPATH_SITE.'/components/com_komento/komento.php');

The second group of code in the fuction builds up the array for the RSFeedback admin and returns the results. Commenting systems that are not present will appear greyed out. Add the following line:

$commentsystem[] = JHTML::_('select.option', '5', JText::_('Komento') , 'value' , 'text', !$komento);

This will give you the full file shown below:

/**
* @package RSFeedback!
* @copyright (C) 2010-2014 www.rsjoomla.com
* @license GPL, http://www.gnu.org/copyleft/gpl.html
*/
defined('_JEXEC') or die('Restricted access');
jimport('joomla.form.formfield');

class JFormFieldCommentingSystems extends JFormField {
    protected $type = 'CommentingSystems';

    public function getInput() 
    {
        $jcomment   = file_exists(JPATH_SITE.'/components/com_jcomments/jcomments.php');
        $jomcomment = file_exists(JPATH_SITE.'/plugins/content/jom_comment_bot.php');
        $rscomment  = file_exists(JPATH_SITE.'/components/com_rscomments/rscomments.php');        
        $komento    = file_exists(JPATH_SITE.'/components/com_komento/komento.php');

        $commentsystem = array();
        $commentsystem[] = JHTML::_('select.option', '0', JText::_( 'COM_RSFEEDBACK_COMMENTS_DISABLED' ) );
        $commentsystem[] = JHTML::_('select.option', '1', JText::_( 'COM_RSFEEDBACK_DEFAULT_COMMENTS' ) );
        $commentsystem[] = JHTML::_('select.option', '2', JText::_('RSComments!') , 'value' , 'text', !$rscomment);
        $commentsystem[] = JHTML::_('select.option', '3', JText::_('JComments') , 'value' , 'text' , !$jcomment);
        $commentsystem[] = JHTML::_('select.option', '4', JText::_('Jom Comments') , 'value' , 'text', !$jomcomment);
        $commentsystem[] = JHTML::_('select.option', '5', JText::_('Komento') , 'value' , 'text', !$komento);

        $html = JHTML::_('select.genericlist', $commentsystem, 'jform[feedback_commenting]', '', 'value', 'text', $this->value);

        return $html;
    }
}

rsfeedback.php

Find the public static function DisplayComments($id) and add the following code at the end of the switch list. You will note it is option 5.

This is the code that will return the HTML from Komento (which has the comments for that Feedback item and the new comment form etc..) which is then assigned to the $comments->listing object which is the generic container RSFeedback uses to hold the comments from the various systems and is then this object that gets rendered below the Feedback giving you the comments.

case '5' :
    require_once( JPATH_ROOT . '/components/com_komento/bootstrap.php' );
    $comments->listing  = KT::commentify('com_rsfeedback', $id);
    $comments->form     = '';
break;

feedback.php

This is a very similiar arrangement to rsfeedback.php except it is not used anywhere and all the code is within public function getComments(). The code here is slightly different but would do the same job if it was used.

add the following lines at the end of the switch list in the function public function getComments().

//Komento integration
case 5:
    if (file_exists(JPATH_ROOT . '/components/com_komento/bootstrap.php')) :
        require_once(JPATH_ROOT . '/components/com_komento/bootstrap.php');
        $comments .= KT::commentify('com_rsfeedback', $this->data->IdFeedback, $options);
    endif;
break;

KT:: or Komento:: or KMT::

Firstly all of these are referenced in:

{Joomla Route}/administrator/components/com_komento/includes/komento.php

What is this all about. KT:: is the new name for the Komento class and it is this that should be used going forwards when making plugins even thought the official instructions (at time of writing) tell you to use Komento:: which is confusing. However the following code at the bottom of komento.php allows the legacy use of these class names.

class KMT extends KT {}
class Komento extends KT {}

So going forwards just use KT:: so for example

require_once(JPATH_ROOT . '/components/com_komento/bootstrap.php');
Komento::commentify('com_sample', $article, $options);

should be

require_once(JPATH_ROOT . '/components/com_komento/bootstrap.php');
KT::commentify('com_sample', $article, $options);

NB: You can call KT:commentify() because it is a static function. I will not go into this further but you might notice a couple of these in the code.

Comments Count

We need to get the comments count from comment and feed this information back to RSFeedback so it can be displayed.

The function that Komento uses is getCount($component, $cid), in the  KT:: class, in the file administrator/components/com_komento/includes/komento.php approx line 1355

$commentsModel = KT::model('Comments');                                 // Create a Comments object
$commentCount  = $commentsModel->getCount($component, $cid);            // Return the Comment count

The function RSFeedback uses to display the comment count is getFeedbackCommentsCount($id) in the file components/com_rsfeedback/helpers/rsfeedback.php approx line 189

Again with previous codes there is a switch choice and we just need to add the Komento code in as option 5.

case '5' :
    require_once( JPATH_ROOT . '/components/com_komento/bootstrap.php' );   // Intialize Komento             
    $commentsModel = KT::model('Comments');                                 // Create a Comments object             
    $comments = $commentsModel->getCount('com_rsfeedback', $id);            // Return the Comment count              
break;

Translation

The last thing to do is translate the new Komento Integration Tab and the dropdown menu option used in various places for our plugin in Komento by adding these translation strings.

COM_KOMENTO_COM_RSFEEDBACK="RSFeedback"
COM_KOMENTO_SETTINGS_TAB_COM_RSFEEDBACK_SETTINGS="RSFeedback"

Thre are 2 ways of adding the translation string into joomla

  • Manually add the translation via Joomla's backend language override section (recommended). This translatation should be added to the admin section
  • Add the translation string as is to administrator/language/en-GB/en-GB.com_komento.ini (or whatever language you need)

Done - Recap

We have now completed all of the steps required to integrate Komento into RSFeedback and have covered most aspects of this process. So he is a quick recap of what to do.

  1. Build the plugin code (or copy and paste it from the top) and create the file components/com_rsfeedback/komento_plugin.php
  2. Add the Komento code into the following files
    • administrator/components/com_rsfeedback/models/fields/commentingsystems.php
    • components/com_rsfeedback/helpers/rsfeedback.php
    • components/com_rsfeedback/models/feedback.php (optional)
  3. Add translation string (via Joomla admin is best)

 

Read 1528 times Last modified on Wednesday, 08 March 2017 09:11