1

I am using the Joomla Rating System, and I'm trying to change the code slightly. Right now, there is a possibility that a user can rate more than once on one article.

I have already changed the star voting system from 5 stars to 10 stars, but I can't manage to limit users to one vote per article.

I think the change should be made in: /components/com_content/models/article.php

Here is the original code:

/**
 * @package     Joomla.Site
 * @subpackage  com_content
 *
 * @copyright   Copyright (C) 2005 - 2016 Open Source Matters, Inc. All rights reserved.
 * @license     GNU General Public License version 2 or later; see LICENSE.txt
 */

defined('_JEXEC') or die;

use Joomla\Registry\Registry;

/**
 * Content Component Article Model
 *
 * @since  1.5
 */
class ContentModelArticle extends JModelItem
{
    /**
     * Model context string.
     *
     * @var        string
     */
    protected $_context = 'com_content.article';

    /**
     * Method to auto-populate the model state.
     *
     * Note. Calling getState in this method will result in recursion.
     *
     * @since   1.6
     *
     * @return void
     */
    protected function populateState()
    {
            $app = JFactory::getApplication('site');

            // Load state from the request.
            $pk = $app->input->getInt('id');
            $this->setState('article.id', $pk);

            $offset = $app->input->getUInt('limitstart');
            $this->setState('list.offset', $offset);

            // Load the parameters.
            $params = $app->getParams();
            $this->setState('params', $params);

            // TODO: Tune these values based on other permissions.
            $user = JFactory::getUser();

            if ((!$user->authorise('core.edit.state', 'com_content')) && (!$user->authorise('core.edit', 'com_content')))
            {
                    $this->setState('filter.published', 1);
                    $this->setState('filter.archived', 2);
            }

            $this->setState('filter.language', JLanguageMultilang::isEnabled());
    }

/**
 * Method to get article data.
 *
 * @param   integer  $pk  The id of the article.
 *
 * @return  object|boolean|JException  Menu item data object on success, boolean false or JException instance on error
 */
    public function getItem($pk = null)
    {
            $user = JFactory::getUser();

            $pk = (!empty($pk)) ? $pk : (int) $this->getState('article.id');

            if ($this->_item === null)
            {
                    $this->_item = array();
            }

            if (!isset($this->_item[$pk]))
            {
                    try
                    {
                            $db = $this->getDbo();
                            $query = $db->getQuery(true)
                                    ->select(
                                            $this->getState(
                                                    'item.select', 'a.id, a.asset_id, a.title, a.alias, a.introtext, a.fulltext, ' .
                                                    // If badcats is not null, this means that the article is inside an unpublished category
                                                    // In this case, the state is set to 0 to indicate Unpublished (even if the article state is Published)
                                                    'CASE WHEN badcats.id is null THEN a.state ELSE 0 END AS state, ' .
                                                    'a.catid, a.created, a.created_by, a.created_by_alias, ' .
                                                    // Use created if modified is 0
                                                    'CASE WHEN a.modified = ' . $db->quote($db->getNullDate()) . ' THEN a.created ELSE a.modified END as modified, ' .
                                                    'a.modified_by, a.checked_out, a.checked_out_time, a.publish_up, a.publish_down, ' .
                                                    'a.images, a.urls, a.attribs, a.version, a.ordering, ' .
                                                    'a.metakey, a.metadesc, a.access, a.hits, a.metadata, a.featured, a.language, a.xreference'
                                            )
                                    );
                            $query->from('#__content AS a');

                            // Join on category table.
                            $query->select('c.title AS category_title, c.alias AS category_alias, c.access AS category_access')
                                    ->join('LEFT', '#__categories AS c on c.id = a.catid');

                            // Join on user table.
                            $query->select('u.name AS author')
                                    ->join('LEFT', '#__users AS u on u.id = a.created_by');

                            // Filter by language
                            if ($this->getState('filter.language'))
                            {
                                    $query->where('a.language in (' . $db->quote(JFactory::getLanguage()->getTag()) . ',' . $db->quote('*') . ')');
                            }

                            // Join over the categories to get parent category titles
                            $query->select('parent.title as parent_title, parent.id as parent_id, parent.path as parent_route, parent.alias as parent_alias')
                                    ->join('LEFT', '#__categories as parent ON parent.id = c.parent_id');

                            // Join on voting table
                            $query->select('ROUND(v.rating_sum / v.rating_count, 0) AS rating, v.rating_count as rating_count')
                                    ->join('LEFT', '#__content_rating AS v ON a.id = v.content_id')

                                   ->where('a.id = ' . (int) $pk);

                            if ((!$user->authorise('core.edit.state', 'com_content')) && (!$user->authorise('core.edit', 'com_content')))
                            {
                                    // Filter by start and end dates.
                                    $nullDate = $db->quote($db->getNullDate());
                                    $date = JFactory::getDate();

                                    $nowDate = $db->quote($date->toSql());

                                    $query->where('(a.publish_up = ' . $nullDate . ' OR a.publish_up <= ' . $nowDate . ')')
                                            ->where('(a.publish_down = ' . $nullDate . ' OR a.publish_down >= ' . $nowDate . ')');
                            }

                            // Join to check for category published state in parent categories up the tree
                            // If all categories are published, badcats.id will be null, and we just use the article state
                            $subquery = ' (SELECT cat.id as id FROM #__categories AS cat JOIN #__categories AS parent ';
                            $subquery .= 'ON cat.lft BETWEEN parent.lft AND parent.rgt ';
                            $subquery .= 'WHERE parent.extension = ' . $db->quote('com_content');
                            $subquery .= ' AND parent.published <= 0 GROUP BY cat.id)';
                            $query->join('LEFT OUTER', $subquery . ' AS badcats ON badcats.id = c.id');

                            // Filter by published state.
                            $published = $this->getState('filter.published');
                            $archived = $this->getState('filter.archived');

                            if (is_numeric($published))
                            {
                                    $query->where('(a.state = ' . (int) $published . ' OR a.state =' . (int) $archived . ')');
                            }

                            $db->setQuery($query);

                            $data = $db->loadObject();

                            if (empty($data))
                            {
                                    return JError::raiseError(404, JText::_('COM_CONTENT_ERROR_ARTICLE_NOT_FOUND'));
                            }

                            // Check for published state if filter set.
                            if (((is_numeric($published)) || (is_numeric($archived))) && (($data->state != $published) && ($data->state != $archived)))
                            {
                                    return JError::raiseError(404, JText::_('COM_CONTENT_ERROR_ARTICLE_NOT_FOUND'));
                            }

                            // Convert parameter fields to objects.
                            $registry = new Registry;
                            $registry->loadString($data->attribs);

                            $data->params = clone $this->getState('params');
                            $data->params->merge($registry);

                            $registry = new Registry;
                            $registry->loadString($data->metadata);
                            $data->metadata = $registry;

                            // Technically guest could edit an article, but lets not check that to improve performance a little.
                            if (!$user->get('guest'))
                            {
                                    $userId = $user->get('id');

                                 $asset = 'com_content.article.' . $data->id;

                                    // Check general edit permission first.
                                    if ($user->authorise('core.edit', $asset))
                                    {
                                            $data->params->set('access-edit', true);
                                    }

                                    // Now check if edit.own is available.
                                    elseif (!empty($userId) && $user->authorise('core.edit.own', $asset))
                                    {
                                            // Check for a valid user and that they are the owner.
                                            if ($userId == $data->created_by)
                                            {
                                                    $data->params->set('access-edit', true);
                                            }
                                    }
                            }

                            // Compute view access permissions.
                            if ($access = $this->getState('filter.access'))
                            {
                                    // If the access filter has been set, we already know this user can view.
                                    $data->params->set('access-view', true);
                            }
                            else
                            {
                                    // If no access filter is set, the layout takes some responsibility for display of limited information.
                                    $user = JFactory::getUser();
                                    $groups = $user->getAuthorisedViewLevels();

                                    if ($data->catid == 0 || $data->category_access === null)
                                    {
                                            $data->params->set('access-view', in_array($data->access, $groups));
                                    }
                                    else
                                    {
                                            $data->params->set('access-view', in_array($data->access, $groups) && in_array($data->category_access, $groups));
                                    }
                            }

                            $this->_item[$pk] = $data;
                    }
                    catch (Exception $e)
                    {
                            if ($e->getCode() == 404)
                            {
                                    // Need to go thru the error handler to allow Redirect to work.
                                    JError::raiseError(404, $e->getMessage());
                            }
                            else
                            {
                                    $this->setError($e);
                                    $this->_item[$pk] = false;
                            }
                    }
            }

            return $this->_item[$pk];
    }

