Articles in This Series
This is a multiple-article series of tutorials on how to develop a Model-View-Controller Component for Joomla! Version.
Begin with the Introduction, and navigate the articles in this series by using the navigation button at the bottom or the box to the right (the Articles in this series).
This tutorial is part of the Developing an MVC Component for Joomla! 3.2 tutorial. You are encouraged to read the previous parts of the tutorial before reading this.
In this step we add a feed to our component. A video accompanying this tutorial may be found at Adding a Feed.
If you're not familiar with feeds then it's worth doing some background reading on the Internet. Joomla provides the capability of generating a feed of featured articles, or articles in a category – see for example this tutorial. In this tutorial step we enable equivalent functionality for generating a feed of helloworld records associated with a helloworld category.
On the Joomla website setting up a feed associated with an article category involves
This module displays an RSS symbol on the webpage which has behind it a link to the URL of the RSS or Atom feed associated with that article category.
To subscribe to the feed a user copies this URL into his or her feed aggregator. When the feed aggregator sends an HTTP request to this URL, then the Joomla website returns an XML document containing details of the articles in that category – their titles, descriptions, published dates, etc.
So for example, if the webpage displays a list of the articles associated with a category "technology" then the URL behind the RSS image will point to the associated feed. When a browser or feed aggregator sends an HTTP request to that feed URL then the server will return an XML document in RSS or Atom format containing details of those articles with a category of "technology".
In this way a feed aggregator can send HTTP requests to all of the feeds to which the user has subscribed, and collate them so that user doesn't have to visit each of the individual news websites. And the user can configure the news aggregator to check the feeds at specific times, so that the information can been already collected and collated by the time the user wishes to view it.
Note that in addition to the syndicated feed which we're considering here, Joomla also has a newsfeed capability, which enables you to display a webpage showing information collected from syndicated feeds elsewhere – similar to what a feed aggregator would do – but this isn't the functionality which this tutorial step is about.
We currently have a site category view which displays the helloworld records associated with a given category. In this step we add an RSS syndicated feed to this page. This will result in an RSS symbol shown as an image, with a URL behind which will give an XML feed in RSS or Atom format of the helloworld records associated with that category.
If you haven't already got a site menuitem which points to a category view then you should configure one as a starting point.
To enable the syndicated feed we must do the following.
The URL of the RSS feed will be something like: http://yourDomain.com/yourJoomlaInstance/index.php?option=com_helloworld&view=category&id=37&Itemid=822&lang=en&format=feed&type=rss When Joomla receives a URL like this, it handles it in the same way as usual:
Following standard Joomla practice, we put the Show Feed Link option within the integration tab of the Helloworld configuration options.
admin/config.xml
<?xml version="1.0" encoding="utf-8"?>
<config>
<fieldset
name="greetings"
label="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_LABEL"
description="COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_DESC"
>
<field
name="show_category"
type="radio"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC"
default="0"
>
<option value="0">JHIDE</option>
<option value="1">JSHOW</option>
</field>
<field
name="captcha"
type="plugins"
folder="captcha"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC"
default="0"
filter="cmd"
>
<option value="">JOPTION_USE_DEFAULT</option>
<option value="0">JOPTION_DO_NOT_USE</option>
</field>
<field
name="user_to_email"
type="user"
label="COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_LABEL"
description="COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_DESC"
default="0"
>
</field>
<field
name="save_history"
type="radio"
class="btn-group btn-group-yesno"
default="1"
label="JGLOBAL_SAVE_HISTORY_OPTIONS_LABEL"
description="JGLOBAL_SAVE_HISTORY_OPTIONS_DESC"
>
<option
value="0">JNO</option>
<option
value="1">JYES</option>
</field>
<field
name="history_limit"
type="text"
filter="integer"
label="JGLOBAL_HISTORY_LIMIT_OPTIONS_LABEL"
description="JGLOBAL_HISTORY_LIMIT_OPTIONS_DESC"
default="5"
>
</field>
</fieldset>
<fieldset name="integration"
label="JGLOBAL_INTEGRATION_LABEL"
description="COM_HELLOWORLD_CONFIG_INTEGRATION_SETTINGS_DESC"
>
<field
name="show_feed_link"
type="radio"
class="btn-group btn-group-yesno"
default="1"
label="JGLOBAL_SHOW_FEED_LINK_LABEL"
description="JGLOBAL_SHOW_FEED_LINK_DESC">
<option value="1">JSHOW</option>
<option value="0">JHIDE</option>
</field>
</fieldset>
<fieldset
name="permissions"
label="JCONFIG_PERMISSIONS_LABEL"
description="JCONFIG_PERMISSIONS_DESC"
>
<field
name="rules"
type="rules"
label="JCONFIG_PERMISSIONS_LABEL"
class="inputbox"
validate="rules"
filter="rules"
component="com_helloworld"
section="component"
/>
</fieldset>
</config>
In our updated category view html file we change the class we're inheriting from and include a call to the parent addFeed() method. This method expects the params to have been set.
site/views/category/view.html.php
<?php
/*
* View file for the view which displays a list of helloworld messages in a given category
*/
defined('_JEXEC') or die;
class HelloworldViewCategory extends JViewCategory
{
public function display($tpl = null)
{
$this->categoryName = $this->get("CategoryName");
$this->items = $this->get('Items');
$this->pagination = $this->get('Pagination');
$this->state = $this->get('State');
$this->filterForm = $this->get('FilterForm');
$this->activeFilters = $this->get('ActiveFilters');
$this->subcategories = $this->get('Subcategories');
$this->params = JFactory::getApplication()->getParams();
parent::display($tpl);
}
protected function prepareDocument()
{
parent::prepareDocument();
parent::addFeed();
}
}
We also have the new view file for the feed format.
site/views/category/view.feed.php
<?php
/**
* view file associated with the Syndicated Feed for a helloworld category
*/
defined('_JEXEC') or die;
class HelloworldViewCategory extends JViewCategoryfeed
{
// required so that the parent class can find the helloworld content-type record containing the field mapping details
protected $viewName = 'helloworld';
/**
* Function overriding the parent reconcileNames() method.
* We use this to insert an html link to the helloworld image into the description
*
* The input parameter is the helloworld item as extracted from the database, passed by reference
*
* The result of the method is that the helloworld item passed as a parameter gets its description property changed
*/
protected function reconcileNames($item)
{
$description = '';
if (!empty($item->image))
{
// Convert the JSON-encoded image info into an array
$imageDetails = new JRegistry;
$imageDetails->loadString($item->image, 'JSON');
$src = $imageDetails->get('image','');
if (!empty($src))
{
$description .= '<p><img src="' . $src . '" /></p>';
}
}
$item->description = $description . $item->description;
}
}
site/models/category.php
<?php
/**
* Model for displaying the helloworld messages in a given category
*/
defined('_JEXEC') or die;
class HelloworldModelCategory extends JModelList
{
public function __construct($config = array())
{
if (empty($config['filter_fields']))
{
$config['filter_fields'] = array(
'id',
'greeting',
'alias',
'lft',
);
}
parent::__construct($config);
}
protected function populateState($ordering = null, $direction = null)
{
parent::populateState($ordering, $direction);
$app = JFactory::getApplication('site');
$catid = $app->input->getInt('id');
$this->setState('category.id', $catid);
}
protected function getListQuery()
{
$db = JFactory::getDbo();
$query = $db->getQuery(true);
$catid = $this->getState('category.id');
$query->select('id, greeting, alias, catid, access, description, image')
->from($db->quoteName('#__helloworld'))
->where('catid = ' . $catid);
if (JLanguageMultilang::isEnabled())
{
$lang = JFactory::getLanguage()->getTag();
$query->where('language IN ("*","' . $lang . '")');
}
$orderCol = $this->state->get('list.ordering', 'lft');
$orderDirn = $this->state->get('list.direction', 'asc');
$query->order($db->escape($orderCol) . ' ' . $db->escape($orderDirn));
return $query;
}
public function getCategoryName()
{
$catid = $this->getState('category.id');
$categories = JCategories::getInstance('Helloworld', array('access' => false));
$categoryNode = $categories->get($catid);
return $categoryNode->title;
}
public function getSubcategories()
{
$catid = $this->getState('category.id');
$categories = JCategories::getInstance('Helloworld', array('access' => false));
$categoryNode = $categories->get($catid);
$subcats = $categoryNode->getChildren();
$lang = JFactory::getLanguage()->getTag();
if (JLanguageMultilang::isEnabled() && $lang)
{
$query_lang = "&lang={$lang}";
}
else
{
$query_lang = '';
}
foreach ($subcats as $subcat)
{
$subcat->url = JRoute::_("index.php?view=category&id=" . $subcat->id . $query_lang);
}
return $subcats;
}
public function getCategoryAccess()
{
$catid = $this->getState('category.id');
$categories = JCategories::getInstance('Helloworld', array('access' => false));
$categoryNode = $categories->get($catid);
return $categoryNode->access;
}
public function getItems()
{
$items = parent::getItems();
$user = JFactory::getUser();
$loggedIn = $user->get('guest') != 1;
if ($user->authorise('core.admin')) // ie superuser
{
return $items;
}
else
{
$userAccessLevels = $user->getAuthorisedViewLevels();
$catAccess = $this->getCategoryAccess();
if (!in_array($catAccess, $userAccessLevels))
{ // the user hasn't access to the category
if ($loggedIn)
{
return array();
}
else
{
foreach ($items as $item)
{
$item->canAccess = false;
}
return $items;
}
}
foreach ($items as $item)
{
if (!in_array($item->access, $userAccessLevels))
{
if ($loggedIn)
{
unset($item);
}
else
{
$item->canAccess = false;
}
}
}
}
return $items;
}
public function getCategory()
{
$categories = JCategories::getInstance('Helloworld', array());
$category = $categories->get($this->getState('category.id', 'root'));
return $category;
}
}
admin/language/en-GB/en-GB.com_helloworld.ini
; Joomla! Project
; Copyright (C) 2005 - 2018 Open Source Matters. All rights reserved.
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php
; Note : All ini files need to be saved as UTF-8
COM_HELLOWORLD_ADMINISTRATION="HelloWorld - Administration"
COM_HELLOWORLD_ADMINISTRATION_CATEGORIES="HelloWorld - Categories"
COM_HELLOWORLD_NUM="#"
COM_HELLOWORLD_HELLOWORLDS_FILTER="Filters"
COM_HELLOWORLD_AUTHOR="Author"
COM_HELLOWORLD_LANGUAGE="Language"
COM_HELLOWORLD_CREATED_DATE="Created"
COM_HELLOWORLD_PUBLISHED="Published"
COM_HELLOWORLD_HELLOWORLDS_NAME="Name"
COM_HELLOWORLD_HELLOWORLDS_POSITION="Position"
COM_HELLOWORLD_HELLOWORLDS_IMAGE="Image"
COM_HELLOWORLD_HELLOWORLDS_ASSOCIATIONS="Associations"
COM_HELLOWORLD_ID="Id"
COM_HELLOWORLD_HELLOWORLD_CREATING="HelloWorld - Creating"
COM_HELLOWORLD_HELLOWORLD_DETAILS="Details"
COM_HELLOWORLD_HELLOWORLD_EDITING="HelloWorld - Editing"
COM_HELLOWORLD_HELLOWORLD_ERROR_UNACCEPTABLE="Some values are unacceptable"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_DESC="The category the messages belongs to"
COM_HELLOWORLD_HELLOWORLD_FIELD_CATID_LABEL="Category"
COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_DESC="This message will be displayed"
COM_HELLOWORLD_HELLOWORLD_FIELD_GREETING_LABEL="Message"
COM_HELLOWORLD_HELLOWORLD_FIELD_DESCRIPTION_DESC="Message description"
COM_HELLOWORLD_HELLOWORLD_FIELD_DESCRIPTION_LABEL="Description"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_LABEL="Show category"
COM_HELLOWORLD_HELLOWORLD_FIELD_SHOW_CATEGORY_DESC="If set to Show, the title of the message’s category will show."
COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_LABEL="Latitude"
COM_HELLOWORLD_HELLOWORLD_FIELD_LATITUDE_DESC="Enter the position latitude, between -90 and +90 degrees"
COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_LABEL="Longitude"
COM_HELLOWORLD_HELLOWORLD_FIELD_LONGITUDE_DESC="Enter the position longitude, between -180 and +180 degrees"
COM_HELLOWORLD_HELLOWORLD_FIELD_PARENT_LABEL="Parent"
COM_HELLOWORLD_HELLOWORLD_FIELD_PARENT_DESC="Select the parent record"
COM_HELLOWORLD_ITEM_FIELD_ORDERING_VALUE_FIRST="-- First record"
COM_HELLOWORLD_ITEM_FIELD_ORDERING_VALUE_LAST="-- Last record"
COM_HELLOWORLD_ITEM_FIELD_ORDERING_TEXT="Ordering will be available after saving."
COM_HELLOWORLD_HELLOWORLD_FIELD_LANGUAGE_DESC="Select the appropriate language"
COM_HELLOWORLD_IMAGE_FIELDS="Image details"
COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_LABEL="Select image"
COM_HELLOWORLD_HELLOWORLD_FIELD_IMAGE_DESC="Select an image from the library, or upload a new one"
COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_LABEL="Alt text"
COM_HELLOWORLD_HELLOWORLD_FIELD_ALT_DESC="Alternative text (if image cannot be displayed)"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_LABEL="Caption"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTION_DESC="Provide a caption for the image"
COM_HELLOWORLD_HELLOWORLD_HEADING_GREETING="Greeting"
COM_HELLOWORLD_HELLOWORLD_HEADING_ID="Id"
COM_HELLOWORLD_MANAGER_HELLOWORLD_EDIT="HelloWorld manager: Edit Message"
COM_HELLOWORLD_MANAGER_HELLOWORLD_NEW="HelloWorld manager: New Message"
COM_HELLOWORLD_MANAGER_HELLOWORLDS="HelloWorld manager"
COM_HELLOWORLD_EDIT_HELLOWORLD="Edit message"
COM_HELLOWORLD_N_ITEMS_DELETED_1="One message deleted"
COM_HELLOWORLD_N_ITEMS_DELETED_MORE="%d messages deleted"
COM_HELLOWORLD_N_ITEMS_PUBLISHED="%d message(s) published"
COM_HELLOWORLD_N_ITEMS_UNPUBLISHED="%d message(s) unpublished"
COM_HELLOWORLD_HELLOWORLD_GREETING_LABEL="Greeting"
COM_HELLOWORLD_HELLOWORLD_GREETING_DESC="Add Hello World Greeting"
COM_HELLOWORLD_SUBMENU_MESSAGES="Messages"
COM_HELLOWORLD_SUBMENU_CATEGORIES="Categories"
COM_HELLOWORLD_CONFIGURATION="HelloWorld Configuration"
COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_LABEL="Messages settings"
COM_HELLOWORLD_CONFIG_GREETING_SETTINGS_DESC="Settings that will be applied to all messages by default"
COM_HELLOWORLD_CONFIG_INTEGRATION_SETTINGS_DESC="Settings relating to integration with the Syndicated Feed module"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_LABEL="Captcha"
COM_HELLOWORLD_HELLOWORLD_FIELD_CAPTCHA_DESC="Select Captcha to use on front end form"
COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_LABEL="User to email"
COM_HELLOWORLD_HELLOWORLD_FIELD_USER_TO_EMAIL_DESC="Select user to email when a new message is entered on front end"
COM_HELLOWORLD_FIELDSET_RULES="Message Permissions"
COM_HELLOWORLD_FIELD_RULES_LABEL="Permissions"
COM_HELLOWORLD_ACCESS_DELETE_DESC="Is this group allowed to edit this message?"
COM_HELLOWORLD_ACCESS_DELETE_DESC="Is this group allowed to delete this message?"
COM_HELLOWORLD_TAB_NEW_MESSAGE="New Message"
COM_HELLOWORLD_TAB_EDIT_MESSAGE="Message Details"
COM_HELLOWORLD_TAB_PARAMS="Parameters"
COM_HELLOWORLD_TAB_ASSOCIATIONS="Associations"
COM_HELLOWORLD_TAB_PERMISSIONS="Permissions"
COM_HELLOWORLD_TAB_IMAGE="Image"
COM_HELLOWORLD_LEGEND_DETAILS="Message Details"
COM_HELLOWORLD_LEGEND_PARAMS="Message Parameters"
COM_HELLOWORLD_LEGEND_ASSOCIATIONS="Message Associations"
COM_HELLOWORLD_LEGEND_PERMISSIONS="Message Permissions"
COM_HELLOWORLD_LEGEND_IMAGE="Image info"
; Column ordering in the Helloworlds view
COM_HELLOWORLD_ORDERING_ASC="Ordering ascending"
COM_HELLOWORLD_ORDERING_DESC="Ordering descending"
COM_HELLOWORLD_GREETING_ASC="Greeting ascending"
COM_HELLOWORLD_GREETING_DESC="Greeting descending"
COM_HELLOWORLD_AUTHOR_ASC="Author ascending"
COM_HELLOWORLD_AUTHOR_DESC="Author descending"
COM_HELLOWORLD_CREATED_ASC="Creation date ascending"
COM_HELLOWORLD_CREATED_DESC="Creation date descending"
COM_HELLOWORLD_PUBLISHED_ASC="Unpublished first"
COM_HELLOWORLD_PUBLISHED_DESC="Published first"
COM_HELLOWORLD_LANGUAGE_ASC="Language ascending"
COM_HELLOWORLD_LANGUAGE_DESC="Language descending"
COM_HELLOWORLD_ASSOCIATION_ASC="Association ascending"
COM_HELLOWORLD_ASSOCIATION_DESC="Association descending"
COM_HELLOWORLD_ACCESS_ASC="Access ascending"
COM_HELLOWORLD_ACCESS_DESC="Access descending"
; Helloworld menuitem - selecting a greeting via modal
COM_HELLOWORLD_MENUITEM_SELECT_MODAL_TITLE="Select greeting"
COM_HELLOWORLD_MENUITEM_SELECT_HELLOWORLD="Select"
COM_HELLOWORLD_MENUITEM_SELECT_BUTTON_TOOLTIP="Select a helloworld greeting"
; Checking in records
COM_HELLOWORLD_N_ITEMS_CHECKED_IN_0="No records checked in."
COM_HELLOWORLD_N_ITEMS_CHECKED_IN_1="%d record successfully checked in."
COM_HELLOWORLD_N_ITEMS_CHECKED_IN_MORE="%d records successfully checked in."
; Batch functionality
COM_HELLOWORLD_BATCH_OPTIONS="Helloworld component batch options"
COM_HELLOWORLD_BATCH_SETPOSITION_LABEL="Set location"
COM_HELLOWORLD_BATCH_KEEP_POSITION="Keep existing location"
COM_HELLOWORLD_BATCH_CHANGE_POSITION="Change location"
Contents of your code directory. Each file link below takes you to the step in the tutorial which has the latest version of that source code file.
helloworld.xml
<?xml version="1.0" encoding="utf-8"?>
<extension type="component" version="3.0" method="upgrade">
<name>COM_HELLOWORLD</name>
<!-- The following elements are optional and free of formatting constraints -->
<creationDate>January 2018</creationDate>
<author>John Doe</author>
<authorEmail>john.doe@example.org</authorEmail>
<authorUrl>http://www.example.org</authorUrl>
<copyright>Copyright Info</copyright>
<license>License Info</license>
<!-- The version string is recorded in the components table -->
<version>0.0.32</version>
<!-- The description is optional and defaults to the name -->
<description>COM_HELLOWORLD_DESCRIPTION</description>
<!-- Runs on install/uninstall/update; New in 2.5 -->
<scriptfile>script.php</scriptfile>
<install> <!-- Runs on install -->
<sql>
<file driver="mysql" charset="utf8">sql/install.mysql.utf8.sql</file>
</sql>
</install>
<uninstall> <!-- Runs on uninstall -->
<sql>
<file driver="mysql" charset="utf8">sql/uninstall.mysql.utf8.sql</file>
</sql>
</uninstall>
<update> <!-- Runs on update; New since J2.5 -->
<schemas>
<schemapath type="mysql">sql/updates/mysql</schemapath>
</schemas>
</update>
<!-- Site Main File Copy Section -->
<!-- Note the folder attribute: This attribute describes the folder
to copy FROM in the package to install therefore files copied
in this section are copied from /site/ in the package -->
<files folder="site">
<filename>index.html</filename>
<filename>helloworld.php</filename>
<filename>controller.php</filename>
<filename>router.php</filename>
<folder>controllers</folder>
<folder>views</folder>
<folder>models</folder>
<folder>helpers</folder>
</files>
<languages folder="site/language">
<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
</languages>
<media destination="com_helloworld" folder="media">
<filename>index.html</filename>
<folder>images</folder>
<folder>js</folder>
<folder>css</folder>
</media>
<administration>
<!-- Administration Menu Section -->
<menu link='index.php?option=com_helloworld' img="../media/com_helloworldhttps://docs.joomla.org/images/tux-16x16.png">COM_HELLOWORLD_MENU</menu>
<!-- Administration Main File Copy Section -->
<!-- Note the folder attribute: This attribute describes the folder
to copy FROM in the package to install therefore files copied
in this section are copied from /admin/ in the package -->
<files folder="admin">
<!-- Admin Main File Copy Section -->
<filename>index.html</filename>
<filename>config.xml</filename>
<filename>helloworld.php</filename>
<filename>controller.php</filename>
<filename>access.xml</filename>
<!-- SQL files section -->
<folder>sql</folder>
<!-- tables files section -->
<folder>tables</folder>
<!-- models files section -->
<folder>models</folder>
<!-- views files section -->
<folder>views</folder>
<!-- controllers files section -->
<folder>controllers</folder>
<!-- helpers files section -->
<folder>helpers</folder>
<!-- layout files section -->
<folder>layouts</folder>
</files>
<languages folder="admin/language">
<language tag="en-GB">en-GB/en-GB.com_helloworld.ini</language>
<language tag="en-GB">en-GB/en-GB.com_helloworld.sys.ini</language>
<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.ini</language>
<language tag="fr-FR">fr-FR/fr-FR.com_helloworld.sys.ini</language>
</languages>
</administration>
</extension>