<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.sandbox.joomla.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Znarf</id>
	<title>Joomla! Documentation - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.sandbox.joomla.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Znarf"/>
	<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/Special:Contributions/Znarf"/>
	<updated>2026-08-10T00:47:09Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68093</id>
		<title>Archived:Adding custom fields to the article component</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68093"/>
		<updated>2012-06-29T21:17:33Z</updated>

		<summary type="html">&lt;p&gt;Znarf: /* Extension Files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This tutorial is for Joomla {{JVer|2.5}}&lt;br /&gt;
&lt;br /&gt;
== Intro ==&lt;br /&gt;
This plugin demonstrates how a component developer can use the features in Joomla 2.5 to add custom fields to the article component (com_content), without the need to change core files. Capabilities that are implemented in this demo package include:&lt;br /&gt;
&lt;br /&gt;
# Adding extra fields to the back-end article manager.&lt;br /&gt;
# Adding extra fields to the front-end article editor.&lt;br /&gt;
# Storing the values of the custom fields into a database table.&lt;br /&gt;
# Converting the field values into a HTML table which is injected into the displayed article.&lt;br /&gt;
&lt;br /&gt;
A capability which is present but not demonstrated, is integrating the values of the custom fields into other components and views, such as article lists or category blogs.&lt;br /&gt;
&lt;br /&gt;
== This example ==&lt;br /&gt;
This example adds food ratings to articles. These ratings consist of texture, temperature and taste. When present, they will be displayed as a simple table. This table is positioned before the article content.&lt;br /&gt;
[[File:Plg_content_rating-1.png|thumb|frame|none|alt=example of rendered article|An example of how the plugin places the table at the beginning of an article]]&lt;br /&gt;
&lt;br /&gt;
Setting the values of these fields can be done in both the backend and frontend.&lt;br /&gt;
[[File:Plg_content_rating-frontend.png|thumb|frame|left|alt=edit article frontend|Accessing the additional fields through the frontend]]&lt;br /&gt;
[[File:Plg_content_rating-backend.png|thumb|frame|none|alt=edit article backend|... and the backend]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Framework ==&lt;br /&gt;
Within Joomla, the article manager is basically a form driven component. Both in the backend as the frontend. When handling a form, the framework performs the following actions.&lt;br /&gt;
&lt;br /&gt;
# Loads the form description. This is a XML structure.&lt;br /&gt;
# Loads the data from the user state if present, and if not from the database.&lt;br /&gt;
# Injects the data into the form&lt;br /&gt;
# Renders/displays the form for the user to work on.&lt;br /&gt;
&lt;br /&gt;
When the user is finished and issues a &amp;lt;code&amp;gt;SAVE&amp;lt;/code&amp;gt;, the framework will:&lt;br /&gt;
&lt;br /&gt;
# Captures the submitted data&lt;br /&gt;
# Loads the form description.&lt;br /&gt;
# Validates the data against the form description.&lt;br /&gt;
# If validation fails, stores the data in the user state and starts from the beginning.&lt;br /&gt;
# Updates the database accordingly&lt;br /&gt;
&lt;br /&gt;
The framework has placeholders for all these moments. This example plugin will hook into these placeholders, expanding the form and data with additional custom fields.&lt;br /&gt;
&lt;br /&gt;
The hooks are:&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareForm()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the form description. It will add an extra form group containing the additional fields&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the article from the database. It will inject the additional fields and their value into the article.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been successfully stored in database. It will store the additional fields in their designated database table.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been deleted (emptying of trash). It will delete the additional fields.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called before the article will be rendered/displayed. It will inject the rating values as a table at the beginning of the article, if values have been entered.&lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
The contents of the extra custom fields need to be stored in the database. Ideally would be if they have their own dedicated database table. This however, requires considerable extension overhead. With Joomla 2.5 is is possible to extend the user profile. The extra fields are stored in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;. That table is constructed and used in such a manner that it can also hold the extra article fields. [I would like to spark the discussion to evolve &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; into a generic table for expanding core tables.]&lt;br /&gt;
&lt;br /&gt;
This plugin example will use table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; to store the extra fields. It will use the tables &amp;lt;code&amp;gt;user_id&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;article_id&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Extension files ==&lt;br /&gt;
The example plugin contains the following files. It has an extra directory &amp;lt;code&amp;gt;rating&amp;lt;/code&amp;gt;. The primary reason is that it contains a XML file that is not a manifest. If it were located in the plugin top-level directory, the installer would find it and mistakenly identify it as an orphaned plugin. This would result in the form showing up when &#039;Discovering&#039; lost extensions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
── plugins/content/rating&lt;br /&gt;
   ├── language&lt;br /&gt;
   │   └── en-GB&lt;br /&gt;
   │       ├── en-GB.plg_content_rating.ini      [language file]&lt;br /&gt;
   │       └── en-GB.plg_content_rating.sys.ini  [language file]&lt;br /&gt;
   ├── rating&lt;br /&gt;
   │   ├── rating.css  [CSS for the rendered rating table]&lt;br /&gt;
   │   └── rating.xml  [Form description]&lt;br /&gt;
   ├── rating.php   [Plugin hooks]&lt;br /&gt;
   └── rating.xml   [Manifest]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manifest ===&lt;br /&gt;
A regular plugin manifest which takes an extra configuration argument, a CSS name that is tagged onto the rendered rating table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;extension version=&amp;quot;2.5&amp;quot; type=&amp;quot;plugin&amp;quot; group=&amp;quot;content&amp;quot; method=&amp;quot;upgrade&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;name&amp;gt;plg_content_rating&amp;lt;/name&amp;gt;&lt;br /&gt;
      &amp;lt;author&amp;gt;Joomla! Project&amp;lt;/author&amp;gt;&lt;br /&gt;
      &amp;lt;creationDate&amp;gt;June 2012&amp;lt;/creationDate&amp;gt;&lt;br /&gt;
      &amp;lt;copyright&amp;gt;(C) 2005 - 2012 Open Source Matters. All rights reserved.&amp;lt;/copyright&amp;gt;&lt;br /&gt;
      &amp;lt;license&amp;gt;GNU General Public License version 2 or later; see LICENSE.txt&amp;lt;/license&amp;gt;&lt;br /&gt;
      &amp;lt;authorEmail&amp;gt;admin@joomla.org&amp;lt;/authorEmail&amp;gt;&lt;br /&gt;
      &amp;lt;authorUrl&amp;gt;www.joomla.org&amp;lt;/authorUrl&amp;gt;&lt;br /&gt;
      &amp;lt;version&amp;gt;2.5.0&amp;lt;/version&amp;gt;&lt;br /&gt;
      &amp;lt;description&amp;gt;PLG_CONTENT_RATING_XML_DESCRIPTION&amp;lt;/description&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;files&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;language&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;rating&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;filename plugin=&amp;quot;rating&amp;quot;&amp;gt;rating.php&amp;lt;/filename&amp;gt;&lt;br /&gt;
         &amp;lt;filename&amp;gt;index.html&amp;lt;/filename&amp;gt;&lt;br /&gt;
      &amp;lt;/files&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;config&amp;gt;&lt;br /&gt;
         &amp;lt;fields name=&amp;quot;params&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;fieldset name=&amp;quot;basic&amp;quot;&amp;gt;&lt;br /&gt;
               &amp;lt;field&lt;br /&gt;
                  name=&amp;quot;ratingclass_sfx&amp;quot;&lt;br /&gt;
                  type=&amp;quot;text&amp;quot;&lt;br /&gt;
                  label=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL&amp;quot;&lt;br /&gt;
                  description=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC&amp;quot;&lt;br /&gt;
               /&amp;gt;&lt;br /&gt;
            &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
         &amp;lt;/fields&amp;gt;&lt;br /&gt;
      &amp;lt;/config&amp;gt;&lt;br /&gt;
   &amp;lt;/extension&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== language files ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
; Joomla! Project&lt;br /&gt;
; Copyright (C) 2005 - 2012 Open Source Matters. All rights reserved.&lt;br /&gt;
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php&lt;br /&gt;
&lt;br /&gt;
PLG_CONTENT_RATING=&amp;quot;Content - Rating&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_XML_DESCRIPTION=&amp;quot;[plg_content_rating] Example plugin on how to add custom fields (texture/temperature/taste) to articles (com_content). Adds ratings to an article, and display them as a table before the article content.&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_SLIDER_LABEL=&amp;quot;Rating Options&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL=&amp;quot;Texture&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_DESC=&amp;quot;What does the sample feel like&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL=&amp;quot;Temperature&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC=&amp;quot;What is the temperature of the sample&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_LABEL=&amp;quot;Taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_DESC=&amp;quot;How does the sample taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL=&amp;quot;Rating Class&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC=&amp;quot;Optional CSS class to add to the rating. This allows CSS styling specific to the page.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CSS ===&lt;br /&gt;
The CSS file for the rendered table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
div.rating {&lt;br /&gt;
    display: block;&lt;br /&gt;
    float: left;&lt;br /&gt;
    padding-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr td {&lt;br /&gt;
    margin: 1px !important;&lt;br /&gt;
    padding: 2px !important;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row0  {&lt;br /&gt;
    background-color:#efefef;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row1 {&lt;br /&gt;
    background-color:#ffffff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Form description ===&lt;br /&gt;
The form containing the custom fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;fields name=&amp;quot;rating&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;fieldset name=&amp;quot;rating&amp;quot; label=&amp;quot;PLG_CONTENT_RATING_SLIDER_LABEL&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;texture&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;texture&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;taste&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;taste&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
         &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
      &amp;lt;/fields&amp;gt;&lt;br /&gt;
   &amp;lt;/form&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Plugin hooks ==&lt;br /&gt;
The main file &#039;rating.php&#039; contains the hooks which are called by the Joomla framework.&lt;br /&gt;
&lt;br /&gt;
For all calls:&lt;br /&gt;
*The &amp;lt;code&amp;gt;$context&amp;lt;/code&amp;gt; parameter is a string identifying the calling placeholder.&lt;br /&gt;
*The &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; parameter is an object containing the form values&lt;br /&gt;
*The &amp;lt;code&amp;gt;$article&amp;lt;/code&amp;gt; parameter is an object (&amp;lt;code&amp;gt;JTableContent&amp;lt;/code&amp;gt;) representing the article.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;__construct&amp;lt;/code&amp;gt; ===&lt;br /&gt;
The constructor. It loads the language file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function __construct(&amp;amp; $subject, $config)&lt;br /&gt;
   {&lt;br /&gt;
      parent::__construct($subject, $config);&lt;br /&gt;
      $this-&amp;gt;loadLanguage();&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; extends the article manager form in both the front-end as the back-end. It loads the form fragment as described in &amp;lt;code&amp;gt;rating/rating.xml&amp;lt;/code&amp;gt; and merges that into the framework form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public public function onContentPrepareForm($form, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (!($form instanceof JForm))&lt;br /&gt;
      {&lt;br /&gt;
         $this-&amp;gt;_subject-&amp;gt;setError(&#039;JERROR_NOT_A_FORM&#039;);&lt;br /&gt;
         return false;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Add the extra fields to the form.&lt;br /&gt;
      JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
      $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareData&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; is executed after an article is loaded from the &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will gather and merge the values of the custom fields, which are located in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The field &amp;lt;code&amp;gt;articleId&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the article record number. That number is used to retrieve the values of the custom fields. If no record number is present, or it is zero, then a new record is being create. When this occurs, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; gets populated with the default values from the form description xml.&lt;br /&gt;
&lt;br /&gt;
It is important that when &amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; returns, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the custom fields. This is due to how the framework uses them as place holders. When a user saves a newly created article, and an error situation occurs (like some values are missing) the form data is temporarily stored in the user state. If the place holders are not present, their values will not be preserved. The user will notice this because the custom fields will get erased.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepareData($context, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (is_object($data))&lt;br /&gt;
      {&lt;br /&gt;
         $articleId = isset($data-&amp;gt;id) ? $data-&amp;gt;id : 0;&lt;br /&gt;
         if ($articleId &amp;gt; 0)&lt;br /&gt;
         {&lt;br /&gt;
            // Load the data from the database.&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;select(&#039;profile_key, profile_value&#039;);&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $query-&amp;gt;order(&#039;ordering&#039;);&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            $results = $db-&amp;gt;loadRowList();&lt;br /&gt;
&lt;br /&gt;
            // Check for a database error.&lt;br /&gt;
            if ($db-&amp;gt;getErrorNum())&lt;br /&gt;
            {&lt;br /&gt;
               $this-&amp;gt;_subject-&amp;gt;setError($db-&amp;gt;getErrorMsg());&lt;br /&gt;
               return false;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Merge the data.&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
&lt;br /&gt;
            foreach ($results as $v)&lt;br /&gt;
            {&lt;br /&gt;
               $k = str_replace(&#039;rating.&#039;, &#039;&#039;, $v[0]);&lt;br /&gt;
               $data-&amp;gt;rating[$k] = json_decode($v[1], true);&lt;br /&gt;
               if ($data-&amp;gt;rating[$k] === null)&lt;br /&gt;
               {&lt;br /&gt;
                  $data-&amp;gt;rating[$k] = $v[1];&lt;br /&gt;
               }&lt;br /&gt;
            }&lt;br /&gt;
         } else {&lt;br /&gt;
            // load the form&lt;br /&gt;
            JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
            $form = new JForm(&#039;com_content.article&#039;);&lt;br /&gt;
            $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
&lt;br /&gt;
            // Merge the default values&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
            foreach ($form-&amp;gt;getFieldset(&#039;rating&#039;) as $field) {&lt;br /&gt;
               $data-&amp;gt;rating[] = array($field-&amp;gt;fieldname, $field-&amp;gt;value);&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterSave&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt; is called after the article is stored into the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will extract the values of the custom fields and store them into a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterSave($context, &amp;amp;$article, $isNew)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId &amp;amp;&amp;amp; isset($article-&amp;gt;rating) &amp;amp;&amp;amp; (count($article-&amp;gt;rating)))&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $query-&amp;gt;clear();&lt;br /&gt;
            $query-&amp;gt;insert(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $order = 1;&lt;br /&gt;
            foreach ($article-&amp;gt;rating as $k =&amp;gt; $v)&lt;br /&gt;
            {&lt;br /&gt;
               $query-&amp;gt;values($articleId.&#039;, &#039;.$db-&amp;gt;quote(&#039;rating.&#039;.$k).&#039;, &#039;.$db-&amp;gt;quote(json_encode($v)).&#039;, &#039;.$order++);&lt;br /&gt;
            }&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterDelete&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt; is called after the article is deleted from the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will delete the linked custom fields which are stored in a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterDelete($context, $article)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId)&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete();&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query())&lt;br /&gt;
            {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepare&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt; is called when the article is being prepared for display. This is the moment where HTML can be injected into what will be displayed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$params&amp;lt;/code&amp;gt; are the article params, &amp;lt;code&amp;gt;$this-&amp;gt;params&amp;lt;/code&amp;gt; are the plugin configuration parameters as described in the manifest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepare($context, &amp;amp;$article, &amp;amp;$params, $page = 0)&lt;br /&gt;
   {&lt;br /&gt;
      if (!isset($article-&amp;gt;rating) || !count($article-&amp;gt;rating))&lt;br /&gt;
         return;&lt;br /&gt;
&lt;br /&gt;
      // add extra css for table&lt;br /&gt;
      $doc = JFactory::getDocument();&lt;br /&gt;
      $doc-&amp;gt;addStyleSheet(JURI::base(true).&#039;/plugins/content/rating/rating/rating.css&#039;);&lt;br /&gt;
       &lt;br /&gt;
      // construct a result table on the fly   &lt;br /&gt;
      jimport(&#039;joomla.html.grid&#039;);&lt;br /&gt;
      $table = new JGrid();&lt;br /&gt;
&lt;br /&gt;
      // Create columns&lt;br /&gt;
      $table-&amp;gt;addColumn(&#039;attr&#039;)&lt;br /&gt;
         -&amp;gt;addColumn(&#039;value&#039;);   &lt;br /&gt;
&lt;br /&gt;
      // populate&lt;br /&gt;
      $rownr = 0;&lt;br /&gt;
      foreach ($article-&amp;gt;rating as $attr =&amp;gt; $value) {&lt;br /&gt;
         $table-&amp;gt;addRow(array(&#039;class&#039; =&amp;gt; &#039;row&#039;.($rownr % 2)));&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;attr&#039;, $attr);&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;value&#039;, $value);&lt;br /&gt;
         $rownr++;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // wrap table in a classed &amp;lt;div&amp;gt;&lt;br /&gt;
      $suffix = $this-&amp;gt;params-&amp;gt;get(&#039;ratingclass_sfx&#039;, &#039;rating&#039;);&lt;br /&gt;
      $html = &#039;&amp;lt;div class=&amp;quot;&#039;.$suffix.&#039;&amp;quot;&amp;gt;&#039;.(string)$table.&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
      $article-&amp;gt;text = $html.$article-&amp;gt;text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
This tutorial requires at least Joomla 2.5.x [to be determined] as the framework for previous versions misses a number of plugin hook place holders.&lt;br /&gt;
&lt;br /&gt;
For Joomla 2.5 a patch file and for Joomla 2.5.6 a patch extension is available.&lt;br /&gt;
&lt;br /&gt;
This patch has been submitted as [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 Joomla patch #28871].&lt;br /&gt;
&lt;br /&gt;
== Extension Files ==&lt;br /&gt;
&lt;br /&gt;
The install zip files for the four versions can be individually downloaded from JoomlaCode.org.&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75015/patch_28771-2.5.6.patch.gz &amp;lt;code&amp;gt;patch_28771-2.5.6.patch.gz&amp;lt;/code&amp;gt;] Joomla 2.5.6 unified diff patch&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75012/patch_28771-2.5.6.zip &amp;lt;code&amp;gt;patch_28771-2.5.6.zip&amp;lt;/code&amp;gt;] Patch as Joomla 2.5.6 installable component&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75013/plg_content_rating-2.5.0.zip &amp;lt;code&amp;gt;plg_content_rating-2.5.0.zip&amp;lt;/code&amp;gt;] The plugin as described in this tutorial&lt;br /&gt;
&lt;br /&gt;
The tracker page for the patch. [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 &amp;lt;code&amp;gt;#28771&amp;lt;/code&amp;gt;]. Patch [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28770 &amp;lt;code&amp;gt;#28770&amp;lt;/code&amp;gt;] is also related.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
*[[User:znarf|Franz Korntner]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Component Development]]&lt;br /&gt;
[[Category:Development]]&lt;br /&gt;
[[category:Joomla! 2.5]]&lt;br /&gt;
[[category:Manual]]&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68092</id>
		<title>Archived:Adding custom fields to the article component</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68092"/>
		<updated>2012-06-29T21:12:22Z</updated>

		<summary type="html">&lt;p&gt;Znarf: /* language files */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This tutorial is for Joomla {{JVer|2.5}}&lt;br /&gt;