    /**
     * Increment the hit counter for the article.
     *
     * @param   integer  $pk  Optional primary key of the article to increment.
     *
     * @return  boolean  True if successful; false otherwise and internal error set.
     */
    public function hit($pk = 0)
    {
            $input = JFactory::getApplication()->input;
            $hitcount = $input->getInt('hitcount', 1);

            if ($hitcount)
            {
                    $pk = (!empty($pk)) ? $pk : (int) $this->getState('article.id');

                    $table = JTable::getInstance('Content', 'JTable');
                    $table->load($pk);
                    $table->hit($pk);
            }

            return true;
    }

    /**
     * Save user vote on article
     *
     * @param   integer  $pk    Joomla Article Id
     * @param   integer  $rate  Voting rate
     *
     * @return  boolean          Return true on success
     */
    public function storeVote($pk = 0, $rate = 0)
    {
            if ($rate >= 1 && $rate <= 10 && $pk > 0)
            {
                    $userIP = $_SERVER['REMOTE_ADDR'];

                    // Initialize variables.
                    $db    = $this->getDbo();
                    $query = $db->getQuery(true);

                    // Create the base select statement.
                    $query->select('*')
                            ->from($db->quoteName('#__content_rating'))
                            ->where($db->quoteName('content_id') . ' = ' . (int) $pk);

                    // Set the query and load the result.
                    $db->setQuery($query);

                    // Check for a database error.
                    try
                    {
                            $rating = $db->loadObject();
                    }
                    catch (RuntimeException $e)
                    {
                            JError::raiseWarning(500, $e->getMessage());

                            return false;
                    }

                    // There are no ratings yet, so lets insert our rating

                 if (!$rating)
                    {
                            $query = $db->getQuery(true);

                            // Create the base insert statement.
                            $query->insert($db->quoteName('#__content_rating'))
                                    ->columns(array($db->quoteName('content_id'), $db->quoteName('lastip'), $db->quoteName('rating_sum'), $db->quoteName('rating_count')))
                                    ->values((int) $pk . ', ' . $db->quote($userIP) . ',' . (int) $rate . ', 1');

                            // Set the query and execute the insert.
                            $db->setQuery($query);

                            try
                            {
                                    $db->execute();
                            }
                            catch (RuntimeException $e)
                            {
                                    JError::raiseWarning(500, $e->getMessage());

                                    return false;
                            }
                    }
                    else
                    {
                            if ($userIP != ($rating->lastip))
                            {
                                    $query = $db->getQuery(true);

                                    // Create the base update statement.
                                    $query->update($db->quoteName('#__content_rating'))
                                            ->set($db->quoteName('rating_count') . ' = rating_count + 1')
                                            ->set($db->quoteName('rating_sum') . ' = rating_sum + ' . (int) $rate)
                                            ->set($db->quoteName('lastip') . ' = ' . $db->quote($userIP))
                                            ->where($db->quoteName('content_id') . ' = ' . (int) $pk);

                                    // Set the query and execute the update.
                                    $db->setQuery($query);

                                    try
                                    {
                                            $db->execute();
                                    }
                                    catch (RuntimeException $e)
                                    {
                                            JError::raiseWarning(500, $e->getMessage());

                                            return false;
                                    }
                            }
                            else
                            {
                                    return false;
                            }
                    }

                    return true;
            }

            JError::raiseWarning('SOME_ERROR_CODE', JText::sprintf('COM_CONTENT_INVALID_RATING', $rate), "JModelArticle::storeVote($rate)");

            return false;

}

When I change the line:

if ($userIP != ($rating->lastip))

to

if ($userIP != ($rating->userId)

then I can vote as often as I want, because there is no ip address check.

If I use the old code, then I can vote only one time per ip address, but of course when I disconnect my internet and connect again I can vote again.

Then I changed it to: if ($userId != ($rating->article.id) AND $userIP != ($rating->lastip))

but it's the same like in the original one.

Can somebody help me with the code because I am not a programmer and it was so hard for me to find the other things.

1 Answer 1

2

Your $rating variable contains the following fields: content_id, rating_sum, rating_count and lastip You can see this by examining the database table #__content_rating.

The ratings are not saved into the database table with an user ID so there is no way to achieve what you want, sorry.

When you change it to if ($userIP != ($rating->userId) the result is always true because $rating->userId is empty (because that field is not contained in your variable. Therefore you can vote as often as you want to.

When you change it to if ($userId != ($rating->article.id) AND $userIP != ($rating->lastip)) you will get the same result as in the original code as you already noticed. That is because $userId != ($rating->article.id) always validates to true (again, article.id is not a valid field in $rating)

Have a look at 3rd party plugins for rating systems at the Joomla Extension Directory. They might provide you with the functionality you need. Otherwise you would have to make more changes to core code which is highly inadvisable, because you might lose your custom code with a future update of your system.

4
  • Hello,Thank you very much for your answer, okay i understand that there is no Chance with an easy override of the Code to get that funcionality... they Problem is i have looked also for Jommly Extension but i have found no one which provide the Feature, to vote only one time for an article, i have buought item Rating Extension, but then i have looked in more deeper, and its not providing those function... do you or someone else know which files i have to Change in the Joomla Rating to get this to work? Thank you
    – Oliverb74
    Sep 27, 2016 at 13:00
  • one other question, you said _content_rating includes Content_id, when i make a query of the table the Content_id is the id from the article, maybe this can be used to get the article onely be voted one time?
    – Oliverb74
    Sep 27, 2016 at 13:28
  • Yes that could be used to get the article voted only one single time, but not one time per user (which is probably what you want). Maybe you should have a look at other plugins too.
    – fruppel
    Sep 27, 2016 at 13:35
  • oh okay, yes thats what i want one article voting only for one user and all other users also for one time. Okay then i will look arround if i find a other plugin. thank you very much for your help
    – Oliverb74
    Sep 27, 2016 at 13:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.