&lt;br /&gt;
== Intro ==&lt;br /&gt;
This plugin demonstrates how a component developer can use the features in Joomla 2.5 to add custom fields to the article component (com_content), without the need to change core files. Capabilities that are implemented in this demo package include:&lt;br /&gt;
&lt;br /&gt;
# Adding extra fields to the back-end article manager.&lt;br /&gt;
# Adding extra fields to the front-end article editor.&lt;br /&gt;
# Storing the values of the custom fields into a database table.&lt;br /&gt;
# Converting the field values into a HTML table which is injected into the displayed article.&lt;br /&gt;
&lt;br /&gt;
A capability which is present but not demonstrated, is integrating the values of the custom fields into other components and views, such as article lists or category blogs.&lt;br /&gt;
&lt;br /&gt;
== This example ==&lt;br /&gt;
This example adds food ratings to articles. These ratings consist of texture, temperature and taste. When present, they will be displayed as a simple table. This table is positioned before the article content.&lt;br /&gt;
[[File:Plg_content_rating-1.png|thumb|frame|none|alt=example of rendered article|An example of how the plugin places the table at the beginning of an article]]&lt;br /&gt;
&lt;br /&gt;
Setting the values of these fields can be done in both the backend and frontend.&lt;br /&gt;
[[File:Plg_content_rating-frontend.png|thumb|frame|left|alt=edit article frontend|Accessing the additional fields through the frontend]]&lt;br /&gt;
[[File:Plg_content_rating-backend.png|thumb|frame|none|alt=edit article backend|... and the backend]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Framework ==&lt;br /&gt;
Within Joomla, the article manager is basically a form driven component. Both in the backend as the frontend. When handling a form, the framework performs the following actions.&lt;br /&gt;
&lt;br /&gt;
# Loads the form description. This is a XML structure.&lt;br /&gt;
# Loads the data from the user state if present, and if not from the database.&lt;br /&gt;
# Injects the data into the form&lt;br /&gt;
# Renders/displays the form for the user to work on.&lt;br /&gt;
&lt;br /&gt;
When the user is finished and issues a &amp;lt;code&amp;gt;SAVE&amp;lt;/code&amp;gt;, the framework will:&lt;br /&gt;
&lt;br /&gt;
# Captures the submitted data&lt;br /&gt;
# Loads the form description.&lt;br /&gt;
# Validates the data against the form description.&lt;br /&gt;
# If validation fails, stores the data in the user state and starts from the beginning.&lt;br /&gt;
# Updates the database accordingly&lt;br /&gt;
&lt;br /&gt;
The framework has placeholders for all these moments. This example plugin will hook into these placeholders, expanding the form and data with additional custom fields.&lt;br /&gt;
&lt;br /&gt;
The hooks are:&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareForm()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the form description. It will add an extra form group containing the additional fields&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the article from the database. It will inject the additional fields and their value into the article.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been successfully stored in database. It will store the additional fields in their designated database table.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been deleted (emptying of trash). It will delete the additional fields.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called before the article will be rendered/displayed. It will inject the rating values as a table at the beginning of the article, if values have been entered.&lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
The contents of the extra custom fields need to be stored in the database. Ideally would be if they have their own dedicated database table. This however, requires considerable extension overhead. With Joomla 2.5 is is possible to extend the user profile. The extra fields are stored in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;. That table is constructed and used in such a manner that it can also hold the extra article fields. [I would like to spark the discussion to evolve &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; into a generic table for expanding core tables.]&lt;br /&gt;
&lt;br /&gt;
This plugin example will use table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; to store the extra fields. It will use the tables &amp;lt;code&amp;gt;user_id&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;article_id&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Extension files ==&lt;br /&gt;
The example plugin contains the following files. It has an extra directory &amp;lt;code&amp;gt;rating&amp;lt;/code&amp;gt;. The primary reason is that it contains a XML file that is not a manifest. If it were located in the plugin top-level directory, the installer would find it and mistakenly identify it as an orphaned plugin. This would result in the form showing up when &#039;Discovering&#039; lost extensions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
── plugins/content/rating&lt;br /&gt;
   ├── language&lt;br /&gt;
   │   └── en-GB&lt;br /&gt;
   │       ├── en-GB.plg_content_rating.ini      [language file]&lt;br /&gt;
   │       └── en-GB.plg_content_rating.sys.ini  [language file]&lt;br /&gt;
   ├── rating&lt;br /&gt;
   │   ├── rating.css  [CSS for the rendered rating table]&lt;br /&gt;
   │   └── rating.xml  [Form description]&lt;br /&gt;
   ├── rating.php   [Plugin hooks]&lt;br /&gt;
   └── rating.xml   [Manifest]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manifest ===&lt;br /&gt;
A regular plugin manifest which takes an extra configuration argument, a CSS name that is tagged onto the rendered rating table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;extension version=&amp;quot;2.5&amp;quot; type=&amp;quot;plugin&amp;quot; group=&amp;quot;content&amp;quot; method=&amp;quot;upgrade&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;name&amp;gt;plg_content_rating&amp;lt;/name&amp;gt;&lt;br /&gt;
      &amp;lt;author&amp;gt;Joomla! Project&amp;lt;/author&amp;gt;&lt;br /&gt;
      &amp;lt;creationDate&amp;gt;June 2012&amp;lt;/creationDate&amp;gt;&lt;br /&gt;
      &amp;lt;copyright&amp;gt;(C) 2005 - 2012 Open Source Matters. All rights reserved.&amp;lt;/copyright&amp;gt;&lt;br /&gt;
      &amp;lt;license&amp;gt;GNU General Public License version 2 or later; see LICENSE.txt&amp;lt;/license&amp;gt;&lt;br /&gt;
      &amp;lt;authorEmail&amp;gt;admin@joomla.org&amp;lt;/authorEmail&amp;gt;&lt;br /&gt;
      &amp;lt;authorUrl&amp;gt;www.joomla.org&amp;lt;/authorUrl&amp;gt;&lt;br /&gt;
      &amp;lt;version&amp;gt;2.5.0&amp;lt;/version&amp;gt;&lt;br /&gt;
      &amp;lt;description&amp;gt;PLG_CONTENT_RATING_XML_DESCRIPTION&amp;lt;/description&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;files&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;language&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;rating&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;filename plugin=&amp;quot;rating&amp;quot;&amp;gt;rating.php&amp;lt;/filename&amp;gt;&lt;br /&gt;
         &amp;lt;filename&amp;gt;index.html&amp;lt;/filename&amp;gt;&lt;br /&gt;
      &amp;lt;/files&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;config&amp;gt;&lt;br /&gt;
         &amp;lt;fields name=&amp;quot;params&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;fieldset name=&amp;quot;basic&amp;quot;&amp;gt;&lt;br /&gt;
               &amp;lt;field&lt;br /&gt;
                  name=&amp;quot;ratingclass_sfx&amp;quot;&lt;br /&gt;
                  type=&amp;quot;text&amp;quot;&lt;br /&gt;
                  label=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL&amp;quot;&lt;br /&gt;
                  description=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC&amp;quot;&lt;br /&gt;
               /&amp;gt;&lt;br /&gt;
            &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
         &amp;lt;/fields&amp;gt;&lt;br /&gt;
      &amp;lt;/config&amp;gt;&lt;br /&gt;
   &amp;lt;/extension&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== language files ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
; Joomla! Project&lt;br /&gt;
; Copyright (C) 2005 - 2012 Open Source Matters. All rights reserved.&lt;br /&gt;
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php&lt;br /&gt;
&lt;br /&gt;
PLG_CONTENT_RATING=&amp;quot;Content - Rating&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_XML_DESCRIPTION=&amp;quot;[plg_content_rating] Example plugin on how to add custom fields (texture/temperature/taste) to articles (com_content). Adds ratings to an article, and display them as a table before the article content.&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_SLIDER_LABEL=&amp;quot;Rating Options&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL=&amp;quot;Texture&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_DESC=&amp;quot;What does the sample feel like&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL=&amp;quot;Temperature&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC=&amp;quot;What is the temperature of the sample&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_LABEL=&amp;quot;Taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_DESC=&amp;quot;How does the sample taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL=&amp;quot;Rating Class&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC=&amp;quot;Optional CSS class to add to the rating. This allows CSS styling specific to the page.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CSS ===&lt;br /&gt;
The CSS file for the rendered table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
div.rating {&lt;br /&gt;
    display: block;&lt;br /&gt;
    float: left;&lt;br /&gt;
    padding-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr td {&lt;br /&gt;
    margin: 1px !important;&lt;br /&gt;
    padding: 2px !important;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row0  {&lt;br /&gt;
    background-color:#efefef;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row1 {&lt;br /&gt;
    background-color:#ffffff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Form description ===&lt;br /&gt;
The form containing the custom fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;fields name=&amp;quot;rating&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;fieldset name=&amp;quot;rating&amp;quot; label=&amp;quot;PLG_CONTENT_RATING_SLIDER_LABEL&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;texture&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;texture&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;taste&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;taste&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
         &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
      &amp;lt;/fields&amp;gt;&lt;br /&gt;
   &amp;lt;/form&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Plugin hooks ==&lt;br /&gt;
The main file &#039;rating.php&#039; contains the hooks which are called by the Joomla framework.&lt;br /&gt;
&lt;br /&gt;
For all calls:&lt;br /&gt;
*The &amp;lt;code&amp;gt;$context&amp;lt;/code&amp;gt; parameter is a string identifying the calling placeholder.&lt;br /&gt;
*The &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; parameter is an object containing the form values&lt;br /&gt;
*The &amp;lt;code&amp;gt;$article&amp;lt;/code&amp;gt; parameter is an object (&amp;lt;code&amp;gt;JTableContent&amp;lt;/code&amp;gt;) representing the article.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;__construct&amp;lt;/code&amp;gt; ===&lt;br /&gt;
The constructor. It loads the language file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function __construct(&amp;amp; $subject, $config)&lt;br /&gt;
   {&lt;br /&gt;
      parent::__construct($subject, $config);&lt;br /&gt;
      $this-&amp;gt;loadLanguage();&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; extends the article manager form in both the front-end as the back-end. It loads the form fragment as described in &amp;lt;code&amp;gt;rating/rating.xml&amp;lt;/code&amp;gt; and merges that into the framework form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public public function onContentPrepareForm($form, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (!($form instanceof JForm))&lt;br /&gt;
      {&lt;br /&gt;
         $this-&amp;gt;_subject-&amp;gt;setError(&#039;JERROR_NOT_A_FORM&#039;);&lt;br /&gt;
         return false;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Add the extra fields to the form.&lt;br /&gt;
      JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
      $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareData&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; is executed after an article is loaded from the &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will gather and merge the values of the custom fields, which are located in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The field &amp;lt;code&amp;gt;articleId&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the article record number. That number is used to retrieve the values of the custom fields. If no record number is present, or it is zero, then a new record is being create. When this occurs, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; gets populated with the default values from the form description xml.&lt;br /&gt;
&lt;br /&gt;
It is important that when &amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; returns, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the custom fields. This is due to how the framework uses them as place holders. When a user saves a newly created article, and an error situation occurs (like some values are missing) the form data is temporarily stored in the user state. If the place holders are not present, their values will not be preserved. The user will notice this because the custom fields will get erased.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepareData($context, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (is_object($data))&lt;br /&gt;
      {&lt;br /&gt;
         $articleId = isset($data-&amp;gt;id) ? $data-&amp;gt;id : 0;&lt;br /&gt;
         if ($articleId &amp;gt; 0)&lt;br /&gt;
         {&lt;br /&gt;
            // Load the data from the database.&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;select(&#039;profile_key, profile_value&#039;);&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $query-&amp;gt;order(&#039;ordering&#039;);&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            $results = $db-&amp;gt;loadRowList();&lt;br /&gt;
&lt;br /&gt;
            // Check for a database error.&lt;br /&gt;
            if ($db-&amp;gt;getErrorNum())&lt;br /&gt;
            {&lt;br /&gt;
               $this-&amp;gt;_subject-&amp;gt;setError($db-&amp;gt;getErrorMsg());&lt;br /&gt;
               return false;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Merge the data.&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
&lt;br /&gt;
            foreach ($results as $v)&lt;br /&gt;
            {&lt;br /&gt;
               $k = str_replace(&#039;rating.&#039;, &#039;&#039;, $v[0]);&lt;br /&gt;
               $data-&amp;gt;rating[$k] = json_decode($v[1], true);&lt;br /&gt;
               if ($data-&amp;gt;rating[$k] === null)&lt;br /&gt;
               {&lt;br /&gt;
                  $data-&amp;gt;rating[$k] = $v[1];&lt;br /&gt;
               }&lt;br /&gt;
            }&lt;br /&gt;
         } else {&lt;br /&gt;
            // load the form&lt;br /&gt;
            JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
            $form = new JForm(&#039;com_content.article&#039;);&lt;br /&gt;
            $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
&lt;br /&gt;
            // Merge the default values&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
            foreach ($form-&amp;gt;getFieldset(&#039;rating&#039;) as $field) {&lt;br /&gt;
               $data-&amp;gt;rating[] = array($field-&amp;gt;fieldname, $field-&amp;gt;value);&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterSave&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt; is called after the article is stored into the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will extract the values of the custom fields and store them into a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterSave($context, &amp;amp;$article, $isNew)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId &amp;amp;&amp;amp; isset($article-&amp;gt;rating) &amp;amp;&amp;amp; (count($article-&amp;gt;rating)))&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $query-&amp;gt;clear();&lt;br /&gt;
            $query-&amp;gt;insert(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $order = 1;&lt;br /&gt;
            foreach ($article-&amp;gt;rating as $k =&amp;gt; $v)&lt;br /&gt;
            {&lt;br /&gt;
               $query-&amp;gt;values($articleId.&#039;, &#039;.$db-&amp;gt;quote(&#039;rating.&#039;.$k).&#039;, &#039;.$db-&amp;gt;quote(json_encode($v)).&#039;, &#039;.$order++);&lt;br /&gt;
            }&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterDelete&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt; is called after the article is deleted from the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will delete the linked custom fields which are stored in a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterDelete($context, $article)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId)&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete();&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query())&lt;br /&gt;
            {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepare&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt; is called when the article is being prepared for display. This is the moment where HTML can be injected into what will be displayed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$params&amp;lt;/code&amp;gt; are the article params, &amp;lt;code&amp;gt;$this-&amp;gt;params&amp;lt;/code&amp;gt; are the plugin configuration parameters as described in the manifest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepare($context, &amp;amp;$article, &amp;amp;$params, $page = 0)&lt;br /&gt;
   {&lt;br /&gt;
      if (!isset($article-&amp;gt;rating) || !count($article-&amp;gt;rating))&lt;br /&gt;
         return;&lt;br /&gt;
&lt;br /&gt;
      // add extra css for table&lt;br /&gt;
      $doc = JFactory::getDocument();&lt;br /&gt;
      $doc-&amp;gt;addStyleSheet(JURI::base(true).&#039;/plugins/content/rating/rating/rating.css&#039;);&lt;br /&gt;
       &lt;br /&gt;
      // construct a result table on the fly   &lt;br /&gt;
      jimport(&#039;joomla.html.grid&#039;);&lt;br /&gt;
      $table = new JGrid();&lt;br /&gt;
&lt;br /&gt;
      // Create columns&lt;br /&gt;
      $table-&amp;gt;addColumn(&#039;attr&#039;)&lt;br /&gt;
         -&amp;gt;addColumn(&#039;value&#039;);   &lt;br /&gt;
&lt;br /&gt;
      // populate&lt;br /&gt;
      $rownr = 0;&lt;br /&gt;
      foreach ($article-&amp;gt;rating as $attr =&amp;gt; $value) {&lt;br /&gt;
         $table-&amp;gt;addRow(array(&#039;class&#039; =&amp;gt; &#039;row&#039;.($rownr % 2)));&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;attr&#039;, $attr);&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;value&#039;, $value);&lt;br /&gt;
         $rownr++;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // wrap table in a classed &amp;lt;div&amp;gt;&lt;br /&gt;
      $suffix = $this-&amp;gt;params-&amp;gt;get(&#039;ratingclass_sfx&#039;, &#039;rating&#039;);&lt;br /&gt;
      $html = &#039;&amp;lt;div class=&amp;quot;&#039;.$suffix.&#039;&amp;quot;&amp;gt;&#039;.(string)$table.&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
      $article-&amp;gt;text = $html.$article-&amp;gt;text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
This tutorial requires at least Joomla 2.5.x [to be determined] as the framework for previous versions misses a number of plugin hook place holders.&lt;br /&gt;
&lt;br /&gt;
For Joomla 2.5 a patch file and for Joomla 2.5.6 a patch extension is available.&lt;br /&gt;
&lt;br /&gt;
This patch has been submitted as [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 Joomla patch #28871].&lt;br /&gt;
&lt;br /&gt;
== Extension Files ==&lt;br /&gt;
&lt;br /&gt;
The install zip files for the four versions can be individually downloaded from JoomlaCode.org.&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75015/patch_28771-2.5.6.patch.gz &amp;lt;code&amp;gt;jpatch_28771-2.5.6.patch.gz&amp;lt;/code&amp;gt;] Joomla 2.5.6 unified diff patch&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75012/patch_28771-2.5.6.zip &amp;lt;code&amp;gt;patch_28771-2.5.6.zip&amp;lt;/code&amp;gt;] Patch as Joomla 2.5.6 installable component&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75013/plg_content_rating-2.5.0.zip &amp;lt;code&amp;gt;plg_content_rating-2.5.0.zip&amp;lt;/code&amp;gt;] The plugin as described in this tutorial&lt;br /&gt;
&lt;br /&gt;
The tracker page for the patch. [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 &amp;lt;code&amp;gt;#28771&amp;lt;/code&amp;gt;]. Patch [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28770 &amp;lt;code&amp;gt;#28770&amp;lt;/code&amp;gt;] is also related.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
*[[User:znarf|Franz Korntner]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Component Development]]&lt;br /&gt;
[[Category:Development]]&lt;br /&gt;
[[category:Joomla! 2.5]]&lt;br /&gt;
[[category:Manual]]&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68091</id>
		<title>Archived:Adding custom fields to the article component</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68091"/>
		<updated>2012-06-29T21:10:46Z</updated>

		<summary type="html">&lt;p&gt;Znarf: /* Intro */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This tutorial is for Joomla {{JVer|2.5}}&lt;br /&gt;
&lt;br /&gt;
== Intro ==&lt;br /&gt;
This plugin demonstrates how a component developer can use the features in Joomla 2.5 to add custom fields to the article component (com_content), without the need to change core files. Capabilities that are implemented in this demo package include:&lt;br /&gt;
&lt;br /&gt;
# Adding extra fields to the back-end article manager.&lt;br /&gt;
# Adding extra fields to the front-end article editor.&lt;br /&gt;
# Storing the values of the custom fields into a database table.&lt;br /&gt;
# Converting the field values into a HTML table which is injected into the displayed article.&lt;br /&gt;
&lt;br /&gt;
A capability which is present but not demonstrated, is integrating the values of the custom fields into other components and views, such as article lists or category blogs.&lt;br /&gt;
&lt;br /&gt;
== This example ==&lt;br /&gt;
This example adds food ratings to articles. These ratings consist of texture, temperature and taste. When present, they will be displayed as a simple table. This table is positioned before the article content.&lt;br /&gt;
[[File:Plg_content_rating-1.png|thumb|frame|none|alt=example of rendered article|An example of how the plugin places the table at the beginning of an article]]&lt;br /&gt;
&lt;br /&gt;
Setting the values of these fields can be done in both the backend and frontend.&lt;br /&gt;
[[File:Plg_content_rating-frontend.png|thumb|frame|left|alt=edit article frontend|Accessing the additional fields through the frontend]]&lt;br /&gt;
[[File:Plg_content_rating-backend.png|thumb|frame|none|alt=edit article backend|... and the backend]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Framework ==&lt;br /&gt;
Within Joomla, the article manager is basically a form driven component. Both in the backend as the frontend. When handling a form, the framework performs the following actions.&lt;br /&gt;
&lt;br /&gt;
# Loads the form description. This is a XML structure.&lt;br /&gt;
# Loads the data from the user state if present, and if not from the database.&lt;br /&gt;
# Injects the data into the form&lt;br /&gt;
# Renders/displays the form for the user to work on.&lt;br /&gt;
&lt;br /&gt;
When the user is finished and issues a &amp;lt;code&amp;gt;SAVE&amp;lt;/code&amp;gt;, the framework will:&lt;br /&gt;
&lt;br /&gt;
# Captures the submitted data&lt;br /&gt;
# Loads the form description.&lt;br /&gt;
# Validates the data against the form description.&lt;br /&gt;
# If validation fails, stores the data in the user state and starts from the beginning.&lt;br /&gt;
# Updates the database accordingly&lt;br /&gt;
&lt;br /&gt;
The framework has placeholders for all these moments. This example plugin will hook into these placeholders, expanding the form and data with additional custom fields.&lt;br /&gt;
&lt;br /&gt;
The hooks are:&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareForm()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the form description. It will add an extra form group containing the additional fields&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the article from the database. It will inject the additional fields and their value into the article.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been successfully stored in database. It will store the additional fields in their designated database table.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been deleted (emptying of trash). It will delete the additional fields.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called before the article will be rendered/displayed. It will inject the rating values as a table at the beginning of the article, if values have been entered.&lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
The contents of the extra custom fields need to be stored in the database. Ideally would be if they have their own dedicated database table. This however, requires considerable extension overhead. With Joomla 2.5 is is possible to extend the user profile. The extra fields are stored in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;. That table is constructed and used in such a manner that it can also hold the extra article fields. [I would like to spark the discussion to evolve &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; into a generic table for expanding core tables.]&lt;br /&gt;
&lt;br /&gt;
This plugin example will use table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; to store the extra fields. It will use the tables &amp;lt;code&amp;gt;user_id&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;article_id&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Extension files ==&lt;br /&gt;
The example plugin contains the following files. It has an extra directory &amp;lt;code&amp;gt;rating&amp;lt;/code&amp;gt;. The primary reason is that it contains a XML file that is not a manifest. If it were located in the plugin top-level directory, the installer would find it and mistakenly identify it as an orphaned plugin. This would result in the form showing up when &#039;Discovering&#039; lost extensions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
── plugins/content/rating&lt;br /&gt;
   ├── language&lt;br /&gt;
   │   └── en-GB&lt;br /&gt;
   │       ├── en-GB.plg_content_rating.ini      [language file]&lt;br /&gt;
   │       └── en-GB.plg_content_rating.sys.ini  [language file]&lt;br /&gt;
   ├── rating&lt;br /&gt;
   │   ├── rating.css  [CSS for the rendered rating table]&lt;br /&gt;
   │   └── rating.xml  [Form description]&lt;br /&gt;
   ├── rating.php   [Plugin hooks]&lt;br /&gt;
   └── rating.xml   [Manifest]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manifest ===&lt;br /&gt;
A regular plugin manifest which takes an extra configuration argument, a CSS name that is tagged onto the rendered rating table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;extension version=&amp;quot;2.5&amp;quot; type=&amp;quot;plugin&amp;quot; group=&amp;quot;content&amp;quot; method=&amp;quot;upgrade&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;name&amp;gt;plg_content_rating&amp;lt;/name&amp;gt;&lt;br /&gt;
      &amp;lt;author&amp;gt;Joomla! Project&amp;lt;/author&amp;gt;&lt;br /&gt;
      &amp;lt;creationDate&amp;gt;June 2012&amp;lt;/creationDate&amp;gt;&lt;br /&gt;
      &amp;lt;copyright&amp;gt;(C) 2005 - 2012 Open Source Matters. All rights reserved.&amp;lt;/copyright&amp;gt;&lt;br /&gt;
      &amp;lt;license&amp;gt;GNU General Public License version 2 or later; see LICENSE.txt&amp;lt;/license&amp;gt;&lt;br /&gt;
      &amp;lt;authorEmail&amp;gt;admin@joomla.org&amp;lt;/authorEmail&amp;gt;&lt;br /&gt;
      &amp;lt;authorUrl&amp;gt;www.joomla.org&amp;lt;/authorUrl&amp;gt;&lt;br /&gt;
      &amp;lt;version&amp;gt;2.5.0&amp;lt;/version&amp;gt;&lt;br /&gt;
      &amp;lt;description&amp;gt;PLG_CONTENT_RATING_XML_DESCRIPTION&amp;lt;/description&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;files&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;language&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;rating&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;filename plugin=&amp;quot;rating&amp;quot;&amp;gt;rating.php&amp;lt;/filename&amp;gt;&lt;br /&gt;
         &amp;lt;filename&amp;gt;index.html&amp;lt;/filename&amp;gt;&lt;br /&gt;
      &amp;lt;/files&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;config&amp;gt;&lt;br /&gt;
         &amp;lt;fields name=&amp;quot;params&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;fieldset name=&amp;quot;basic&amp;quot;&amp;gt;&lt;br /&gt;
               &amp;lt;field&lt;br /&gt;
                  name=&amp;quot;ratingclass_sfx&amp;quot;&lt;br /&gt;
                  type=&amp;quot;text&amp;quot;&lt;br /&gt;
                  label=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL&amp;quot;&lt;br /&gt;
                  description=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC&amp;quot;&lt;br /&gt;
               /&amp;gt;&lt;br /&gt;
            &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
         &amp;lt;/fields&amp;gt;&lt;br /&gt;
      &amp;lt;/config&amp;gt;&lt;br /&gt;
   &amp;lt;/extension&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== language files ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
; Joomla! Project&lt;br /&gt;
; Copyright (C) 2005 - 2012 Open Source Matters. All rights reserved.&lt;br /&gt;
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php&lt;br /&gt;
&lt;br /&gt;
PLG_CONTENT_RATING=&amp;quot;Content - Rating&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_XML_DESCRIPTION=&amp;quot;[plg_content_rating] Example plugin on how to add custom fields (texture/temperature/taste) to articles (com_content). Adds ratings to an article, and display them as a table before the article content.&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_SLIDER_LABEL=&amp;quot;Showcase Options&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL=&amp;quot;Texture&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_DESC=&amp;quot;What does the sample feel like&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL=&amp;quot;Temperature&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC=&amp;quot;What is the temperature of the sample&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_LABEL=&amp;quot;Taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_DESC=&amp;quot;How does the sample taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL=&amp;quot;Rating Class&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC=&amp;quot;Optional CSS class to add to the rating. This allows CSS styling specific to the page.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CSS ===&lt;br /&gt;
The CSS file for the rendered table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
div.rating {&lt;br /&gt;
    display: block;&lt;br /&gt;
    float: left;&lt;br /&gt;
    padding-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr td {&lt;br /&gt;
    margin: 1px !important;&lt;br /&gt;
    padding: 2px !important;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row0  {&lt;br /&gt;
    background-color:#efefef;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row1 {&lt;br /&gt;
    background-color:#ffffff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Form description ===&lt;br /&gt;
The form containing the custom fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;fields name=&amp;quot;rating&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;fieldset name=&amp;quot;rating&amp;quot; label=&amp;quot;PLG_CONTENT_RATING_SLIDER_LABEL&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;texture&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;texture&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;taste&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;taste&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
         &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
      &amp;lt;/fields&amp;gt;&lt;br /&gt;
   &amp;lt;/form&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Plugin hooks ==&lt;br /&gt;
The main file &#039;rating.php&#039; contains the hooks which are called by the Joomla framework.&lt;br /&gt;
&lt;br /&gt;
For all calls:&lt;br /&gt;
*The &amp;lt;code&amp;gt;$context&amp;lt;/code&amp;gt; parameter is a string identifying the calling placeholder.&lt;br /&gt;
*The &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; parameter is an object containing the form values&lt;br /&gt;
*The &amp;lt;code&amp;gt;$article&amp;lt;/code&amp;gt; parameter is an object (&amp;lt;code&amp;gt;JTableContent&amp;lt;/code&amp;gt;) representing the article.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;__construct&amp;lt;/code&amp;gt; ===&lt;br /&gt;
The constructor. It loads the language file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function __construct(&amp;amp; $subject, $config)&lt;br /&gt;
   {&lt;br /&gt;
      parent::__construct($subject, $config);&lt;br /&gt;
      $this-&amp;gt;loadLanguage();&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; extends the article manager form in both the front-end as the back-end. It loads the form fragment as described in &amp;lt;code&amp;gt;rating/rating.xml&amp;lt;/code&amp;gt; and merges that into the framework form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public public function onContentPrepareForm($form, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (!($form instanceof JForm))&lt;br /&gt;
      {&lt;br /&gt;
         $this-&amp;gt;_subject-&amp;gt;setError(&#039;JERROR_NOT_A_FORM&#039;);&lt;br /&gt;
         return false;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Add the extra fields to the form.&lt;br /&gt;
      JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
      $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareData&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; is executed after an article is loaded from the &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will gather and merge the values of the custom fields, which are located in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The field &amp;lt;code&amp;gt;articleId&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the article record number. That number is used to retrieve the values of the custom fields. If no record number is present, or it is zero, then a new record is being create. When this occurs, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; gets populated with the default values from the form description xml.&lt;br /&gt;
&lt;br /&gt;
It is important that when &amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; returns, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the custom fields. This is due to how the framework uses them as place holders. When a user saves a newly created article, and an error situation occurs (like some values are missing) the form data is temporarily stored in the user state. If the place holders are not present, their values will not be preserved. The user will notice this because the custom fields will get erased.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepareData($context, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (is_object($data))&lt;br /&gt;
      {&lt;br /&gt;
         $articleId = isset($data-&amp;gt;id) ? $data-&amp;gt;id : 0;&lt;br /&gt;
         if ($articleId &amp;gt; 0)&lt;br /&gt;
         {&lt;br /&gt;
            // Load the data from the database.&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;select(&#039;profile_key, profile_value&#039;);&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $query-&amp;gt;order(&#039;ordering&#039;);&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            $results = $db-&amp;gt;loadRowList();&lt;br /&gt;
&lt;br /&gt;
            // Check for a database error.&lt;br /&gt;
            if ($db-&amp;gt;getErrorNum())&lt;br /&gt;
            {&lt;br /&gt;
               $this-&amp;gt;_subject-&amp;gt;setError($db-&amp;gt;getErrorMsg());&lt;br /&gt;
               return false;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Merge the data.&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
&lt;br /&gt;
            foreach ($results as $v)&lt;br /&gt;
            {&lt;br /&gt;
               $k = str_replace(&#039;rating.&#039;, &#039;&#039;, $v[0]);&lt;br /&gt;
               $data-&amp;gt;rating[$k] = json_decode($v[1], true);&lt;br /&gt;
               if ($data-&amp;gt;rating[$k] === null)&lt;br /&gt;
               {&lt;br /&gt;
                  $data-&amp;gt;rating[$k] = $v[1];&lt;br /&gt;
               }&lt;br /&gt;
            }&lt;br /&gt;
         } else {&lt;br /&gt;
            // load the form&lt;br /&gt;
            JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
            $form = new JForm(&#039;com_content.article&#039;);&lt;br /&gt;
            $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
&lt;br /&gt;
            // Merge the default values&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
            foreach ($form-&amp;gt;getFieldset(&#039;rating&#039;) as $field) {&lt;br /&gt;
               $data-&amp;gt;rating[] = array($field-&amp;gt;fieldname, $field-&amp;gt;value);&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterSave&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt; is called after the article is stored into the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will extract the values of the custom fields and store them into a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterSave($context, &amp;amp;$article, $isNew)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId &amp;amp;&amp;amp; isset($article-&amp;gt;rating) &amp;amp;&amp;amp; (count($article-&amp;gt;rating)))&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $query-&amp;gt;clear();&lt;br /&gt;
            $query-&amp;gt;insert(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $order = 1;&lt;br /&gt;
            foreach ($article-&amp;gt;rating as $k =&amp;gt; $v)&lt;br /&gt;
            {&lt;br /&gt;
               $query-&amp;gt;values($articleId.&#039;, &#039;.$db-&amp;gt;quote(&#039;rating.&#039;.$k).&#039;, &#039;.$db-&amp;gt;quote(json_encode($v)).&#039;, &#039;.$order++);&lt;br /&gt;
            }&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterDelete&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt; is called after the article is deleted from the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will delete the linked custom fields which are stored in a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterDelete($context, $article)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId)&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete();&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query())&lt;br /&gt;
            {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepare&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt; is called when the article is being prepared for display. This is the moment where HTML can be injected into what will be displayed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$params&amp;lt;/code&amp;gt; are the article params, &amp;lt;code&amp;gt;$this-&amp;gt;params&amp;lt;/code&amp;gt; are the plugin configuration parameters as described in the manifest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepare($context, &amp;amp;$article, &amp;amp;$params, $page = 0)&lt;br /&gt;
   {&lt;br /&gt;
      if (!isset($article-&amp;gt;rating) || !count($article-&amp;gt;rating))&lt;br /&gt;
         return;&lt;br /&gt;
&lt;br /&gt;
      // add extra css for table&lt;br /&gt;
      $doc = JFactory::getDocument();&lt;br /&gt;
      $doc-&amp;gt;addStyleSheet(JURI::base(true).&#039;/plugins/content/rating/rating/rating.css&#039;);&lt;br /&gt;
       &lt;br /&gt;
      // construct a result table on the fly   &lt;br /&gt;
      jimport(&#039;joomla.html.grid&#039;);&lt;br /&gt;
      $table = new JGrid();&lt;br /&gt;
&lt;br /&gt;
      // Create columns&lt;br /&gt;
      $table-&amp;gt;addColumn(&#039;attr&#039;)&lt;br /&gt;
         -&amp;gt;addColumn(&#039;value&#039;);   &lt;br /&gt;
&lt;br /&gt;
      // populate&lt;br /&gt;
      $rownr = 0;&lt;br /&gt;
      foreach ($article-&amp;gt;rating as $attr =&amp;gt; $value) {&lt;br /&gt;
         $table-&amp;gt;addRow(array(&#039;class&#039; =&amp;gt; &#039;row&#039;.($rownr % 2)));&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;attr&#039;, $attr);&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;value&#039;, $value);&lt;br /&gt;
         $rownr++;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // wrap table in a classed &amp;lt;div&amp;gt;&lt;br /&gt;
      $suffix = $this-&amp;gt;params-&amp;gt;get(&#039;ratingclass_sfx&#039;, &#039;rating&#039;);&lt;br /&gt;
      $html = &#039;&amp;lt;div class=&amp;quot;&#039;.$suffix.&#039;&amp;quot;&amp;gt;&#039;.(string)$table.&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
      $article-&amp;gt;text = $html.$article-&amp;gt;text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
This tutorial requires at least Joomla 2.5.x [to be determined] as the framework for previous versions misses a number of plugin hook place holders.&lt;br /&gt;
&lt;br /&gt;
For Joomla 2.5 a patch file and for Joomla 2.5.6 a patch extension is available.&lt;br /&gt;
&lt;br /&gt;
This patch has been submitted as [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 Joomla patch #28871].&lt;br /&gt;
&lt;br /&gt;
== Extension Files ==&lt;br /&gt;
&lt;br /&gt;
The install zip files for the four versions can be individually downloaded from JoomlaCode.org.&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75015/patch_28771-2.5.6.patch.gz &amp;lt;code&amp;gt;jpatch_28771-2.5.6.patch.gz&amp;lt;/code&amp;gt;] Joomla 2.5.6 unified diff patch&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75012/patch_28771-2.5.6.zip &amp;lt;code&amp;gt;patch_28771-2.5.6.zip&amp;lt;/code&amp;gt;] Patch as Joomla 2.5.6 installable component&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75013/plg_content_rating-2.5.0.zip &amp;lt;code&amp;gt;plg_content_rating-2.5.0.zip&amp;lt;/code&amp;gt;] The plugin as described in this tutorial&lt;br /&gt;
&lt;br /&gt;
The tracker page for the patch. [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 &amp;lt;code&amp;gt;#28771&amp;lt;/code&amp;gt;]. Patch [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28770 &amp;lt;code&amp;gt;#28770&amp;lt;/code&amp;gt;] is also related.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
*[[User:znarf|Franz Korntner]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Component Development]]&lt;br /&gt;
[[Category:Development]]&lt;br /&gt;
[[category:Joomla! 2.5]]&lt;br /&gt;
[[category:Manual]]&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68090</id>
		<title>Archived:Adding custom fields to the article component</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68090"/>
		<updated>2012-06-29T21:05:29Z</updated>

		<summary type="html">&lt;p&gt;Znarf: /* Prerequisites */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This tutorial is for Joomla {{JVer|2.5}}&lt;br /&gt;
&lt;br /&gt;
== Intro ==&lt;br /&gt;
This plugin demonstrates how a component developer can use the features in Joomla 2.5 to add custom fields to the article component (com_content), without the need to change core files. Capabilities that are implemented in this demo package include:&lt;br /&gt;
# Adding extra fields to the back-end article manager.&lt;br /&gt;
# Adding extra fields to the front-end article editor.&lt;br /&gt;
# Storing the editor supplied field values into a database table.&lt;br /&gt;
# Converting the field values into HTML which is injected into the displayed article.&lt;br /&gt;
A capability which is present but not demonstrated, is integrating the values of the custom fields into other components and views, such as article lists or category blogs.&lt;br /&gt;
&lt;br /&gt;
== This example ==&lt;br /&gt;
This example adds food ratings to articles. These ratings consist of texture, temperature and taste. When present, they will be displayed as a simple table. This table is positioned before the article content.&lt;br /&gt;
[[File:Plg_content_rating-1.png|thumb|frame|none|alt=example of rendered article|An example of how the plugin places the table at the beginning of an article]]&lt;br /&gt;
&lt;br /&gt;
Setting the values of these fields can be done in both the backend and frontend.&lt;br /&gt;
[[File:Plg_content_rating-frontend.png|thumb|frame|left|alt=edit article frontend|Accessing the additional fields through the frontend]]&lt;br /&gt;
[[File:Plg_content_rating-backend.png|thumb|frame|none|alt=edit article backend|... and the backend]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Framework ==&lt;br /&gt;
Within Joomla, the article manager is basically a form driven component. Both in the backend as the frontend. When handling a form, the framework performs the following actions.&lt;br /&gt;
&lt;br /&gt;
# Loads the form description. This is a XML structure.&lt;br /&gt;
# Loads the data from the user state if present, and if not from the database.&lt;br /&gt;
# Injects the data into the form&lt;br /&gt;
# Renders/displays the form for the user to work on.&lt;br /&gt;
&lt;br /&gt;
When the user is finished and issues a &amp;lt;code&amp;gt;SAVE&amp;lt;/code&amp;gt;, the framework will:&lt;br /&gt;
&lt;br /&gt;
# Captures the submitted data&lt;br /&gt;
# Loads the form description.&lt;br /&gt;
# Validates the data against the form description.&lt;br /&gt;
# If validation fails, stores the data in the user state and starts from the beginning.&lt;br /&gt;
# Updates the database accordingly&lt;br /&gt;
&lt;br /&gt;
The framework has placeholders for all these moments. This example plugin will hook into these placeholders, expanding the form and data with additional custom fields.&lt;br /&gt;
&lt;br /&gt;
The hooks are:&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareForm()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the form description. It will add an extra form group containing the additional fields&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the article from the database. It will inject the additional fields and their value into the article.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been successfully stored in database. It will store the additional fields in their designated database table.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been deleted (emptying of trash). It will delete the additional fields.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called before the article will be rendered/displayed. It will inject the rating values as a table at the beginning of the article, if values have been entered.&lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
The contents of the extra custom fields need to be stored in the database. Ideally would be if they have their own dedicated database table. This however, requires considerable extension overhead. With Joomla 2.5 is is possible to extend the user profile. The extra fields are stored in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;. That table is constructed and used in such a manner that it can also hold the extra article fields. [I would like to spark the discussion to evolve &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; into a generic table for expanding core tables.]&lt;br /&gt;
&lt;br /&gt;
This plugin example will use table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; to store the extra fields. It will use the tables &amp;lt;code&amp;gt;user_id&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;article_id&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Extension files ==&lt;br /&gt;
The example plugin contains the following files. It has an extra directory &amp;lt;code&amp;gt;rating&amp;lt;/code&amp;gt;. The primary reason is that it contains a XML file that is not a manifest. If it were located in the plugin top-level directory, the installer would find it and mistakenly identify it as an orphaned plugin. This would result in the form showing up when &#039;Discovering&#039; lost extensions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
── plugins/content/rating&lt;br /&gt;
   ├── language&lt;br /&gt;
   │   └── en-GB&lt;br /&gt;
   │       ├── en-GB.plg_content_rating.ini      [language file]&lt;br /&gt;
   │       └── en-GB.plg_content_rating.sys.ini  [language file]&lt;br /&gt;
   ├── rating&lt;br /&gt;
   │   ├── rating.css  [CSS for the rendered rating table]&lt;br /&gt;
   │   └── rating.xml  [Form description]&lt;br /&gt;
   ├── rating.php   [Plugin hooks]&lt;br /&gt;
   └── rating.xml   [Manifest]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manifest ===&lt;br /&gt;
A regular plugin manifest which takes an extra configuration argument, a CSS name that is tagged onto the rendered rating table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;extension version=&amp;quot;2.5&amp;quot; type=&amp;quot;plugin&amp;quot; group=&amp;quot;content&amp;quot; method=&amp;quot;upgrade&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;name&amp;gt;plg_content_rating&amp;lt;/name&amp;gt;&lt;br /&gt;
      &amp;lt;author&amp;gt;Joomla! Project&amp;lt;/author&amp;gt;&lt;br /&gt;
      &amp;lt;creationDate&amp;gt;June 2012&amp;lt;/creationDate&amp;gt;&lt;br /&gt;
      &amp;lt;copyright&amp;gt;(C) 2005 - 2012 Open Source Matters. All rights reserved.&amp;lt;/copyright&amp;gt;&lt;br /&gt;
      &amp;lt;license&amp;gt;GNU General Public License version 2 or later; see LICENSE.txt&amp;lt;/license&amp;gt;&lt;br /&gt;
      &amp;lt;authorEmail&amp;gt;admin@joomla.org&amp;lt;/authorEmail&amp;gt;&lt;br /&gt;
      &amp;lt;authorUrl&amp;gt;www.joomla.org&amp;lt;/authorUrl&amp;gt;&lt;br /&gt;
      &amp;lt;version&amp;gt;2.5.0&amp;lt;/version&amp;gt;&lt;br /&gt;
      &amp;lt;description&amp;gt;PLG_CONTENT_RATING_XML_DESCRIPTION&amp;lt;/description&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;files&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;language&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;rating&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;filename plugin=&amp;quot;rating&amp;quot;&amp;gt;rating.php&amp;lt;/filename&amp;gt;&lt;br /&gt;
         &amp;lt;filename&amp;gt;index.html&amp;lt;/filename&amp;gt;&lt;br /&gt;
      &amp;lt;/files&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;config&amp;gt;&lt;br /&gt;
         &amp;lt;fields name=&amp;quot;params&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;fieldset name=&amp;quot;basic&amp;quot;&amp;gt;&lt;br /&gt;
               &amp;lt;field&lt;br /&gt;
                  name=&amp;quot;ratingclass_sfx&amp;quot;&lt;br /&gt;
                  type=&amp;quot;text&amp;quot;&lt;br /&gt;
                  label=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL&amp;quot;&lt;br /&gt;
                  description=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC&amp;quot;&lt;br /&gt;
               /&amp;gt;&lt;br /&gt;
            &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
         &amp;lt;/fields&amp;gt;&lt;br /&gt;
      &amp;lt;/config&amp;gt;&lt;br /&gt;
   &amp;lt;/extension&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== language files ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
; Joomla! Project&lt;br /&gt;
; Copyright (C) 2005 - 2012 Open Source Matters. All rights reserved.&lt;br /&gt;
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php&lt;br /&gt;
&lt;br /&gt;
PLG_CONTENT_RATING=&amp;quot;Content - Rating&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_XML_DESCRIPTION=&amp;quot;[plg_content_rating] Example plugin on how to add custom fields (texture/temperature/taste) to articles (com_content). Adds ratings to an article, and display them as a table before the article content.&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_SLIDER_LABEL=&amp;quot;Showcase Options&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL=&amp;quot;Texture&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_DESC=&amp;quot;What does the sample feel like&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL=&amp;quot;Temperature&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC=&amp;quot;What is the temperature of the sample&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_LABEL=&amp;quot;Taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_DESC=&amp;quot;How does the sample taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL=&amp;quot;Rating Class&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC=&amp;quot;Optional CSS class to add to the rating. This allows CSS styling specific to the page.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CSS ===&lt;br /&gt;
The CSS file for the rendered table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
div.rating {&lt;br /&gt;
    display: block;&lt;br /&gt;
    float: left;&lt;br /&gt;
    padding-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr td {&lt;br /&gt;
    margin: 1px !important;&lt;br /&gt;
    padding: 2px !important;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row0  {&lt;br /&gt;
    background-color:#efefef;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row1 {&lt;br /&gt;
    background-color:#ffffff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Form description ===&lt;br /&gt;
The form containing the custom fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;fields name=&amp;quot;rating&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;fieldset name=&amp;quot;rating&amp;quot; label=&amp;quot;PLG_CONTENT_RATING_SLIDER_LABEL&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;texture&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;texture&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;taste&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;taste&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
         &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
      &amp;lt;/fields&amp;gt;&lt;br /&gt;
   &amp;lt;/form&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Plugin hooks ==&lt;br /&gt;
The main file &#039;rating.php&#039; contains the hooks which are called by the Joomla framework.&lt;br /&gt;
&lt;br /&gt;
For all calls:&lt;br /&gt;
*The &amp;lt;code&amp;gt;$context&amp;lt;/code&amp;gt; parameter is a string identifying the calling placeholder.&lt;br /&gt;
*The &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; parameter is an object containing the form values&lt;br /&gt;
*The &amp;lt;code&amp;gt;$article&amp;lt;/code&amp;gt; parameter is an object (&amp;lt;code&amp;gt;JTableContent&amp;lt;/code&amp;gt;) representing the article.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;__construct&amp;lt;/code&amp;gt; ===&lt;br /&gt;
The constructor. It loads the language file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function __construct(&amp;amp; $subject, $config)&lt;br /&gt;
   {&lt;br /&gt;
      parent::__construct($subject, $config);&lt;br /&gt;
      $this-&amp;gt;loadLanguage();&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; extends the article manager form in both the front-end as the back-end. It loads the form fragment as described in &amp;lt;code&amp;gt;rating/rating.xml&amp;lt;/code&amp;gt; and merges that into the framework form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public public function onContentPrepareForm($form, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (!($form instanceof JForm))&lt;br /&gt;
      {&lt;br /&gt;
         $this-&amp;gt;_subject-&amp;gt;setError(&#039;JERROR_NOT_A_FORM&#039;);&lt;br /&gt;
         return false;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Add the extra fields to the form.&lt;br /&gt;
      JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
      $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareData&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; is executed after an article is loaded from the &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will gather and merge the values of the custom fields, which are located in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The field &amp;lt;code&amp;gt;articleId&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the article record number. That number is used to retrieve the values of the custom fields. If no record number is present, or it is zero, then a new record is being create. When this occurs, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; gets populated with the default values from the form description xml.&lt;br /&gt;
&lt;br /&gt;
It is important that when &amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; returns, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the custom fields. This is due to how the framework uses them as place holders. When a user saves a newly created article, and an error situation occurs (like some values are missing) the form data is temporarily stored in the user state. If the place holders are not present, their values will not be preserved. The user will notice this because the custom fields will get erased.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepareData($context, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (is_object($data))&lt;br /&gt;
      {&lt;br /&gt;
         $articleId = isset($data-&amp;gt;id) ? $data-&amp;gt;id : 0;&lt;br /&gt;
         if ($articleId &amp;gt; 0)&lt;br /&gt;
         {&lt;br /&gt;
            // Load the data from the database.&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;select(&#039;profile_key, profile_value&#039;);&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $query-&amp;gt;order(&#039;ordering&#039;);&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            $results = $db-&amp;gt;loadRowList();&lt;br /&gt;
&lt;br /&gt;
            // Check for a database error.&lt;br /&gt;
            if ($db-&amp;gt;getErrorNum())&lt;br /&gt;
            {&lt;br /&gt;
               $this-&amp;gt;_subject-&amp;gt;setError($db-&amp;gt;getErrorMsg());&lt;br /&gt;
               return false;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Merge the data.&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
&lt;br /&gt;
            foreach ($results as $v)&lt;br /&gt;
            {&lt;br /&gt;
               $k = str_replace(&#039;rating.&#039;, &#039;&#039;, $v[0]);&lt;br /&gt;
               $data-&amp;gt;rating[$k] = json_decode($v[1], true);&lt;br /&gt;
               if ($data-&amp;gt;rating[$k] === null)&lt;br /&gt;
               {&lt;br /&gt;
                  $data-&amp;gt;rating[$k] = $v[1];&lt;br /&gt;
               }&lt;br /&gt;
            }&lt;br /&gt;
         } else {&lt;br /&gt;
            // load the form&lt;br /&gt;
            JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
            $form = new JForm(&#039;com_content.article&#039;);&lt;br /&gt;
            $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
&lt;br /&gt;
            // Merge the default values&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
            foreach ($form-&amp;gt;getFieldset(&#039;rating&#039;) as $field) {&lt;br /&gt;
               $data-&amp;gt;rating[] = array($field-&amp;gt;fieldname, $field-&amp;gt;value);&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterSave&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt; is called after the article is stored into the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will extract the values of the custom fields and store them into a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterSave($context, &amp;amp;$article, $isNew)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId &amp;amp;&amp;amp; isset($article-&amp;gt;rating) &amp;amp;&amp;amp; (count($article-&amp;gt;rating)))&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $query-&amp;gt;clear();&lt;br /&gt;
            $query-&amp;gt;insert(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $order = 1;&lt;br /&gt;
            foreach ($article-&amp;gt;rating as $k =&amp;gt; $v)&lt;br /&gt;
            {&lt;br /&gt;
               $query-&amp;gt;values($articleId.&#039;, &#039;.$db-&amp;gt;quote(&#039;rating.&#039;.$k).&#039;, &#039;.$db-&amp;gt;quote(json_encode($v)).&#039;, &#039;.$order++);&lt;br /&gt;
            }&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterDelete&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt; is called after the article is deleted from the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will delete the linked custom fields which are stored in a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterDelete($context, $article)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId)&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete();&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query())&lt;br /&gt;
            {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepare&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt; is called when the article is being prepared for display. This is the moment where HTML can be injected into what will be displayed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$params&amp;lt;/code&amp;gt; are the article params, &amp;lt;code&amp;gt;$this-&amp;gt;params&amp;lt;/code&amp;gt; are the plugin configuration parameters as described in the manifest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepare($context, &amp;amp;$article, &amp;amp;$params, $page = 0)&lt;br /&gt;
   {&lt;br /&gt;
      if (!isset($article-&amp;gt;rating) || !count($article-&amp;gt;rating))&lt;br /&gt;
         return;&lt;br /&gt;
&lt;br /&gt;
      // add extra css for table&lt;br /&gt;
      $doc = JFactory::getDocument();&lt;br /&gt;
      $doc-&amp;gt;addStyleSheet(JURI::base(true).&#039;/plugins/content/rating/rating/rating.css&#039;);&lt;br /&gt;
       &lt;br /&gt;
      // construct a result table on the fly   &lt;br /&gt;
      jimport(&#039;joomla.html.grid&#039;);&lt;br /&gt;
      $table = new JGrid();&lt;br /&gt;
&lt;br /&gt;
      // Create columns&lt;br /&gt;
      $table-&amp;gt;addColumn(&#039;attr&#039;)&lt;br /&gt;
         -&amp;gt;addColumn(&#039;value&#039;);   &lt;br /&gt;
&lt;br /&gt;
      // populate&lt;br /&gt;
      $rownr = 0;&lt;br /&gt;
      foreach ($article-&amp;gt;rating as $attr =&amp;gt; $value) {&lt;br /&gt;
         $table-&amp;gt;addRow(array(&#039;class&#039; =&amp;gt; &#039;row&#039;.($rownr % 2)));&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;attr&#039;, $attr);&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;value&#039;, $value);&lt;br /&gt;
         $rownr++;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // wrap table in a classed &amp;lt;div&amp;gt;&lt;br /&gt;
      $suffix = $this-&amp;gt;params-&amp;gt;get(&#039;ratingclass_sfx&#039;, &#039;rating&#039;);&lt;br /&gt;
      $html = &#039;&amp;lt;div class=&amp;quot;&#039;.$suffix.&#039;&amp;quot;&amp;gt;&#039;.(string)$table.&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
      $article-&amp;gt;text = $html.$article-&amp;gt;text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
This tutorial requires at least Joomla 2.5.x [to be determined] as the framework for previous versions misses a number of plugin hook place holders.&lt;br /&gt;
&lt;br /&gt;
For Joomla 2.5 a patch file and for Joomla 2.5.6 a patch extension is available.&lt;br /&gt;
&lt;br /&gt;
This patch has been submitted as [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 Joomla patch #28871].&lt;br /&gt;
&lt;br /&gt;
== Extension Files ==&lt;br /&gt;
&lt;br /&gt;
The install zip files for the four versions can be individually downloaded from JoomlaCode.org.&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75015/patch_28771-2.5.6.patch.gz &amp;lt;code&amp;gt;jpatch_28771-2.5.6.patch.gz&amp;lt;/code&amp;gt;] Joomla 2.5.6 unified diff patch&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75012/patch_28771-2.5.6.zip &amp;lt;code&amp;gt;patch_28771-2.5.6.zip&amp;lt;/code&amp;gt;] Patch as Joomla 2.5.6 installable component&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75013/plg_content_rating-2.5.0.zip &amp;lt;code&amp;gt;plg_content_rating-2.5.0.zip&amp;lt;/code&amp;gt;] The plugin as described in this tutorial&lt;br /&gt;
&lt;br /&gt;
The tracker page for the patch. [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 &amp;lt;code&amp;gt;#28771&amp;lt;/code&amp;gt;]. Patch [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28770 &amp;lt;code&amp;gt;#28770&amp;lt;/code&amp;gt;] is also related.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
*[[User:znarf|Franz Korntner]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Component Development]]&lt;br /&gt;
[[Category:Development]]&lt;br /&gt;
[[category:Joomla! 2.5]]&lt;br /&gt;
[[category:Manual]]&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68089</id>
		<title>Archived:Adding custom fields to the article component</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=Archived:Adding_custom_fields_to_the_article_component&amp;diff=68089"/>
		<updated>2012-06-29T21:03:17Z</updated>

		<summary type="html">&lt;p&gt;Znarf: Created page with &amp;quot;This tutorial is for Joomla {{JVer|2.5}}  == Intro == This plugin demonstrates how a component developer can use the features in Joomla 2.5 to add custom fields to the article co...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This tutorial is for Joomla {{JVer|2.5}}&lt;br /&gt;
&lt;br /&gt;
== Intro ==&lt;br /&gt;
This plugin demonstrates how a component developer can use the features in Joomla 2.5 to add custom fields to the article component (com_content), without the need to change core files. Capabilities that are implemented in this demo package include:&lt;br /&gt;
# Adding extra fields to the back-end article manager.&lt;br /&gt;
# Adding extra fields to the front-end article editor.&lt;br /&gt;
# Storing the editor supplied field values into a database table.&lt;br /&gt;
# Converting the field values into HTML which is injected into the displayed article.&lt;br /&gt;
A capability which is present but not demonstrated, is integrating the values of the custom fields into other components and views, such as article lists or category blogs.&lt;br /&gt;
&lt;br /&gt;
== This example ==&lt;br /&gt;
This example adds food ratings to articles. These ratings consist of texture, temperature and taste. When present, they will be displayed as a simple table. This table is positioned before the article content.&lt;br /&gt;
[[File:Plg_content_rating-1.png|thumb|frame|none|alt=example of rendered article|An example of how the plugin places the table at the beginning of an article]]&lt;br /&gt;
&lt;br /&gt;
Setting the values of these fields can be done in both the backend and frontend.&lt;br /&gt;
[[File:Plg_content_rating-frontend.png|thumb|frame|left|alt=edit article frontend|Accessing the additional fields through the frontend]]&lt;br /&gt;
[[File:Plg_content_rating-backend.png|thumb|frame|none|alt=edit article backend|... and the backend]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Framework ==&lt;br /&gt;
Within Joomla, the article manager is basically a form driven component. Both in the backend as the frontend. When handling a form, the framework performs the following actions.&lt;br /&gt;
&lt;br /&gt;
# Loads the form description. This is a XML structure.&lt;br /&gt;
# Loads the data from the user state if present, and if not from the database.&lt;br /&gt;
# Injects the data into the form&lt;br /&gt;
# Renders/displays the form for the user to work on.&lt;br /&gt;
&lt;br /&gt;
When the user is finished and issues a &amp;lt;code&amp;gt;SAVE&amp;lt;/code&amp;gt;, the framework will:&lt;br /&gt;
&lt;br /&gt;
# Captures the submitted data&lt;br /&gt;
# Loads the form description.&lt;br /&gt;
# Validates the data against the form description.&lt;br /&gt;
# If validation fails, stores the data in the user state and starts from the beginning.&lt;br /&gt;
# Updates the database accordingly&lt;br /&gt;
&lt;br /&gt;
The framework has placeholders for all these moments. This example plugin will hook into these placeholders, expanding the form and data with additional custom fields.&lt;br /&gt;
&lt;br /&gt;
The hooks are:&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareForm()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the form description. It will add an extra form group containing the additional fields&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after loading the article from the database. It will inject the additional fields and their value into the article.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been successfully stored in database. It will store the additional fields in their designated database table.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called after an article has been deleted (emptying of trash). It will delete the additional fields.&lt;br /&gt;
*&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt;&lt;br /&gt;
*:Called before the article will be rendered/displayed. It will inject the rating values as a table at the beginning of the article, if values have been entered.&lt;br /&gt;
&lt;br /&gt;
== Database ==&lt;br /&gt;
The contents of the extra custom fields need to be stored in the database. Ideally would be if they have their own dedicated database table. This however, requires considerable extension overhead. With Joomla 2.5 is is possible to extend the user profile. The extra fields are stored in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;. That table is constructed and used in such a manner that it can also hold the extra article fields. [I would like to spark the discussion to evolve &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; into a generic table for expanding core tables.]&lt;br /&gt;
&lt;br /&gt;
This plugin example will use table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt; to store the extra fields. It will use the tables &amp;lt;code&amp;gt;user_id&amp;lt;/code&amp;gt; as &amp;lt;code&amp;gt;article_id&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Extension files ==&lt;br /&gt;
The example plugin contains the following files. It has an extra directory &amp;lt;code&amp;gt;rating&amp;lt;/code&amp;gt;. The primary reason is that it contains a XML file that is not a manifest. If it were located in the plugin top-level directory, the installer would find it and mistakenly identify it as an orphaned plugin. This would result in the form showing up when &#039;Discovering&#039; lost extensions.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
── plugins/content/rating&lt;br /&gt;
   ├── language&lt;br /&gt;
   │   └── en-GB&lt;br /&gt;
   │       ├── en-GB.plg_content_rating.ini      [language file]&lt;br /&gt;
   │       └── en-GB.plg_content_rating.sys.ini  [language file]&lt;br /&gt;
   ├── rating&lt;br /&gt;
   │   ├── rating.css  [CSS for the rendered rating table]&lt;br /&gt;
   │   └── rating.xml  [Form description]&lt;br /&gt;
   ├── rating.php   [Plugin hooks]&lt;br /&gt;
   └── rating.xml   [Manifest]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Manifest ===&lt;br /&gt;
A regular plugin manifest which takes an extra configuration argument, a CSS name that is tagged onto the rendered rating table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;extension version=&amp;quot;2.5&amp;quot; type=&amp;quot;plugin&amp;quot; group=&amp;quot;content&amp;quot; method=&amp;quot;upgrade&amp;quot;&amp;gt;&lt;br /&gt;
      &amp;lt;name&amp;gt;plg_content_rating&amp;lt;/name&amp;gt;&lt;br /&gt;
      &amp;lt;author&amp;gt;Joomla! Project&amp;lt;/author&amp;gt;&lt;br /&gt;
      &amp;lt;creationDate&amp;gt;June 2012&amp;lt;/creationDate&amp;gt;&lt;br /&gt;
      &amp;lt;copyright&amp;gt;(C) 2005 - 2012 Open Source Matters. All rights reserved.&amp;lt;/copyright&amp;gt;&lt;br /&gt;
      &amp;lt;license&amp;gt;GNU General Public License version 2 or later; see LICENSE.txt&amp;lt;/license&amp;gt;&lt;br /&gt;
      &amp;lt;authorEmail&amp;gt;admin@joomla.org&amp;lt;/authorEmail&amp;gt;&lt;br /&gt;
      &amp;lt;authorUrl&amp;gt;www.joomla.org&amp;lt;/authorUrl&amp;gt;&lt;br /&gt;
      &amp;lt;version&amp;gt;2.5.0&amp;lt;/version&amp;gt;&lt;br /&gt;
      &amp;lt;description&amp;gt;PLG_CONTENT_RATING_XML_DESCRIPTION&amp;lt;/description&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;files&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;language&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;folder&amp;gt;rating&amp;lt;/folder&amp;gt;&lt;br /&gt;
         &amp;lt;filename plugin=&amp;quot;rating&amp;quot;&amp;gt;rating.php&amp;lt;/filename&amp;gt;&lt;br /&gt;
         &amp;lt;filename&amp;gt;index.html&amp;lt;/filename&amp;gt;&lt;br /&gt;
      &amp;lt;/files&amp;gt;&lt;br /&gt;
&lt;br /&gt;
      &amp;lt;config&amp;gt;&lt;br /&gt;
         &amp;lt;fields name=&amp;quot;params&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;fieldset name=&amp;quot;basic&amp;quot;&amp;gt;&lt;br /&gt;
               &amp;lt;field&lt;br /&gt;
                  name=&amp;quot;ratingclass_sfx&amp;quot;&lt;br /&gt;
                  type=&amp;quot;text&amp;quot;&lt;br /&gt;
                  label=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL&amp;quot;&lt;br /&gt;
                  description=&amp;quot;PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC&amp;quot;&lt;br /&gt;
               /&amp;gt;&lt;br /&gt;
            &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
         &amp;lt;/fields&amp;gt;&lt;br /&gt;
      &amp;lt;/config&amp;gt;&lt;br /&gt;
   &amp;lt;/extension&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== language files ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source&amp;gt;&lt;br /&gt;
; Joomla! Project&lt;br /&gt;
; Copyright (C) 2005 - 2012 Open Source Matters. All rights reserved.&lt;br /&gt;
; License GNU General Public License version 2 or later; see LICENSE.txt, see LICENSE.php&lt;br /&gt;
&lt;br /&gt;
PLG_CONTENT_RATING=&amp;quot;Content - Rating&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_XML_DESCRIPTION=&amp;quot;[plg_content_rating] Example plugin on how to add custom fields (texture/temperature/taste) to articles (com_content). Adds ratings to an article, and display them as a table before the article content.&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_SLIDER_LABEL=&amp;quot;Showcase Options&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL=&amp;quot;Texture&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEXTURE_DESC=&amp;quot;What does the sample feel like&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL=&amp;quot;Temperature&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC=&amp;quot;What is the temperature of the sample&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_LABEL=&amp;quot;Taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_FIELD_TASTE_DESC=&amp;quot;How does the sample taste&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_LABEL=&amp;quot;Rating Class&amp;quot;&lt;br /&gt;
PLG_CONTENT_RATING_ITEM_FIELD_RATING_CLASS_DESC=&amp;quot;Optional CSS class to add to the rating. This allows CSS styling specific to the page.&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== CSS ===&lt;br /&gt;
The CSS file for the rendered table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;css&amp;quot;&amp;gt;&lt;br /&gt;
div.rating {&lt;br /&gt;
    display: block;&lt;br /&gt;
    float: left;&lt;br /&gt;
    padding-right: 10px;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr td {&lt;br /&gt;
    margin: 1px !important;&lt;br /&gt;
    padding: 2px !important;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row0  {&lt;br /&gt;
    background-color:#efefef;&lt;br /&gt;
}&lt;br /&gt;
div.rating table tr.row1 {&lt;br /&gt;
    background-color:#ffffff;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Form description ===&lt;br /&gt;
The form containing the custom fields.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;xml&amp;quot;&amp;gt;&lt;br /&gt;
   &amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;br /&gt;
   &amp;lt;form&amp;gt;&lt;br /&gt;
      &amp;lt;fields name=&amp;quot;rating&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;fieldset name=&amp;quot;rating&amp;quot; label=&amp;quot;PLG_CONTENT_RATING_SLIDER_LABEL&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;texture&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;texture&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEXTURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;temperature&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TEMPERATURE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
            &amp;lt;field&lt;br /&gt;
               name=&amp;quot;taste&amp;quot;&lt;br /&gt;
               type=&amp;quot;text&amp;quot;&lt;br /&gt;
               id=&amp;quot;taste&amp;quot;&lt;br /&gt;
               description=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_DESC&amp;quot;&lt;br /&gt;
               label=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_LABEL&amp;quot;&lt;br /&gt;
               message=&amp;quot;PLG_CONTENT_RATING_FIELD_TASTE_MESSAGE&amp;quot;&lt;br /&gt;
               size=&amp;quot;30&amp;quot;&lt;br /&gt;
            /&amp;gt;&lt;br /&gt;
         &amp;lt;/fieldset&amp;gt;&lt;br /&gt;
      &amp;lt;/fields&amp;gt;&lt;br /&gt;
   &amp;lt;/form&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Plugin hooks ==&lt;br /&gt;
The main file &#039;rating.php&#039; contains the hooks which are called by the Joomla framework.&lt;br /&gt;
&lt;br /&gt;
For all calls:&lt;br /&gt;
*The &amp;lt;code&amp;gt;$context&amp;lt;/code&amp;gt; parameter is a string identifying the calling placeholder.&lt;br /&gt;
*The &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; parameter is an object containing the form values&lt;br /&gt;
*The &amp;lt;code&amp;gt;$article&amp;lt;/code&amp;gt; parameter is an object (&amp;lt;code&amp;gt;JTableContent&amp;lt;/code&amp;gt;) representing the article.&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;__construct&amp;lt;/code&amp;gt; ===&lt;br /&gt;
The constructor. It loads the language file.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function __construct(&amp;amp; $subject, $config)&lt;br /&gt;
   {&lt;br /&gt;
      parent::__construct($subject, $config);&lt;br /&gt;
      $this-&amp;gt;loadLanguage();&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareForm&amp;lt;/code&amp;gt; extends the article manager form in both the front-end as the back-end. It loads the form fragment as described in &amp;lt;code&amp;gt;rating/rating.xml&amp;lt;/code&amp;gt; and merges that into the framework form.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public public function onContentPrepareForm($form, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (!($form instanceof JForm))&lt;br /&gt;
      {&lt;br /&gt;
         $this-&amp;gt;_subject-&amp;gt;setError(&#039;JERROR_NOT_A_FORM&#039;);&lt;br /&gt;
         return false;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // Add the extra fields to the form.&lt;br /&gt;
      JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
      $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepareData&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; is executed after an article is loaded from the &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will gather and merge the values of the custom fields, which are located in the table &amp;lt;code&amp;gt;#__user_profiles&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
The field &amp;lt;code&amp;gt;articleId&amp;lt;/code&amp;gt; in &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the article record number. That number is used to retrieve the values of the custom fields. If no record number is present, or it is zero, then a new record is being create. When this occurs, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; gets populated with the default values from the form description xml.&lt;br /&gt;
&lt;br /&gt;
It is important that when &amp;lt;code&amp;gt;onContentPrepareData()&amp;lt;/code&amp;gt; returns, &amp;lt;code&amp;gt;$data&amp;lt;/code&amp;gt; contains the custom fields. This is due to how the framework uses them as place holders. When a user saves a newly created article, and an error situation occurs (like some values are missing) the form data is temporarily stored in the user state. If the place holders are not present, their values will not be preserved. The user will notice this because the custom fields will get erased.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepareData($context, $data)&lt;br /&gt;
   {&lt;br /&gt;
      if (is_object($data))&lt;br /&gt;
      {&lt;br /&gt;
         $articleId = isset($data-&amp;gt;id) ? $data-&amp;gt;id : 0;&lt;br /&gt;
         if ($articleId &amp;gt; 0)&lt;br /&gt;
         {&lt;br /&gt;
            // Load the data from the database.&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;select(&#039;profile_key, profile_value&#039;);&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $query-&amp;gt;order(&#039;ordering&#039;);&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            $results = $db-&amp;gt;loadRowList();&lt;br /&gt;
&lt;br /&gt;
            // Check for a database error.&lt;br /&gt;
            if ($db-&amp;gt;getErrorNum())&lt;br /&gt;
            {&lt;br /&gt;
               $this-&amp;gt;_subject-&amp;gt;setError($db-&amp;gt;getErrorMsg());&lt;br /&gt;
               return false;&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            // Merge the data.&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
&lt;br /&gt;
            foreach ($results as $v)&lt;br /&gt;
            {&lt;br /&gt;
               $k = str_replace(&#039;rating.&#039;, &#039;&#039;, $v[0]);&lt;br /&gt;
               $data-&amp;gt;rating[$k] = json_decode($v[1], true);&lt;br /&gt;
               if ($data-&amp;gt;rating[$k] === null)&lt;br /&gt;
               {&lt;br /&gt;
                  $data-&amp;gt;rating[$k] = $v[1];&lt;br /&gt;
               }&lt;br /&gt;
            }&lt;br /&gt;
         } else {&lt;br /&gt;
            // load the form&lt;br /&gt;
            JForm::addFormPath(dirname(__FILE__) . &#039;/rating&#039;);&lt;br /&gt;
            $form = new JForm(&#039;com_content.article&#039;);&lt;br /&gt;
            $form-&amp;gt;loadFile(&#039;rating&#039;, false);&lt;br /&gt;
&lt;br /&gt;
            // Merge the default values&lt;br /&gt;
            $data-&amp;gt;rating = array();&lt;br /&gt;
            foreach ($form-&amp;gt;getFieldset(&#039;rating&#039;) as $field) {&lt;br /&gt;
               $data-&amp;gt;rating[] = array($field-&amp;gt;fieldname, $field-&amp;gt;value);&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterSave&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterSave()&amp;lt;/code&amp;gt; is called after the article is stored into the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will extract the values of the custom fields and store them into a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterSave($context, &amp;amp;$article, $isNew)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId &amp;amp;&amp;amp; isset($article-&amp;gt;rating) &amp;amp;&amp;amp; (count($article-&amp;gt;rating)))&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
&lt;br /&gt;
            $query-&amp;gt;clear();&lt;br /&gt;
            $query-&amp;gt;insert(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $order = 1;&lt;br /&gt;
            foreach ($article-&amp;gt;rating as $k =&amp;gt; $v)&lt;br /&gt;
            {&lt;br /&gt;
               $query-&amp;gt;values($articleId.&#039;, &#039;.$db-&amp;gt;quote(&#039;rating.&#039;.$k).&#039;, &#039;.$db-&amp;gt;quote(json_encode($v)).&#039;, &#039;.$order++);&lt;br /&gt;
            }&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query()) {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentAfterDelete&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentAfterDelete()&amp;lt;/code&amp;gt; is called after the article is deleted from the database &amp;lt;code&amp;gt;#__content&amp;lt;/code&amp;gt; table. It will delete the linked custom fields which are stored in a separate table.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentAfterDelete($context, $article)&lt;br /&gt;
   {&lt;br /&gt;
      $articleId = $article-&amp;gt;id;&lt;br /&gt;
      if ($articleId)&lt;br /&gt;
      {&lt;br /&gt;
         try&lt;br /&gt;
         {&lt;br /&gt;
            $db = JFactory::getDbo();&lt;br /&gt;
&lt;br /&gt;
            $query = $db-&amp;gt;getQuery(true);&lt;br /&gt;
            $query-&amp;gt;delete();&lt;br /&gt;
            $query-&amp;gt;from(&#039;#__user_profiles&#039;);&lt;br /&gt;
            $query-&amp;gt;where(&#039;user_id = &#039; . $db-&amp;gt;Quote($articleId));&lt;br /&gt;
            $query-&amp;gt;where(&#039;profile_key LIKE &#039; . $db-&amp;gt;Quote(&#039;rating.%&#039;));&lt;br /&gt;
            $db-&amp;gt;setQuery($query);&lt;br /&gt;
&lt;br /&gt;
            if (!$db-&amp;gt;query())&lt;br /&gt;
            {&lt;br /&gt;
               throw new Exception($db-&amp;gt;getErrorMsg());&lt;br /&gt;
            }&lt;br /&gt;
         }&lt;br /&gt;
         catch (JException $e)&lt;br /&gt;
         {&lt;br /&gt;
            $this-&amp;gt;_subject-&amp;gt;setError($e-&amp;gt;getMessage());&lt;br /&gt;
            return false;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      return true;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== &amp;lt;code&amp;gt;onContentPrepare&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&amp;lt;code&amp;gt;onContentPrepare()&amp;lt;/code&amp;gt; is called when the article is being prepared for display. This is the moment where HTML can be injected into what will be displayed.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;$params&amp;lt;/code&amp;gt; are the article params, &amp;lt;code&amp;gt;$this-&amp;gt;params&amp;lt;/code&amp;gt; are the plugin configuration parameters as described in the manifest.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;php&amp;quot;&amp;gt;&lt;br /&gt;
   public function onContentPrepare($context, &amp;amp;$article, &amp;amp;$params, $page = 0)&lt;br /&gt;
   {&lt;br /&gt;
      if (!isset($article-&amp;gt;rating) || !count($article-&amp;gt;rating))&lt;br /&gt;
         return;&lt;br /&gt;
&lt;br /&gt;
      // add extra css for table&lt;br /&gt;
      $doc = JFactory::getDocument();&lt;br /&gt;
      $doc-&amp;gt;addStyleSheet(JURI::base(true).&#039;/plugins/content/rating/rating/rating.css&#039;);&lt;br /&gt;
       &lt;br /&gt;
      // construct a result table on the fly   &lt;br /&gt;
      jimport(&#039;joomla.html.grid&#039;);&lt;br /&gt;
      $table = new JGrid();&lt;br /&gt;
&lt;br /&gt;
      // Create columns&lt;br /&gt;
      $table-&amp;gt;addColumn(&#039;attr&#039;)&lt;br /&gt;
         -&amp;gt;addColumn(&#039;value&#039;);   &lt;br /&gt;
&lt;br /&gt;
      // populate&lt;br /&gt;
      $rownr = 0;&lt;br /&gt;
      foreach ($article-&amp;gt;rating as $attr =&amp;gt; $value) {&lt;br /&gt;
         $table-&amp;gt;addRow(array(&#039;class&#039; =&amp;gt; &#039;row&#039;.($rownr % 2)));&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;attr&#039;, $attr);&lt;br /&gt;
         $table-&amp;gt;setRowCell(&#039;value&#039;, $value);&lt;br /&gt;
         $rownr++;&lt;br /&gt;
      }&lt;br /&gt;
&lt;br /&gt;
      // wrap table in a classed &amp;lt;div&amp;gt;&lt;br /&gt;
      $suffix = $this-&amp;gt;params-&amp;gt;get(&#039;ratingclass_sfx&#039;, &#039;rating&#039;);&lt;br /&gt;
      $html = &#039;&amp;lt;div class=&amp;quot;&#039;.$suffix.&#039;&amp;quot;&amp;gt;&#039;.(string)$table.&#039;&amp;lt;/div&amp;gt;&#039;;&lt;br /&gt;
&lt;br /&gt;
      $article-&amp;gt;text = $html.$article-&amp;gt;text;&lt;br /&gt;
   }&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Prerequisites ==&lt;br /&gt;
This tutorial requires at least Joomla 2.5.x [to be determined] as the framework for previous versions misses a number of plugin hook place holders.&lt;br /&gt;
&lt;br /&gt;
For Joomla 2.5 a patch file and for Joomla 2.5.6 a patch extension is available.&lt;br /&gt;
&lt;br /&gt;
This patch has been submitted as [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_item_id=xxxxx Joomla patch #xxxxx].&lt;br /&gt;
&lt;br /&gt;
== Extension Files ==&lt;br /&gt;
&lt;br /&gt;
The install zip files for the four versions can be individually downloaded from JoomlaCode.org.&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75015/patch_28771-2.5.6.patch.gz &amp;lt;code&amp;gt;jpatch_28771-2.5.6.patch.gz&amp;lt;/code&amp;gt;] Joomla 2.5.6 unified diff patch&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75012/patch_28771-2.5.6.zip &amp;lt;code&amp;gt;patch_28771-2.5.6.zip&amp;lt;/code&amp;gt;] Patch as Joomla 2.5.6 installable component&lt;br /&gt;
* [http://joomlacode.org/gf/download/trackeritem/28771/75013/plg_content_rating-2.5.0.zip &amp;lt;code&amp;gt;plg_content_rating-2.5.0.zip&amp;lt;/code&amp;gt;] The plugin as described in this tutorial&lt;br /&gt;
&lt;br /&gt;
The tracker page for the patch. [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28771 &amp;lt;code&amp;gt;#28771&amp;lt;/code&amp;gt;]. Patch [http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&amp;amp;tracker_id=8103&amp;amp;tracker_item_id=28770 &amp;lt;code&amp;gt;#28770&amp;lt;/code&amp;gt;] is also related.&lt;br /&gt;
&lt;br /&gt;
== Contributors ==&lt;br /&gt;
*[[User:znarf|Franz Korntner]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Tutorials]]&lt;br /&gt;
[[Category:Component Development]]&lt;br /&gt;
[[Category:Development]]&lt;br /&gt;
[[category:Joomla! 2.5]]&lt;br /&gt;
[[category:Manual]]&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=File:Plg_content_rating-frontend.png&amp;diff=68081</id>
		<title>File:Plg content rating-frontend.png</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=File:Plg_content_rating-frontend.png&amp;diff=68081"/>
		<updated>2012-06-29T12:27:49Z</updated>

		<summary type="html">&lt;p&gt;Znarf: Plugin plg_content_rating adds customs fields to an article. This image shows how these fields are accessable through the frontend.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Plugin plg_content_rating adds customs fields to an article. This image shows how these fields are accessable through the frontend.&lt;br /&gt;
== Licensing ==&lt;br /&gt;
{{JEDL}}&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=File:Plg_content_rating-backend.png&amp;diff=68080</id>
		<title>File:Plg content rating-backend.png</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=File:Plg_content_rating-backend.png&amp;diff=68080"/>
		<updated>2012-06-29T12:27:12Z</updated>

		<summary type="html">&lt;p&gt;Znarf: Plugin plg_content_rating adds customs fields to an article. This image shows how these fields are accessable through the backend.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Plugin plg_content_rating adds customs fields to an article. This image shows how these fields are accessable through the backend.&lt;br /&gt;
== Licensing ==&lt;br /&gt;
{{JEDL}}&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=File:Plg_content_rating-1.png&amp;diff=68077</id>
		<title>File:Plg content rating-1.png</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=File:Plg_content_rating-1.png&amp;diff=68077"/>
		<updated>2012-06-29T12:03:43Z</updated>

		<summary type="html">&lt;p&gt;Znarf: Plugin plg_content_rating adds customs fields to an article. This image shows how these fields get rendered.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Summary ==&lt;br /&gt;
Plugin plg_content_rating adds customs fields to an article. This image shows how these fields get rendered.&lt;br /&gt;
== Licensing ==&lt;br /&gt;
{{JEDL}}&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=Deploying_an_Update_Server&amp;diff=64857</id>
		<title>Deploying an Update Server</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=Deploying_an_Update_Server&amp;diff=64857"/>
		<updated>2012-02-12T02:45:13Z</updated>

		<summary type="html">&lt;p&gt;Znarf: /* Extension */ it took hours before I discovered that this is required for the auto update to work.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{RightTOC}}&lt;br /&gt;
&lt;br /&gt;
This tutorial is for all Joomla! versions newer than {{JVer|1.6}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This tutorial is designed to teach developers how to create an update server for integration with the update system introduced in Joomla! 1.6.  By adding an update server listing to your extension&#039;s manifest, developers enable users to update their extensions via the [[Help16:Extensions Extension Manager Update|Extension Manager&#039;s Update]] view with only a few clicks.&lt;br /&gt;
&lt;br /&gt;
= Defining an update server =&lt;br /&gt;
In order to use this feature, an update server must be defined in your extension&#039;s manifest.  This definition can be used in all Joomla! 1.6 and newer compatible extensions but is not available for templates.  You can use two options for your server type; collection or extension.  These will be explained in detail shortly.  The update server is defined as follows for each type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 &amp;lt;updateservers&amp;gt;&lt;br /&gt;
    &amp;lt;server type=&amp;quot;collection&amp;quot;&amp;gt;http://example.com/list.xml&amp;lt;/server&amp;gt;&lt;br /&gt;
    &amp;lt;server type=&amp;quot;extension&amp;quot; priority=&amp;quot;2&amp;quot; name=&amp;quot;My Extension&#039;s Updates&amp;quot;&amp;gt;http://example.com/extension.xml&amp;lt;/server&amp;gt;&lt;br /&gt;
 &amp;lt;/updateservers&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple servers can be defined within the &amp;lt;updateservers&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
= Server types =&lt;br /&gt;
== Collection ==&lt;br /&gt;
The collection server type allows developers to define an extension&#039;s manifest to pull updates from a collection.  This type of server can be used if the developer wants to define all of their extension&#039;s updates in a single file (not recommended) or if their extension has multiple sub-extensions which are not distributed or updated at the same time (such as a package extension type).  The below example is the collection definition used by the updater when processing core Joomla! updates:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 &amp;lt;extensionset name=&amp;quot;Joomla Core&amp;quot; description=&amp;quot;Joomla! Core&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;extension name=&amp;quot;Joomla&amp;quot; element=&amp;quot;joomla&amp;quot; type=&amp;quot;file&amp;quot; version=&amp;quot;1.7.0&amp;quot; detailsurl=&amp;quot;http://update.joomla.org/core/extension.xml&amp;quot;/&amp;gt;&lt;br /&gt;
 &amp;lt;/extensionset&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All definitions must be defined between &amp;lt;extensionset&amp;gt; tags in your collection manifest.  The &amp;lt;extensionset&amp;gt; tag has two optional parameters; name and description.  For each extension that this collection references, a separate &amp;lt;extension&amp;gt; tag is required.  The &amp;lt;extension&amp;gt; tag has the following parameters, all of which are required for updates to properly process:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - The name of the extension&lt;br /&gt;
* &#039;&#039;&#039;element&#039;&#039;&#039; - The untranslated extension name i.e. mod_custom&lt;br /&gt;
* &#039;&#039;&#039;type&#039;&#039;&#039; - The extension type (component, module, plugin, etc.)&lt;br /&gt;
* &#039;&#039;&#039;version&#039;&#039;&#039; - The latest version of the extension&lt;br /&gt;
* &#039;&#039;&#039;detailsurl&#039;&#039;&#039; - The URL of the XML file which contains that extension&#039;s individual update definitions&lt;br /&gt;
&lt;br /&gt;
== Extension ==&lt;br /&gt;
The extension server type allows developers to define an extension&#039;s manifest to pull updates from a single extension&#039;s manifest.  All collection manifests eventually point to this XML file.  All updates in this file must be defined after an &amp;lt;updates&amp;gt; tag at the beginning of the file.  The below example is the update definition for the Joomla! 1.7.0 release:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 &amp;lt;update&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Joomla! 1.7&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;description&amp;gt;Joomla! 1.7 CMS&amp;lt;/description&amp;gt;&lt;br /&gt;
    &amp;lt;element&amp;gt;joomla&amp;lt;/element&amp;gt;&lt;br /&gt;
    &amp;lt;type&amp;gt;file&amp;lt;/type&amp;gt;&lt;br /&gt;
    &amp;lt;version&amp;gt;1.7.0&amp;lt;/version&amp;gt;&lt;br /&gt;
    &amp;lt;infourl title=&amp;quot;Joomla!&amp;quot;&amp;gt;http://www.joomla.org/&amp;lt;/infourl&amp;gt;&lt;br /&gt;
    &amp;lt;downloads&amp;gt;&lt;br /&gt;
        &amp;lt;downloadurl type=&amp;quot;full&amp;quot; format=&amp;quot;zip&amp;quot;&amp;gt;http://joomlacode.org/gf/download/frsrelease/15279/66552/Joomla_1.6.5_to_1.7.0_Package.zip&amp;lt;/downloadurl&amp;gt;&lt;br /&gt;
    &amp;lt;/downloads&amp;gt;&lt;br /&gt;
    &amp;lt;tags&amp;gt;&lt;br /&gt;
        &amp;lt;tag&amp;gt;stable&amp;lt;/tag&amp;gt;&lt;br /&gt;
    &amp;lt;/tags&amp;gt;&lt;br /&gt;
    &amp;lt;maintainer&amp;gt;Sam Moffatt&amp;lt;/maintainer&amp;gt;&lt;br /&gt;
    &amp;lt;maintainerurl&amp;gt;http://sammoffatt.com.au&amp;lt;/maintainerurl&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;Testing&amp;lt;/section&amp;gt;&lt;br /&gt;
    &amp;lt;targetplatform name=&amp;quot;joomla&amp;quot; version=&amp;quot;1.6&amp;quot;/&amp;gt;&lt;br /&gt;
 &amp;lt;/update&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following section describes the elements of a single update entity.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - The name of the extension, this name will appear in the Name column of the Extension Manager&#039;s Update view (required)&lt;br /&gt;
* &#039;&#039;&#039;description&#039;&#039;&#039; - A short description of the extension (optional) -- if you choose to use &amp;lt;![CDATA[]]&amp;gt;, double-quotes will break the HTML formatting.  Use single quotes with your HTML entities.&lt;br /&gt;
* &#039;&#039;&#039;element&#039;&#039;&#039; - The installed name of the extension (required)&lt;br /&gt;
* &#039;&#039;&#039;type&#039;&#039;&#039; - The type of extension (component, module, plugin, etc.) (required)&lt;br /&gt;
* &#039;&#039;&#039;folder&#039;&#039;&#039; - Specific to plugins, this tag describes the type of plugin being updated (content, system, etc.) (required for plugins)&lt;br /&gt;
* &#039;&#039;&#039;client&#039;&#039;&#039; - The client name of the extension (&amp;quot;site&amp;quot; or &amp;quot;administrator&amp;quot;) (usually optional, required for J! 1.7.3 due to a regression; but highly encouraged regardless). Plugins are automatically installed with a client of &amp;quot;site&amp;quot;, but you will need to specify the client in an update or it will use &amp;quot;administrator&amp;quot;. Note that the tag is &amp;quot;client&amp;quot; not &amp;quot;client_id&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;version&#039;&#039;&#039; - The version of the release (required)&lt;br /&gt;
* &#039;&#039;&#039;infourl&#039;&#039;&#039; - A URL to point users to containing information about the update (optional) (In CMS 2.5, if set, this URL will be displayed in the update view)&lt;br /&gt;
* &#039;&#039;&#039;downloads&#039;&#039;&#039; - The section which lists all download locations&lt;br /&gt;
** &#039;&#039;&#039;downloadurl&#039;&#039;&#039; - The URL to download the extension from; the &amp;lt;downloadurl&amp;gt; tag has two required parameters:&lt;br /&gt;
*** &#039;&#039;&#039;type&#039;&#039;&#039; - The type of package (full or upgrade)&lt;br /&gt;
*** &#039;&#039;&#039;format&#039;&#039;&#039; - The format of the package (zip, tar, etc.)&lt;br /&gt;
* &#039;&#039;&#039;tags&#039;&#039;&#039; - Optional (unknown use)&lt;br /&gt;
* &#039;&#039;&#039;maintainer&#039;&#039;&#039; - The name of the extension maintainer (similar to the &amp;lt;author&amp;gt; tag in a manifest) (optional)&lt;br /&gt;
* &#039;&#039;&#039;maintainerurl&#039;&#039;&#039; - The website of the extension maintainer (similar to the &amp;lt;authorUrl&amp;gt; tag in a manifest) (optional)&lt;br /&gt;
* &#039;&#039;&#039;section&#039;&#039;&#039; - Optional (unknown use)&lt;br /&gt;
* &#039;&#039;&#039;targetplatform&#039;&#039;&#039; - A tag to define platform requirements, requires the following elements&lt;br /&gt;
** &#039;&#039;&#039;name&#039;&#039;&#039; - The name of the platform dependency; as of this writing, it should ONLY be &amp;quot;joomla&amp;quot;&lt;br /&gt;
** &#039;&#039;&#039;version&#039;&#039;&#039; - The version of Joomla! the extension supports&lt;br /&gt;
*** &#039;&#039;&#039;Note:&#039;&#039;&#039; If your extension is Joomla! 1.6, 1.7, and/or 2.5 compatible, you will be required to have separate &amp;lt;update&amp;gt; definitions for each version due to the manner in which the updater checks the version&lt;br /&gt;
&lt;br /&gt;
A separate &amp;lt;update&amp;gt; definition will be required for each version of your extension you release.&lt;br /&gt;
&lt;br /&gt;
The values of &#039;&#039;&#039;element&#039;&#039;&#039;, &#039;&#039;&#039;type&#039;&#039;&#039;, &#039;&#039;&#039;client_id&#039;&#039;&#039; and &#039;&#039;&#039;folder&#039;&#039;&#039; should match those in the table #__extensions.&lt;br /&gt;
&lt;br /&gt;
= Supporting Tools =&lt;br /&gt;
Maintaining your update server files can be difficult depending on the manner in which you set up your files.  An extension which can help you to maintain this is the Akeeba Release System, available free of charge from https://www.akeebabackup.com&lt;br /&gt;
&lt;br /&gt;
= Contributors =&lt;br /&gt;
*[[User:mbabker|Michael Babker]]&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
	<entry>
		<id>https://docs.sandbox.joomla.org/index.php?title=Deploying_an_Update_Server&amp;diff=64856</id>
		<title>Deploying an Update Server</title>
		<link rel="alternate" type="text/html" href="https://docs.sandbox.joomla.org/index.php?title=Deploying_an_Update_Server&amp;diff=64856"/>
		<updated>2012-02-12T02:31:20Z</updated>

		<summary type="html">&lt;p&gt;Znarf: /* Extension */  client is not an integer (as client_id) but textual.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{RightTOC}}&lt;br /&gt;
&lt;br /&gt;
This tutorial is for all Joomla! versions newer than {{JVer|1.6}}&lt;br /&gt;
&lt;br /&gt;
= Introduction =&lt;br /&gt;
This tutorial is designed to teach developers how to create an update server for integration with the update system introduced in Joomla! 1.6.  By adding an update server listing to your extension&#039;s manifest, developers enable users to update their extensions via the [[Help16:Extensions Extension Manager Update|Extension Manager&#039;s Update]] view with only a few clicks.&lt;br /&gt;
&lt;br /&gt;
= Defining an update server =&lt;br /&gt;
In order to use this feature, an update server must be defined in your extension&#039;s manifest.  This definition can be used in all Joomla! 1.6 and newer compatible extensions but is not available for templates.  You can use two options for your server type; collection or extension.  These will be explained in detail shortly.  The update server is defined as follows for each type:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 &amp;lt;updateservers&amp;gt;&lt;br /&gt;
    &amp;lt;server type=&amp;quot;collection&amp;quot;&amp;gt;http://example.com/list.xml&amp;lt;/server&amp;gt;&lt;br /&gt;
    &amp;lt;server type=&amp;quot;extension&amp;quot; priority=&amp;quot;2&amp;quot; name=&amp;quot;My Extension&#039;s Updates&amp;quot;&amp;gt;http://example.com/extension.xml&amp;lt;/server&amp;gt;&lt;br /&gt;
 &amp;lt;/updateservers&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Multiple servers can be defined within the &amp;lt;updateservers&amp;gt; tag.&lt;br /&gt;
&lt;br /&gt;
= Server types =&lt;br /&gt;
== Collection ==&lt;br /&gt;
The collection server type allows developers to define an extension&#039;s manifest to pull updates from a collection.  This type of server can be used if the developer wants to define all of their extension&#039;s updates in a single file (not recommended) or if their extension has multiple sub-extensions which are not distributed or updated at the same time (such as a package extension type).  The below example is the collection definition used by the updater when processing core Joomla! updates:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 &amp;lt;extensionset name=&amp;quot;Joomla Core&amp;quot; description=&amp;quot;Joomla! Core&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;extension name=&amp;quot;Joomla&amp;quot; element=&amp;quot;joomla&amp;quot; type=&amp;quot;file&amp;quot; version=&amp;quot;1.7.0&amp;quot; detailsurl=&amp;quot;http://update.joomla.org/core/extension.xml&amp;quot;/&amp;gt;&lt;br /&gt;
 &amp;lt;/extensionset&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
All definitions must be defined between &amp;lt;extensionset&amp;gt; tags in your collection manifest.  The &amp;lt;extensionset&amp;gt; tag has two optional parameters; name and description.  For each extension that this collection references, a separate &amp;lt;extension&amp;gt; tag is required.  The &amp;lt;extension&amp;gt; tag has the following parameters, all of which are required for updates to properly process:&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - The name of the extension&lt;br /&gt;
* &#039;&#039;&#039;element&#039;&#039;&#039; - The untranslated extension name i.e. mod_custom&lt;br /&gt;
* &#039;&#039;&#039;type&#039;&#039;&#039; - The extension type (component, module, plugin, etc.)&lt;br /&gt;
* &#039;&#039;&#039;version&#039;&#039;&#039; - The latest version of the extension&lt;br /&gt;
* &#039;&#039;&#039;detailsurl&#039;&#039;&#039; - The URL of the XML file which contains that extension&#039;s individual update definitions&lt;br /&gt;
&lt;br /&gt;
== Extension ==&lt;br /&gt;
The extension server type allows developers to define an extension&#039;s manifest to pull updates from a single extension&#039;s manifest.  All collection manifests eventually point to this XML file.  All updates in this file must be defined after an &amp;lt;updates&amp;gt; tag at the beginning of the file.  The below example is the update definition for the Joomla! 1.7.0 release:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
 &amp;lt;update&amp;gt;&lt;br /&gt;
    &amp;lt;name&amp;gt;Joomla! 1.7&amp;lt;/name&amp;gt;&lt;br /&gt;
    &amp;lt;description&amp;gt;Joomla! 1.7 CMS&amp;lt;/description&amp;gt;&lt;br /&gt;
    &amp;lt;element&amp;gt;joomla&amp;lt;/element&amp;gt;&lt;br /&gt;
    &amp;lt;type&amp;gt;file&amp;lt;/type&amp;gt;&lt;br /&gt;
    &amp;lt;version&amp;gt;1.7.0&amp;lt;/version&amp;gt;&lt;br /&gt;
    &amp;lt;infourl title=&amp;quot;Joomla!&amp;quot;&amp;gt;http://www.joomla.org/&amp;lt;/infourl&amp;gt;&lt;br /&gt;
    &amp;lt;downloads&amp;gt;&lt;br /&gt;
        &amp;lt;downloadurl type=&amp;quot;full&amp;quot; format=&amp;quot;zip&amp;quot;&amp;gt;http://joomlacode.org/gf/download/frsrelease/15279/66552/Joomla_1.6.5_to_1.7.0_Package.zip&amp;lt;/downloadurl&amp;gt;&lt;br /&gt;
    &amp;lt;/downloads&amp;gt;&lt;br /&gt;
    &amp;lt;tags&amp;gt;&lt;br /&gt;
        &amp;lt;tag&amp;gt;stable&amp;lt;/tag&amp;gt;&lt;br /&gt;
    &amp;lt;/tags&amp;gt;&lt;br /&gt;
    &amp;lt;maintainer&amp;gt;Sam Moffatt&amp;lt;/maintainer&amp;gt;&lt;br /&gt;
    &amp;lt;maintainerurl&amp;gt;http://sammoffatt.com.au&amp;lt;/maintainerurl&amp;gt;&lt;br /&gt;
    &amp;lt;section&amp;gt;Testing&amp;lt;/section&amp;gt;&lt;br /&gt;
    &amp;lt;targetplatform name=&amp;quot;joomla&amp;quot; version=&amp;quot;1.6&amp;quot;/&amp;gt;&lt;br /&gt;
 &amp;lt;/update&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The following section describes the elements of a single update entity.&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;&#039;name&#039;&#039;&#039; - The name of the extension, this name will appear in the Name column of the Extension Manager&#039;s Update view (required)&lt;br /&gt;
* &#039;&#039;&#039;description&#039;&#039;&#039; - A short description of the extension (optional) -- if you choose to use &amp;lt;![CDATA[]]&amp;gt;, double-quotes will break the HTML formatting.  Use single quotes with your HTML entities.&lt;br /&gt;
* &#039;&#039;&#039;element&#039;&#039;&#039; - The installed name of the extension (required)&lt;br /&gt;
* &#039;&#039;&#039;type&#039;&#039;&#039; - The type of extension (component, module, plugin, etc.) (required)&lt;br /&gt;
* &#039;&#039;&#039;folder&#039;&#039;&#039; - Specific to plugins, this tag describes the type of plugin being updated (content, system, etc.) (required for plugins)&lt;br /&gt;
* &#039;&#039;&#039;client&#039;&#039;&#039; - The client name of the extension (&amp;quot;site&amp;quot; or &amp;quot;administrator&amp;quot;) (usually optional, required for J! 1.7.3 due to a regression; but highly encouraged regardless). Plugins are automatically installed with a client of &amp;quot;site&amp;quot;, but you will need to specify the client in an update or it will use &amp;quot;administrator&amp;quot;. Note that the tag is &amp;quot;client&amp;quot; not &amp;quot;client_id&amp;quot;.&lt;br /&gt;
* &#039;&#039;&#039;version&#039;&#039;&#039; - The version of the release (required)&lt;br /&gt;
* &#039;&#039;&#039;infourl&#039;&#039;&#039; - A URL to point users to containing information about the update (optional) (In CMS 2.5, if set, this URL will be displayed in the update view)&lt;br /&gt;
* &#039;&#039;&#039;downloads&#039;&#039;&#039; - The section which lists all download locations&lt;br /&gt;
** &#039;&#039;&#039;downloadurl&#039;&#039;&#039; - The URL to download the extension from; the &amp;lt;downloadurl&amp;gt; tag has two required parameters:&lt;br /&gt;
*** &#039;&#039;&#039;type&#039;&#039;&#039; - The type of package (full or upgrade)&lt;br /&gt;
*** &#039;&#039;&#039;format&#039;&#039;&#039; - The format of the package (zip, tar, etc.)&lt;br /&gt;
* &#039;&#039;&#039;tags&#039;&#039;&#039; - Optional (unknown use)&lt;br /&gt;
* &#039;&#039;&#039;maintainer&#039;&#039;&#039; - The name of the extension maintainer (similar to the &amp;lt;author&amp;gt; tag in a manifest) (optional)&lt;br /&gt;
* &#039;&#039;&#039;maintainerurl&#039;&#039;&#039; - The website of the extension maintainer (similar to the &amp;lt;authorUrl&amp;gt; tag in a manifest) (optional)&lt;br /&gt;
* &#039;&#039;&#039;section&#039;&#039;&#039; - Optional (unknown use)&lt;br /&gt;
* &#039;&#039;&#039;targetplatform&#039;&#039;&#039; - A tag to define platform requirements, requires the following elements&lt;br /&gt;
** &#039;&#039;&#039;name&#039;&#039;&#039; - The name of the platform dependency; as of this writing, it should ONLY be &amp;quot;joomla&amp;quot;&lt;br /&gt;
** &#039;&#039;&#039;version&#039;&#039;&#039; - The version of Joomla! the extension supports&lt;br /&gt;
*** &#039;&#039;&#039;Note:&#039;&#039;&#039; If your extension is Joomla! 1.6, 1.7, and/or 2.5 compatible, you will be required to have separate &amp;lt;update&amp;gt; definitions for each version due to the manner in which the updater checks the version&lt;br /&gt;
&lt;br /&gt;
A separate &amp;lt;update&amp;gt; definition will be required for each version of your extension you release.&lt;br /&gt;
&lt;br /&gt;
= Supporting Tools =&lt;br /&gt;
Maintaining your update server files can be difficult depending on the manner in which you set up your files.  An extension which can help you to maintain this is the Akeeba Release System, available free of charge from https://www.akeebabackup.com&lt;br /&gt;
&lt;br /&gt;
= Contributors =&lt;br /&gt;
*[[User:mbabker|Michael Babker]]&lt;/div&gt;</summary>
		<author><name>Znarf</name></author>
	</entry>
</feed>