MDL-27471 Adding mandatory component & ratingarea to the ratings API + other fixes
authorSam Hemelryk <sam@moodle.com>
Wed, 11 May 2011 05:32:36 +0000 (13:32 +0800)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 23 May 2011 10:35:39 +0000 (12:35 +0200)
* Added the component and ratingarea fields and implemented it throughout
  the rating API as mandatory fields
* Cleanup rating indexes
* Upgrade forum/data/glossary ratings
* Moved the logic in the render_rating method to methods of the rating object.
* Added new callback for checking ratingareas
* Cleaned comments here and there
* Mark the xxx_get_participants methods as deprecated
* Refactor rate_ajax and ratingsuser_can_view_aggregate methods
* Cleaned up rating/index.php to use html_table object and moved inline styles to CSS.
* Added missing properties of the rating object that were being set throughout the rating
  API.

47 files changed:
course/moodleform_mod.php
lang/en/error.php
lib/db/install.xml
lib/db/upgrade.php
lib/outputrenderers.php
lib/rsslib.php
mod/assignment/lib.php
mod/chat/lib.php
mod/choice/lib.php
mod/data/backup/moodle2/backup_data_stepslib.php
mod/data/backup/moodle2/restore_data_stepslib.php
mod/data/db/upgrade.php
mod/data/lib.php
mod/data/version.php
mod/data/view.php
mod/feedback/lib.php
mod/folder/lib.php
mod/forum/backup/moodle2/backup_forum_stepslib.php
mod/forum/backup/moodle2/restore_forum_stepslib.php
mod/forum/db/upgrade.php
mod/forum/lib.php
mod/forum/user.php
mod/forum/version.php
mod/forum/view.php
mod/glossary/backup/moodle2/backup_glossary_stepslib.php
mod/glossary/backup/moodle2/restore_glossary_stepslib.php
mod/glossary/db/upgrade.php
mod/glossary/deleteentry.php
mod/glossary/lib.php
mod/glossary/version.php
mod/glossary/view.php
mod/imscp/lib.php
mod/label/lib.php
mod/lesson/lib.php
mod/page/lib.php
mod/quiz/lib.php
mod/resource/lib.php
mod/survey/lib.php
mod/url/lib.php
mod/wiki/lib.php
mod/workshop/lib.php
rating/index.php
rating/lib.php
rating/rate.php
rating/rate_ajax.php
theme/base/style/core.css
version.php

index 0482168..136e6c2 100644 (file)
@@ -351,7 +351,7 @@ abstract class moodleform_mod extends moodleform {
 
         if ($this->_features->rating) {
             require_once($CFG->dirroot.'/rating/lib.php');
-            $rm = new rating_manager();
+            $rm = new rating_manager();;
 
             $mform->addElement('header', 'modstandardratings', get_string('ratings', 'rating'));
 
index d7199dc..20004e0 100644 (file)
@@ -303,6 +303,7 @@ $string['invalidpagesize'] = 'Invalid page size';
 $string['invalidpasswordpolicy'] = 'Invalid password policy';
 $string['invalidpaymentmethod'] = 'Invalid payment method: {$a}';
 $string['invalidqueryparam'] = 'ERROR: Incorrect number of query parameters. Expected {$a->expected}, got {$a->actual}.';
+$string['invalidratingarea'] = 'Invalid rating area';
 $string['invalidrecord'] = 'Can not find data record in database table {$a}.';
 $string['invalidrecordunknown'] = 'Can not find data record in database.';
 $string['invalidrequest'] = 'Invalid request';
index 27b3c1e..5c1397d 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20110209" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20110523" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
     <TABLE NAME="rating" COMMENT="moodle ratings" PREVIOUS="blog_external" NEXT="license">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="contextid"/>
-        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="itemid"/>
-        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="scaleid"/>
+        <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="component"/>
+        <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" PREVIOUS="contextid" NEXT="ratingarea"/>
+        <FIELD NAME="ratingarea" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="component" NEXT="itemid"/>
+        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="ratingarea" NEXT="scaleid"/>
         <FIELD NAME="scaleid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="false" SEQUENCE="false" PREVIOUS="itemid" NEXT="rating"/>
         <FIELD NAME="rating" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="scaleid" NEXT="userid"/>
         <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="rating" NEXT="timecreated"/>
         <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" COMMENT="Relates to user.id" PREVIOUS="contextid"/>
       </KEYS>
       <INDEXES>
-        <INDEX NAME="itemid" UNIQUE="false" FIELDS="itemid"/>
+        <INDEX NAME="uniqueuserrating" UNIQUE="false" FIELDS="component, ratingarea, contextid, itemid" COMMENT="These fields define a unique user rating of an item"/>
       </INDEXES>
     </TABLE>
     <TABLE NAME="license" COMMENT="store licenses used by moodle" PREVIOUS="rating" NEXT="registration_hubs">
       </KEYS>
     </TABLE>
   </TABLES>
-</XMLDB>
+</XMLDB>
\ No newline at end of file
index b47810c..2918eb1 100644 (file)
@@ -6062,6 +6062,56 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
         upgrade_main_savepoint(true, 2011022100.01);
     }
 
+    if ($oldversion < 2011052300.00) {
+        $table = new xmldb_table('rating');
+
+        // Add the component field to the ratings table
+        upgrade_set_timeout(60 * 20);
+        $field = new xmldb_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, 'unknown', 'contextid');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Add the ratingarea field to the ratings table
+        upgrade_set_timeout(60 * 20);
+        $field = new xmldb_field('ratingarea', XMLDB_TYPE_CHAR, '50', null, XMLDB_NOTNULL, null, 'unknown', 'component');
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        upgrade_main_savepoint(true, 2011052300.00);
+    }
+
+    if ($oldversion < 2011052300.01) {
+
+        // Define index uniqueuserrating (unique) to be added to rating
+        $table = new xmldb_table('rating');
+        $index = new xmldb_index('uniqueuserrating', XMLDB_INDEX_NOTUNIQUE, array('component', 'ratingarea', 'contextid', 'itemid'));
+
+        // Conditionally launch add index uniqueuserrating
+        if (!$dbman->index_exists($table, $index)) {
+            $dbman->add_index($table, $index);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2011052300.01);
+    }
+
+    if ($oldversion < 2011052300.02) {
+
+        // Define index itemid (not unique) to be dropped form rating
+        $table = new xmldb_table('rating');
+        $index = new xmldb_index('itemid', XMLDB_INDEX_NOTUNIQUE, array('itemid'));
+
+        // Conditionally launch drop index itemid
+        if ($dbman->index_exists($table, $index)) {
+            $dbman->drop_index($table, $index);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2011052300.02);
+    }
+
 
     return true;
 }
index 2cc0d1d..786444f 100644 (file)
@@ -1410,95 +1410,38 @@ class core_renderer extends renderer_base {
     */
     function render_rating(rating $rating) {
         global $CFG, $USER;
-        static $havesetupjavascript = false;
 
-        if( $rating->settings->aggregationmethod == RATING_AGGREGATE_NONE ){
+        if ($rating->settings->aggregationmethod == RATING_AGGREGATE_NONE) {
             return null;//ratings are turned off
         }
 
-        $useajax = !empty($CFG->enableajax);
-
-        //include required Javascript
-        if( !$havesetupjavascript && $useajax ) {
-            $this->page->requires->js_init_call('M.core_rating.init');
-            $havesetupjavascript = true;
-        }
-
-        //check the item we're rating was created in the assessable time window
-        $inassessablewindow = true;
-        if ( $rating->settings->assesstimestart && $rating->settings->assesstimefinish ) {
-            if ($rating->itemtimecreated < $rating->settings->assesstimestart || $rating->itemtimecreated > $rating->settings->assesstimefinish) {
-                $inassessablewindow = false;
-            }
-        }
+        $ratingmanager = new rating_manager();
+        // Initialise the JavaScript so ratings can be done by AJAX.
+        $ratingmanager->initialise_rating_javascript($this->page);
 
         $strrate = get_string("rate", "rating");
         $ratinghtml = ''; //the string we'll return
 
-        //permissions check - can they view the aggregate?
-        $canviewaggregate = false;
-
-        //if its the current user's item and they have permission to view the aggregate on their own items
-        if ( $rating->itemuserid==$USER->id && $rating->settings->permissions->view && $rating->settings->pluginpermissions->view) {
-            $canviewaggregate = true;
-        }
-
-        //if the item doesnt belong to anyone or its another user's items and they can see the aggregate on items they don't own
-        //Note that viewany doesnt mean you can see the aggregate or ratings of your own items
-        if ( (empty($rating->itemuserid) or $rating->itemuserid!=$USER->id) && $rating->settings->permissions->viewany && $rating->settings->pluginpermissions->viewany ) {
-            $canviewaggregate = true;
-        }
-
-        if ($canviewaggregate==true) {
-            $aggregatelabel = '';
-            switch ($rating->settings->aggregationmethod) {
-                case RATING_AGGREGATE_AVERAGE :
-                    $aggregatelabel .= get_string("aggregateavg", "rating");
-                    break;
-                case RATING_AGGREGATE_COUNT :
-                    $aggregatelabel .= get_string("aggregatecount", "rating");
-                    break;
-                case RATING_AGGREGATE_MAXIMUM :
-                    $aggregatelabel .= get_string("aggregatemax", "rating");
-                    break;
-                case RATING_AGGREGATE_MINIMUM :
-                    $aggregatelabel .= get_string("aggregatemin", "rating");
-                    break;
-                case RATING_AGGREGATE_SUM :
-                    $aggregatelabel .= get_string("aggregatesum", "rating");
-                    break;
-            }
-            $aggregatelabel .= get_string('labelsep', 'langconfig');
+        // permissions check - can they view the aggregate?
+        if ($rating->user_can_view_aggregate()) {
 
-            //$scalemax = 0;//no longer displaying scale max
-            $aggregatestr = '';
+            $aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
+            $aggregatestr   = $rating->get_aggregate_string();
 
-            //only display aggregate if aggregation method isn't COUNT
-            if ($rating->aggregate && $rating->settings->aggregationmethod!= RATING_AGGREGATE_COUNT) {
-                if ($rating->settings->aggregationmethod!= RATING_AGGREGATE_SUM && is_array($rating->settings->scale->scaleitems)) {
-                    $aggregatestr .= $rating->settings->scale->scaleitems[round($rating->aggregate)];//round aggregate as we're using it as an index
-                }
-                else { //aggregation is SUM or the scale is numeric
-                    $aggregatestr .= round($rating->aggregate,1);
-                }
+            $aggregatehtml  = html_writer::tag('span', $aggregatestr, array('id' => 'ratingaggregate'.$rating->itemid)).' ';
+            $aggregatehtml .= html_writer::start_tag('span', array('id'=>"ratingcount{$rating->itemid}"));
+            if ($rating->count > 0) {
+                $aggregatehtml .= "({$rating->count})";
             } else {
-                $aggregatestr = '';
-            }
-
-            $countstr = html_writer::start_tag('span', array('id'=>"ratingcount{$rating->itemid}"));
-            if ($rating->count>0) {
-                $countstr .= "({$rating->count})";
+                $aggregatehtml .= '-';
             }
-            $countstr .= html_writer::end_tag('span');
-
-            //$aggregatehtml = "{$ratingstr} / $scalemax ({$rating->count}) ";
-            $aggregatehtml = "<span id='ratingaggregate{$rating->itemid}'>{$aggregatestr}</span> $countstr ";
+            $aggregatehtml .= html_writer::end_tag('span').' ';
 
             $ratinghtml .= html_writer::tag('span', $aggregatelabel, array('class'=>'rating-aggregate-label'));
             if ($rating->settings->permissions->viewall && $rating->settings->pluginpermissions->viewall) {
-                $url = "/rating/index.php?contextid={$rating->context->id}&itemid={$rating->itemid}&scaleid={$rating->settings->scale->id}";
-                $nonpopuplink = new moodle_url($url);
-                $popuplink = new moodle_url("$url&popup=1");
+
+                $nonpopuplink = $rating->get_view_ratings_url();
+                $popuplink = $rating->get_view_ratings_url(true);
 
                 $action = new popup_action('click', $popuplink, 'ratings', array('height' => 400, 'width' => 600));
                 $ratinghtml .= $this->action_link($nonpopuplink, $aggregatehtml, $action);
@@ -1508,81 +1451,45 @@ class core_renderer extends renderer_base {
         }
 
         $formstart = null;
-        //if the item doesn't belong to the current user, the user has permission to rate
-        //and we're within the assessable period
-        if ($rating->itemuserid!=$USER->id
-            && $rating->settings->permissions->rate
-            && $rating->settings->pluginpermissions->rate
-            && $inassessablewindow) {
-
-            //start the rating form
-            $formstart = html_writer::start_tag('form',
-                array('id'=>"postrating{$rating->itemid}", 'class'=>'postratingform', 'method'=>'post', 'action'=>"{$CFG->wwwroot}/rating/rate.php"));
-
-            $formstart .= html_writer::start_tag('div', array('class'=>'ratingform'));
-
-            //add the hidden inputs
-
-            $attributes = array('type'=>'hidden', 'class'=>'ratinginput', 'name'=>'contextid', 'value'=>$rating->context->id);
-            $formstart .= html_writer::empty_tag('input', $attributes);
-
-            $attributes['name'] = 'component';
-            $attributes['value'] = $rating->settings->component;
-            $formstart .= html_writer::empty_tag('input', $attributes);
-
-            $attributes['name'] = 'itemid';
-            $attributes['value'] = $rating->itemid;
-            $formstart .= html_writer::empty_tag('input', $attributes);
-
-            $attributes['name'] = 'scaleid';
-            $attributes['value'] = $rating->settings->scale->id;
-            $formstart .= html_writer::empty_tag('input', $attributes);
+        // if the item doesn't belong to the current user, the user has permission to rate
+        // and we're within the assessable period
+        if ($rating->user_can_rate()) {
 
-            $attributes['name'] = 'returnurl';
-            $attributes['value'] = $rating->settings->returnurl;
-            $formstart .= html_writer::empty_tag('input', $attributes);
+            $rateurl = $rating->get_rate_url();
+            $inputs = $rateurl->params();
 
-            $attributes['name'] = 'rateduserid';
-            $attributes['value'] = $rating->itemuserid;
-            $formstart .= html_writer::empty_tag('input', $attributes);
-
-            $attributes['name'] = 'aggregation';
-            $attributes['value'] = $rating->settings->aggregationmethod;
-            $formstart .= html_writer::empty_tag('input', $attributes);
-
-            $attributes['name'] = 'sesskey';
-            $attributes['value'] = sesskey();;
-            $formstart .= html_writer::empty_tag('input', $attributes);
+            //start the rating form
+            $formattrs = array(
+                'id'     => "postrating{$rating->itemid}",
+                'class'  => 'postratingform',
+                'method' => 'post',
+                'action' => $rateurl->out_omit_querystring()
+            );
+            $formstart  = html_writer::start_tag('form', $formattrs);
+            $formstart .= html_writer::start_tag('div', array('class' => 'ratingform'));
+
+            // add the hidden inputs
+            foreach ($inputs as $name => $value) {
+                $attributes = array('type' => 'hidden', 'class' => 'ratinginput', 'name' => $name, 'value' => $value);
+                $formstart .= html_writer::empty_tag('input', $attributes);
+            }
 
             if (empty($ratinghtml)) {
                 $ratinghtml .= $strrate.': ';
             }
-
             $ratinghtml = $formstart.$ratinghtml;
 
-            //generate an array of values for numeric scales
-            $scalearray = $rating->settings->scale->scaleitems;
-            if (!is_array($scalearray)) { //almost certainly a numerical scale
-                $intscalearray = intval($scalearray);//just in case they've passed "5" instead of 5
-                $scalearray = array();
-                if( is_int($intscalearray) && $intscalearray>0 ) {
-                    for($i=0; $i<=$rating->settings->scale->scaleitems; $i++) {
-                        $scalearray[$i] = $i;
-                    }
-                }
-            }
-
-            $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $scalearray;
-            $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid));
+            $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $rating->settings->scale->scaleitems;
+            $scaleattrs = array('class'=>'postratingmenu ratinginput','id'=>'menurating'.$rating->itemid);
+            $ratinghtml .= html_writer::select($scalearray, 'rating', $rating->rating, false, $scaleattrs);
 
             //output submit button
-
             $ratinghtml .= html_writer::start_tag('span', array('class'=>"ratingsubmit"));
 
-            $attributes = array('type'=>'submit', 'class'=>'postratingmenusubmit', 'id'=>'postratingsubmit'.$rating->itemid, 'value'=>s(get_string('rate', 'rating')));
+            $attributes = array('type' => 'submit', 'class' => 'postratingmenusubmit', 'id' => 'postratingsubmit'.$rating->itemid, 'value' => s(get_string('rate', 'rating')));
             $ratinghtml .= html_writer::empty_tag('input', $attributes);
 
-            if (is_array($rating->settings->scale->scaleitems)) {
+            if (!$rating->settings->scale->isnumeric) {
                 $ratinghtml .= $this->help_icon_scale($rating->settings->scale->courseid, $rating->settings->scale);
             }
             $ratinghtml .= html_writer::end_tag('span');
index 3159960..15dd6eb 100644 (file)
@@ -280,7 +280,7 @@ function rss_add_items($items) {
             $result .= rss_add_enclosures($item);
             $result .= rss_full_tag('pubDate',3,false,gmdate('D, d M Y H:i:s',$item->pubdate).' GMT');  # MDL-12563
             //Include the author if exists
-            if (isset($item->author)) {
+            if (isset($item->author) && !empty($item->author)) {
                 //$result .= rss_full_tag('author',3,false,$item->author);
                 //We put it in the description instead because it's more important
                 //for moodle than most other feeds, and most rss software seems to ignore
index c681a3c..eed4da0 100644 (file)
@@ -2772,6 +2772,8 @@ function assignment_grade_item_delete($assignment) {
 /**
  * Returns the users with data in one assignment (students and teachers)
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param $assignmentid int
  * @return array of user objects
  */
index 6df859d..88bed4f 100644 (file)
@@ -430,7 +430,8 @@ function chat_cron () {
  * Returns the users with data in one chat
  * (users with records in chat_messages, students)
  *
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $chatid
  * @param int $groupid
  * @return array
index 4efe58b..6667305 100644 (file)
@@ -591,6 +591,8 @@ function choice_delete_instance($id) {
  * Returns the users with data in one choice
  * (users with records in choice_responses, students)
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $choiceid
  * @return array
  */
index 691b059..5a155bd 100644 (file)
@@ -69,7 +69,7 @@ class backup_data_activity_structure_step extends backup_activity_structure_step
         $ratings = new backup_nested_element('ratings');
 
         $rating = new backup_nested_element('rating', array('id'), array(
-            'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
+            'component', 'ratingarea', 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
 
         // Build the tree
         $data->add_child($fields);
@@ -99,8 +99,10 @@ class backup_data_activity_structure_step extends backup_activity_structure_step
 
             $content->set_source_table('data_content', array('recordid' => backup::VAR_PARENTID));
 
-            $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
-                                                      'itemid'    => backup::VAR_PARENTID));
+            $rating->set_source_table('rating', array('contextid'  => backup::VAR_CONTEXTID,
+                                                      'itemid'     => backup::VAR_PARENTID,
+                                                      'component'  => 'mod_data',
+                                                      'ratingarea' => 'entry'));
             $rating->set_source_alias('rating', 'value');
         }
 
index 15af06a..e22ee2d 100644 (file)
@@ -138,6 +138,14 @@ class restore_data_activity_structure_step extends restore_activity_structure_st
         $data->timecreated = $this->apply_date_offset($data->timecreated);
         $data->timemodified = $this->apply_date_offset($data->timemodified);
 
+        // We need to check that component and ratingarea are both set here.
+        if (empty($data->component)) {
+            $data->component = 'mod_data';
+        }
+        if (empty($data->ratingarea)) {
+            $data->ratingarea = 'entry';
+        }
+
         $newitemid = $DB->insert_record('rating', $data);
     }
 
index 371da31..9e06d45 100644 (file)
@@ -315,6 +315,29 @@ function xmldb_data_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2010100101, 'data');
     }
 
+    if ($oldversion < 2011052300) {
+        // rating.component and rating.ratingarea have now been added as mandatory fields.
+        // Presently you can only rate data entries so component = 'mod_data' and ratingarea = 'entry'
+        // for all ratings with a data context.
+        // We want to update all ratings that belong to a data context and don't already have a
+        // component set.
+        // This could take a while reset upgrade timeout to 5 min
+        upgrade_set_timeout(60 * 20);
+        $sql = "UPDATE {rating}
+                SET component = 'mod_data', ratingarea = 'entry'
+                WHERE contextid IN (
+                    SELECT ctx.id
+                      FROM {context} ctx
+                      JOIN {course_modules} cm ON cm.id = ctx.instanceid
+                      JOIN {modules} m ON m.id = cm.module
+                     WHERE ctx.contextlevel = 70 AND
+                           m.name = 'data'
+                ) AND component = 'unknown'";
+        $DB->execute($sql);
+
+        upgrade_mod_savepoint(true, 2011052300, 'data');
+    }
+
     return true;
 }
 
index 4ab2ee5..284b275 100644 (file)
@@ -527,10 +527,10 @@ function data_generate_default_template(&$data, $template, $recordid=0, $form=fa
             $cell->attributes['class'] = 'controls';
             $table->data[] = new html_table_row(array($cell));
         } else if ($template == 'asearchtemplate') {
-            $row = new html_table_row(get_string('authorfirstname', 'data').': ', '##firstname##');
+            $row = new html_table_row(array(get_string('authorfirstname', 'data').': ', '##firstname##'));
             $row->attributes['class'] = 'searchcontrols';
             $table->data[] = $row;
-            $row = new html_table_row(get_string('authorlastname', 'data').': ', '##lastname##');
+            $row = new html_table_row(array(get_string('authorlastname', 'data').': ', '##lastname##'));
             $row->attributes['class'] = 'searchcontrols';
             $table->data[] = $row;
         }
@@ -1016,9 +1016,10 @@ function data_get_user_grades($data, $userid=0) {
     global $CFG;
 
     require_once($CFG->dirroot.'/rating/lib.php');
-    $rm = new rating_manager();
 
-    $ratingoptions = new stdclass();
+    $ratingoptions = new stdClass;
+    $ratingoptions->component = 'mod_data';
+    $ratingoptions->ratingarea = 'entry';
     $ratingoptions->modulename = 'data';
     $ratingoptions->moduleid   = $data->id;
 
@@ -1028,6 +1029,7 @@ function data_get_user_grades($data, $userid=0) {
     $ratingoptions->itemtable = 'data_records';
     $ratingoptions->itemtableusercolumn = 'userid';
 
+    $rm = new rating_manager();
     return $rm->get_user_grades($ratingoptions);
 }
 
@@ -1144,25 +1146,46 @@ function data_grade_item_delete($data) {
 /**
  * returns a list of participants of this database
  *
- * @global object
+ * Returns the users with data in one data
+ * (users with records in data_records, data_comments and ratings)
+ *
+ * @todo: deprecated - to be deleted in 2.2
+ *
+ * @param int $dataid
  * @return array
  */
 function data_get_participants($dataid) {
-// Returns the users with data in one data
-// (users with records in data_records, data_comments and ratings)
     global $DB;
 
-    $records = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
-                                       FROM {user} u, {data_records} r
-                                      WHERE r.dataid = ? AND u.id = r.userid", array($dataid));
-
-    $comments = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
-                                        FROM {user} u, {data_records} r, {comments} c
-                                       WHERE r.dataid = ? AND u.id = r.userid AND r.id = c.itemid AND c.commentarea='database_entry'", array($dataid));
-
-    $ratings = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
-                                       FROM {user} u, {data_records} r, {ratings} a
-                                      WHERE r.dataid = ? AND u.id = r.userid AND r.id = a.itemid", array($dataid));
+    $params = array('dataid' => $dataid);
+
+    $sql = "SELECT DISTINCT u.id, u.id
+              FROM {user} u,
+                   {data_records} r
+             WHERE r.dataid = :dataid AND
+                   u.id = r.userid";
+    $records = $DB->get_records_sql($sql, $params);
+
+    $sql = "SELECT DISTINCT u.id, u.id
+              FROM {user} u,
+                   {data_records} r,
+                   {comments} c
+             WHERE r.dataid = ? AND
+                   u.id = r.userid AND
+                   r.id = c.itemid AND
+                   c.commentarea = 'database_entry'";
+    $comments = $DB->get_records_sql($sql, $params);
+
+    $sql = "SELECT DISTINCT u.id, u.id
+              FROM {user} u,
+                   {data_records} r,
+                   {ratings} a
+             WHERE r.dataid = ? AND
+                   u.id = r.userid AND
+                   r.id = a.itemid AND
+                   a.component = 'mod_data' AND
+                   a.ratingarea = 'entry'";
+    $ratings = $DB->get_records_sql($sql, $params);
 
     $participants = array();
 
@@ -1357,20 +1380,23 @@ function data_print_template($template, $records, $data, $search='', $page=0, $r
 
 /**
  * Return rating related permissions
- * @param string $options the context id
+ *
+ * @param string $contextid the context id
+ * @param string $component the component to get rating permissions for
+ * @param string $ratingarea the rating area to get permissions for
  * @return array an associative array of the user's rating permissions
  */
-function data_rating_permissions($options) {
-    $contextid = $options;
-    $context = get_context_instance_by_id($contextid);
-
-    if (!$context) {
-        print_error('invalidcontext');
+function data_rating_permissions($contextid, $component, $ratingarea) {
+    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    if ($component != 'mod_data' || $ratingarea != 'entry') {
         return null;
-    } else {
-        $ret = new stdclass();
-        return array('view'=>has_capability('mod/data:viewrating',$context), 'viewany'=>has_capability('mod/data:viewanyrating',$context), 'viewall'=>has_capability('mod/data:viewallratings',$context), 'rate'=>has_capability('mod/data:rate',$context));
     }
+    return array(
+        'view'    => has_capability('mod/data:viewrating',$context),
+        'viewany' => has_capability('mod/data:viewanyrating',$context),
+        'viewall' => has_capability('mod/data:viewallratings',$context),
+        'rate'    => has_capability('mod/data:rate',$context)
+    );
 }
 
 /**
@@ -1387,14 +1413,22 @@ function data_rating_permissions($options) {
 function data_rating_validate($params) {
     global $DB, $USER;
 
-    if (!array_key_exists('itemid', $params)
-            || !array_key_exists('context', $params)
-            || !array_key_exists('rateduserid', $params)
-            || !array_key_exists('scaleid', $params)) {
-        throw new rating_exception('missingparameter');
+    // Check the component is mod_data
+    if ($params['component'] != 'mod_data') {
+        throw new rating_exception('invalidcomponent');
+    }
+
+    // Check the ratingarea is entry (the only rating area in data module)
+    if ($params['ratingarea'] != 'entry') {
+        throw new rating_exception('invalidratingarea');
     }
 
-    $datasql = "SELECT d.id as did, d.scale, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
+    // Check the rateduserid is not the current user .. you can't rate your own entries
+    if ($params['rateduserid'] == $USER->id) {
+        throw new rating_exception('nopermissiontorate');
+    }
+
+    $datasql = "SELECT d.id as dataid, d.scale, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
                   FROM {data_records} r
                   JOIN {data} d ON r.dataid = d.id
                  WHERE r.id = :itemid";
@@ -1409,22 +1443,12 @@ function data_rating_validate($params) {
         throw new rating_exception('invalidscaleid');
     }
 
-    if ($info->userid == $USER->id) {
-        //user is attempting to rate their own glossary entry
-        throw new rating_exception('nopermissiontorate');
-    }
-
-    if ($info->userid != $params['rateduserid']) {
-        //supplied user ID doesnt match the user ID from the database
-        throw new rating_exception('invaliduserid');
-    }
-
     //check that the submitted rating is valid for the scale
     if ($params['rating'] < 0) {
         throw new rating_exception('invalidnum');
     } else if ($info->scale < 0) {
         //its a custom scale
-        $scalerecord = $DB->get_record('scale', array('id' => -$params['scaleid']));
+        $scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
         if ($scalerecord) {
             $scalearray = explode(',', $scalerecord->scale);
             if ($params['rating'] > count($scalearray)) {
@@ -1443,30 +1467,24 @@ function data_rating_validate($params) {
         throw new rating_exception('nopermissiontorate');
     }
 
-    //check the item we're rating was created in the assessable time window
+    // check the item we're rating was created in the assessable time window
     if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) {
         if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
             throw new rating_exception('notavailable');
         }
     }
 
-    $dataid = $info->did;
-    $groupid = $info->groupid;
-    $courseid = $info->course;
+    $course = $DB->get_record('course', array('id'=>$info->course), '*', MUST_EXIST);
+    $cm = get_coursemodule_from_instance('data', $info->dataid, $course->id, false, MUST_EXIST);
+    $context = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
 
-    $cm = get_coursemodule_from_instance('data', $dataid);
-    if (empty($cm)) {
-        throw new rating_exception('unknowncontext');
-    }
-    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
-
-    //if the supplied context doesnt match the item's context
-    if (empty($context) || $context->id != $params['context']->id) {
+    // if the supplied context doesnt match the item's context
+    if ($context->id != $params['context']->id) {
         throw new rating_exception('invalidcontext');
     }
 
     // Make sure groups allow this user to see the item they're rating
-    $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+    $groupid = $info->groupid;
     if ($groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
         if (!groups_group_exists($groupid)) { // Can't find group
             throw new rating_exception('cannotfindgroup');//something is wrong
@@ -1676,7 +1694,7 @@ function data_print_preference_form($data, $perpage, $search, $sort='', $order='
  */
 function data_print_ratings($data, $record) {
     global $OUTPUT;
-    if( !empty($record->rating) ){
+    if (!empty($record->rating)){
         echo $OUTPUT->render($record->rating);
     }
 }
@@ -2523,7 +2541,9 @@ function data_reset_userdata($data) {
                      WHERE d.course=?";
 
     $rm = new rating_manager();
-    $ratingdeloptions = new stdclass();
+    $ratingdeloptions = new stdClass;
+    $ratingdeloptions->component = 'mod_data';
+    $ratingdeloptions->ratingarea = 'entry';
 
     // delete entries if requested
     if (!empty($data->reset_data)) {
index b57666a..deba450 100644 (file)
@@ -5,8 +5,8 @@
 //  This fragment is called by /admin/index.php
 ////////////////////////////////////////////////////////////////////////////////
 
-$module->version  = 2010100101;
-$module->requires = 2010080300;  // Requires this Moodle version
+$module->version  = 2011052300;
+$module->requires = 2011052300;  // Requires this Moodle version
 $module->cron     = 60;
 
 
index ffb074f..de9edb5 100644 (file)
@@ -668,10 +668,11 @@ if ($showactivity) {
                 //data_print_template() only adds ratings for singletemplate which is why we're attaching them here
                 //attach ratings to data records
                 require_once($CFG->dirroot.'/rating/lib.php');
-                if ($data->assessed!=RATING_AGGREGATE_NONE) {
-                    $ratingoptions = new stdclass();
+                if ($data->assessed != RATING_AGGREGATE_NONE) {
+                    $ratingoptions = new stdClass;
                     $ratingoptions->context = $context;
                     $ratingoptions->component = 'mod_data';
+                    $ratingoptions->ratingarea = 'entry';
                     $ratingoptions->items = $records;
                     $ratingoptions->aggregate = $data->assessed;//the aggregation method
                     $ratingoptions->scaleid = $data->scale;
index a75d8c9..bc297e5 100644 (file)
@@ -467,6 +467,7 @@ function feedback_cron () {
 }
 
 /**
+ * @todo: deprecated - to be deleted in 2.2
  * @return bool false
  */
 function feedback_get_participants($feedbackid) {
index 3cffd25..49c1bba 100644 (file)
@@ -205,6 +205,8 @@ function folder_user_complete($course, $user, $mod, $folder) {
 /**
  * Returns the users with data in one folder
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $folderid
  * @return bool false
  */
index 9eb48c2..f9c1bc4 100644 (file)
@@ -63,7 +63,7 @@ class backup_forum_activity_structure_step extends backup_activity_structure_ste
         $ratings = new backup_nested_element('ratings');
 
         $rating = new backup_nested_element('rating', array('id'), array(
-            'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
+            'component', 'ratingarea', 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
 
         $subscriptions = new backup_nested_element('subscriptions');
 
@@ -125,8 +125,10 @@ class backup_forum_activity_structure_step extends backup_activity_structure_ste
 
             $track->set_source_table('forum_track_prefs', array('forumid' => backup::VAR_PARENTID));
 
-            $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
-                                                      'itemid'    => backup::VAR_PARENTID));
+            $rating->set_source_table('rating', array('contextid'  => backup::VAR_CONTEXTID,
+                                                      'component'  => 'mod_forum',
+                                                      'ratingarea' => 'post',
+                                                      'itemid'     => backup::VAR_PARENTID));
             $rating->set_source_alias('rating', 'value');
         }
 
index 17e8820..40c1b39 100644 (file)
@@ -126,6 +126,14 @@ class restore_forum_activity_structure_step extends restore_activity_structure_s
         $data->timecreated = $this->apply_date_offset($data->timecreated);
         $data->timemodified = $this->apply_date_offset($data->timemodified);
 
+        // We need to check that component and ratingarea are both set here.
+        if (empty($data->component)) {
+            $data->component = 'mod_forum';
+        }
+        if (empty($data->ratingarea)) {
+            $data->ratingarea = 'post';
+        }
+
         $newitemid = $DB->insert_record('rating', $data);
     }
 
index 3a56755..5f9a0db 100644 (file)
@@ -315,6 +315,28 @@ function xmldb_forum_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2010091900, 'forum');
     }
 
+    if ($oldversion < 2011052300) {
+        // rating.component and rating.ratingarea have now been added as mandatory fields.
+        // Presently you can only rate forum posts so component = 'mod_forum' and ratingarea = 'post'
+        // for all ratings with a forum context.
+        // We want to update all ratings that belong to a forum context and don't already have a
+        // component set.
+        // This could take a while reset upgrade timeout to 5 min
+        upgrade_set_timeout(60 * 20);
+        $sql = "UPDATE {rating}
+                SET component = 'mod_forum', ratingarea = 'post'
+                WHERE contextid IN (
+                    SELECT ctx.id
+                      FROM {context} ctx
+                      JOIN {course_modules} cm ON cm.id = ctx.instanceid
+                      JOIN {modules} m ON m.id = cm.module
+                     WHERE ctx.contextlevel = 70 AND
+                           m.name = 'forum'
+                ) AND component = 'unknown'";
+        $DB->execute($sql);
+
+        upgrade_mod_savepoint(true, 2011052300, 'forum');
+    }
 
     return true;
 }
index d850e57..4640c84 100644 (file)
@@ -1445,25 +1445,25 @@ function forum_print_recent_activity($course, $viewfullnames, $timestart) {
  * @param int $userid optional user id, 0 means all users
  * @return array array of grades, false if none
  */
-function forum_get_user_grades($forum, $userid=0) {
+function forum_get_user_grades($forum, $userid = 0) {
     global $CFG;
 
     require_once($CFG->dirroot.'/rating/lib.php');
-    $rm = new rating_manager();
 
-    $ratingoptions = new stdclass();
+    $ratingoptions = new stdClass;
+    $ratingoptions->component = 'mod_forum';
+    $ratingoptions->ratingarea = 'post';
 
     //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
     $ratingoptions->modulename = 'forum';
     $ratingoptions->moduleid   = $forum->id;
-    //$ratingoptions->cmidnumber = $forum->cmidnumber;
-
     $ratingoptions->userid = $userid;
     $ratingoptions->aggregationmethod = $forum->assessed;
     $ratingoptions->scaleid = $forum->scale;
     $ratingoptions->itemtable = 'forum_posts';
     $ratingoptions->itemtableusercolumn = 'userid';
 
+    $rm = new rating_manager();
     return $rm->get_user_grades($ratingoptions);
 }
 
@@ -1586,8 +1586,8 @@ function forum_grade_item_delete($forum) {
  * Returns the users with data in one forum
  * (users with records in forum_subscriptions, forum_posts, students)
  *
- * @global object
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $forumid
  * @return mixed array or false if none
  */
@@ -1595,31 +1595,35 @@ function forum_get_participants($forumid) {
 
     global $CFG, $DB;
 
+    $params = array('forumid' => $forumid);
+
     //Get students from forum_subscriptions
-    $st_subscriptions = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
-                                         FROM {user} u,
-                                              {forum_subscriptions} s
-                                         WHERE s.forum = ? AND
-                                               u.id = s.userid", array($forumid));
+    $sql = "SELECT DISTINCT u.id, u.id
+              FROM {user} u,
+                   {forum_subscriptions} s
+             WHERE s.forum = :forumid AND
+                   u.id = s.userid";
+    $st_subscriptions = $DB->get_records_sql($sql, $params);
+
     //Get students from forum_posts
-    $st_posts = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
-                                 FROM {user} u,
-                                      {forum_discussions} d,
-                                      {forum_posts} p
-                                 WHERE d.forum = ? AND
-                                       p.discussion = d.id AND
-                                       u.id = p.userid", array($forumid));
+    $sql = "SELECT DISTINCT u.id, u.id
+              FROM {user} u,
+                   {forum_discussions} d,
+                   {forum_posts} p
+              WHERE d.forum = :forumid AND
+                    p.discussion = d.id AND
+                    u.id = p.userid";
+    $st_posts = $DB->get_records_sql($sql, $params);
 
     //Get students from the ratings table
-    $st_ratings = $DB->get_records_sql("SELECT DISTINCT u.id, u.id
-                                   FROM {user} u,
-                                        {forum_discussions} d,
-                                        {forum_posts} p,
-                                        {ratings} r
-                                   WHERE d.forum = ? AND
-                                         p.discussion = d.id AND
-                                         r.post = p.id AND
-                                         u.id = r.userid", array($forumid));
+    $sql = "SELECT DISTINCT r.userid, r.userid AS id
+              FROM {forum_discussions} d
+              JOIN {forum_posts} p ON p.discussion = d.id
+              JOIN {rating} r on r.itemid = p.id
+             WHERE d.forum = :forumid AND
+                   r.component = 'mod_forum' AND
+                   r.ratingarea = 'post'";
+    $st_ratings = $DB->get_records_sql($sql, $params);
 
     //Add st_posts to st_subscriptions
     if ($st_posts) {
@@ -2050,29 +2054,29 @@ function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=5
 /**
  * Returns a list of ratings for a particular post - sorted.
  *
- * @global object
- * @global object
+ * TODO: Check if this function is actually used anywhere.
+ * Up until the fix for MDL-27471 this function wasn't even returning.
+ *
+ * @param stdClass $context
  * @param int $postid
  * @param string $sort
  * @return array Array of ratings or false
  */
-function forum_get_ratings($context, $postid, $sort="u.firstname ASC") {
-    global $PAGE;
-
-    $options = new stdclass();
-    $options->context = $PAGE->context;
+function forum_get_ratings($context, $postid, $sort = "u.firstname ASC") {
+    $options = new stdClass;
+    $options->context = $context;
+    $options->component = 'mod_forum';
+    $options->ratingarea = 'post';
     $options->itemid = $postid;
     $options->sort = "ORDER BY $sort";
 
     $rm = new rating_manager();
-    $rm->get_all_ratings_for_item($options);
+    return $rm->get_all_ratings_for_item($options);
 }
 
 /**
  * Returns a list of all new posts that have not been mailed yet
  *
- * @global object
- * @global object
  * @param int $starttime posts created after this time
  * @param int $endtime posts created before this
  * @param int $now used for timed discussions only
@@ -2456,29 +2460,35 @@ function forum_count_discussions($forum, $cm, $course) {
 /**
  * How many posts by other users are unrated by a given user in the given discussion?
  *
- * @global object
- * @global object
+ * TODO: Is this function still used anywhere?
+ *
  * @param int $discussionid
  * @param int $userid
  * @return mixed
  */
 function forum_count_unrated_posts($discussionid, $userid) {
     global $CFG, $DB;
-    if ($posts = $DB->get_record_sql("SELECT count(*) as num
-                                   FROM {forum_posts}
-                                  WHERE parent > 0
-                                    AND discussion = ?
-                                    AND userid <> ? ", array($discussionid, $userid))) {
-
-        if ($rated = $DB->get_record_sql("SELECT count(*) as num
-                                       FROM {forum_posts} p,
-                                            {rating} r
-                                      WHERE p.discussion = ?
-                                        AND p.id = r.itemid
-                                        AND r.userid = ?", array($discussionid, $userid))) {
-            $difference = $posts->num - $rated->num;
-            if ($difference > 0) {
-                return $difference;
+
+    $sql = "SELECT COUNT(*) as num
+              FROM {forum_posts}
+             WHERE parent > 0
+               AND discussion = :discussionid
+               AND userid <> :userid";
+    $params = array('discussionid' => $discussionid, 'userid' => $userid);
+    $posts = $DB->get_record_sql($sql, $params);
+    if ($posts) {
+        $sql = "SELECT count(*) as num
+                  FROM {forum_posts} p,
+                       {rating} r
+                 WHERE p.discussion = :discussionid AND
+                       p.id = r.itemid AND
+                       r.userid = userid AND
+                       r.component = 'mod_forum' AND
+                       r.ratingarea = 'post'";
+        $rated = $DB->get_record_sql($sql, $params);
+        if ($rated) {
+            if ($posts->num > $rated->num) {
+                return $posts->num - $rated->num;
             } else {
                 return 0;    // Just in case there was a counting error
             }
@@ -3451,24 +3461,31 @@ function forum_print_post($post, $discussion, $forum, &$cm, $course, $ownpost=fa
 
 /**
  * Return rating related permissions
+ *
  * @param string $options the context id
  * @return array an associative array of the user's rating permissions
  */
-function forum_rating_permissions($contextid) {
-    $context = get_context_instance_by_id($contextid);
-
-    if (!$context) {
-        print_error('invalidcontext');
+function forum_rating_permissions($contextid, $component, $ratingarea) {
+    $context = get_context_instance_by_id($contextid, MUST_EXIST);
+    if ($component != 'mod_forum' || $ratingarea != 'post') {
+        // We don't know about this component/ratingarea so just return null to get the
+        // default restrictive permissions.
         return null;
-    } else {
-        return array('view'=>has_capability('mod/forum:viewrating',$context), 'viewany'=>has_capability('mod/forum:viewanyrating',$context), 'viewall'=>has_capability('mod/forum:viewallratings',$context), 'rate'=>has_capability('mod/forum:rate',$context));
     }
+    return array(
+        'view'    => has_capability('mod/forum:viewrating', $context),
+        'viewany' => has_capability('mod/forum:viewanyrating', $context),
+        'viewall' => has_capability('mod/forum:viewallratings', $context),
+        'rate'    => has_capability('mod/forum:rate', $context)
+    );
 }
 
 /**
  * Validates a submitted rating
  * @param array $params submitted data
  *            context => object the context in which the rated items exists [required]
+ *            component => The component for this module - should always be mod_forum [required]
+ *            ratingarea => object the context in which the rated items exists [required]
  *            itemid => int the ID of the object being rated [required]
  *            scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
  *            rating => int the submitted rating [required]
@@ -3479,45 +3496,52 @@ function forum_rating_permissions($contextid) {
 function forum_rating_validate($params) {
     global $DB, $USER;
 
-    if (!array_key_exists('itemid', $params)
-            || !array_key_exists('context', $params)
-            || !array_key_exists('rateduserid', $params)
-            || !array_key_exists('scaleid', $params)) {
-        throw new rating_exception('missingparameter');
-    }
-
-    $forumsql = "SELECT f.id as fid, f.course, f.scale, d.id as did, p.userid as userid, p.created, f.assesstimestart, f.assesstimefinish, d.groupid
-                   FROM {forum_posts} p
-                   JOIN {forum_discussions} d ON p.discussion = d.id
-                   JOIN {forum} f ON d.forum = f.id
-                  WHERE p.id = :itemid";
-    $forumparams = array('itemid'=>$params['itemid']);
-    if (!$info = $DB->get_record_sql($forumsql, $forumparams)) {
-        //item doesn't exist
-        throw new rating_exception('invaliditemid');
+    // Check the component is mod_forum
+    if ($params['component'] != 'mod_forum') {
+        throw new rating_exception('invalidcomponent');
     }
 
-    if ($info->scale != $params['scaleid']) {
-        //the scale being submitted doesnt match the one in the database
-        throw new rating_exception('invalidscaleid');
+    // Check the ratingarea is post (the only rating area in forum)
+    if ($params['ratingarea'] != 'post') {
+        throw new rating_exception('invalidratingarea');
     }
 
-    if ($info->userid == $USER->id) {
-        //user is attempting to rate their own post
+    // Check the rateduserid is not the current user .. you can't rate your own posts
+    if ($params['rateduserid'] == $USER->id) {
         throw new rating_exception('nopermissiontorate');
     }
 
-    if ($info->userid != $params['rateduserid']) {
-        //supplied user ID doesnt match the user ID from the database
-        throw new rating_exception('invaliduserid');
+    // Fetch all the related records ... we need to do this anyway to call forum_user_can_see_post
+    $post = $DB->get_record('forum_posts', array('id' => $params['itemid'], 'userid' => $params['rateduserid']), '*', MUST_EXIST);
+    $discussion = $DB->get_record('forum_discussions', array('id' => $post->discussion), '*', MUST_EXIST);
+    $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
+    $course = $DB->get_record('course', array('id' => $forum->course), '*', MUST_EXIST);
+    $cm = get_coursemodule_from_instance('forum', $forum->id, $course->id , false, MUST_EXIST);
+    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+
+    // Make sure the context provided is the context of the forum
+    if ($context->id != $params['context']->id) {
+        throw new rating_exception('invalidcontext');
+    }
+    
+    if ($forum->scale != $params['scaleid']) {
+        //the scale being submitted doesnt match the one in the database
+        throw new rating_exception('invalidscaleid');
     }
 
+    // check the item we're rating was created in the assessable time window
+    if (!empty($forum->assesstimestart) && !empty($forum->assesstimefinish)) {
+        if ($post->created < $forum->assesstimestart || $post->created > $forum->assesstimefinish) {
+            throw new rating_exception('notavailable');
+        }
+    }
+    
     //check that the submitted rating is valid for the scale
     if ($params['rating'] < 0) {
         throw new rating_exception('invalidnum');
-    } else if ($info->scale < 0) {
+    } else if ($forum->scale < 0) {
         //its a custom scale
-        $scalerecord = $DB->get_record('scale', array('id' => -$params['scaleid']));
+        $scalerecord = $DB->get_record('scale', array('id' => -$forum->scale));
         if ($scalerecord) {
             $scalearray = explode(',', $scalerecord->scale);
             if ($params['rating'] > count($scalearray)) {
@@ -3526,61 +3550,25 @@ function forum_rating_validate($params) {
         } else {
             throw new rating_exception('invalidscaleid');
         }
-    } else if ($params['rating'] > $info->scale) {
+    } else if ($params['rating'] > $forum->scale) {
         //if its numeric and submitted rating is above maximum
         throw new rating_exception('invalidnum');
     }
 
-    //check the item we're rating was created in the assessable time window
-    if (!empty($info->assesstimestart) && !empty($info->assesstimefinish)) {
-        if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
-            throw new rating_exception('notavailable');
-        }
-    }
-
-    $forumid = $info->fid;
-    $discussionid = $info->did;
-    $groupid = $info->groupid;
-    $courseid = $info->course;
-
-    $cm = get_coursemodule_from_instance('forum', $forumid);
-    if (empty($cm)) {
-        throw new rating_exception('unknowncontext');
-    }
-    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
-
-    //if the supplied context doesnt match the item's context
-    if (empty($context) || $context->id != $params['context']->id) {
-        throw new rating_exception('invalidcontext');
-    }
-
     // Make sure groups allow this user to see the item they're rating
-    $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
-    if ($groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
-        if (!groups_group_exists($groupid)) { // Can't find group
+    if ($discussion->groupid > 0 and $groupmode = groups_get_activity_groupmode($cm, $course)) {   // Groups are being used
+        if (!groups_group_exists($discussion->groupid)) { // Can't find group
             throw new rating_exception('cannotfindgroup');//something is wrong
         }
 
-        if (!groups_is_member($groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
+        if (!groups_is_member($discussion->groupid) and !has_capability('moodle/site:accessallgroups', $context)) {
             // do not allow rating of posts from other groups when in SEPARATEGROUPS or VISIBLEGROUPS
             throw new rating_exception('notmemberofgroup');
         }
     }
 
-    //need to load the full objects here as ajax scripts don't like
-    //the debugging messages produced by forum_user_can_see_post() if you just supply IDs
-    if (!$forum = $DB->get_record('forum',array('id'=>$forumid))) {
-        throw new rating_exception('invalidrecordunknown');
-    }
-    if (!$post = $DB->get_record('forum_posts',array('id'=>$params['itemid']))) {
-        throw new rating_exception('invalidrecordunknown');
-    }
-    if (!$discussion = $DB->get_record('forum_discussions',array('id'=>$discussionid))) {
-        throw new rating_exception('invalidrecordunknown');
-    }
-
-    //perform some final capability checks
-    if( !forum_user_can_see_post($forum, $discussion, $post, $USER, $cm)) {
+    // perform some final capability checks
+    if (!forum_user_can_see_post($forum, $discussion, $post, $USER, $cm)) {
         throw new rating_exception('nopermissiontorate');
     }
 
@@ -4350,8 +4338,10 @@ function forum_delete_post($post, $children, $course, $cm, $forum, $skipcompleti
 
     //delete ratings
     require_once($CFG->dirroot.'/rating/lib.php');
-    $delopt = new stdclass();
+    $delopt = new stdClass;
     $delopt->contextid = $context->id;
+    $delopt->component = 'mod_forum';
+    $delopt->ratingarea = 'post';
     $delopt->itemid = $post->id;
     $rm = new rating_manager();
     $rm->delete_ratings($delopt);
@@ -5386,32 +5376,29 @@ function forum_print_latest_discussions($course, $forum, $maxdiscussions=-1, $di
 
 
 /**
- * @global object
- * @global object
+ * Prints a forum discussion
+ *
  * @uses CONTEXT_MODULE
  * @uses FORUM_MODE_FLATNEWEST
  * @uses FORUM_MODE_FLATOLDEST
  * @uses FORUM_MODE_THREADED
  * @uses FORUM_MODE_NESTED
- * @param object $course
- * @param object $cm
- * @param object $forum
- * @param object $discussion
- * @param object $post
- * @param object $mode
+ * @param stdClass $course
+ * @param stdClass $cm
+ * @param stdClass $forum
+ * @param stdClass $discussion
+ * @param stdClass $post
+ * @param int $mode
  * @param mixed $canreply
- * @param bool $cancreate
+ * @param bool $canrate
  */
 function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode, $canreply=NULL, $canrate=false) {
+    global $USER, $CFG;
 
-    global $USER, $CFG, $DB, $PAGE, $OUTPUT;
     require_once($CFG->dirroot.'/rating/lib.php');
 
-    if (isloggedin()) {
-        $ownpost = ($USER->id == $post->userid);
-    } else {
-        $ownpost = false;
-    }
+    $ownpost = (isloggedin() && $USER->id == $post->userid);
+
     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
     if ($canreply === NULL) {
         $reply = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
@@ -5420,7 +5407,7 @@ function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode,
     }
 
     // $cm holds general cache for forum functions
-    $cm->cache = new stdClass();
+    $cm->cache = new stdClass;
     $cm->cache->groups      = groups_get_all_groups($course->id, 0, $cm->groupingid);
     $cm->cache->usersgroups = array();
 
@@ -5453,10 +5440,11 @@ function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode,
     }
 
     //load ratings
-    if ($forum->assessed!=RATING_AGGREGATE_NONE) {
-        $ratingoptions = new stdclass();
+    if ($forum->assessed != RATING_AGGREGATE_NONE) {
+        $ratingoptions = new stdClass;
         $ratingoptions->context = $modcontext;
         $ratingoptions->component = 'mod_forum';
+        $ratingoptions->ratingarea = 'post';
         $ratingoptions->items = $posts;
         $ratingoptions->aggregate = $forum->assessed;//the aggregation method
         $ratingoptions->scaleid = $forum->scale;
@@ -7130,10 +7118,14 @@ function forum_reset_userdata($data) {
                            WHERE f.course=? AND f.id=fd.forum AND fd.id=fp.discussion";
 
     $forumssql = $forums = $rm = null;
+
     if( $removeposts || !empty($data->reset_forum_ratings) ) {
         $forumssql      = "$allforumssql $typesql";
         $forums = $forums = $DB->get_records_sql($forumssql, $params);
-        $rm = new rating_manager();
+        $rm = new rating_manager();;
+        $ratingdeloptions = new stdClass;
+        $ratingdeloptions->component = 'mod_forum';
+        $ratingdeloptions->ratingarea = 'post';
     }
 
     if ($removeposts) {
@@ -7142,7 +7134,6 @@ function forum_reset_userdata($data) {
 
         // now get rid of all attachments
         $fs = get_file_storage();
-        $ratingdeloptions = new stdclass();
         if ($forums) {
             foreach ($forums as $forumid=>$unused) {
                 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) {
@@ -7190,8 +7181,6 @@ function forum_reset_userdata($data) {
 
     // remove all ratings in this course's forums
     if (!empty($data->reset_forum_ratings)) {
-        $ratingdeloptions = new stdclass();
-
         if ($forums) {
             foreach ($forums as $forumid=>$unused) {
                 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) {
index e56910e..4714b34 100644 (file)
@@ -118,7 +118,8 @@ if ($course->id == SITEID) {
 }
 
 // Get the posts.
-if ($posts = forum_search_posts($searchterms, $searchcourse, $page*$perpage, $perpage, $totalcount, $extrasql)) {
+$posts = forum_search_posts($searchterms, $searchcourse, $page*$perpage, $perpage, $totalcount, $extrasql);
+if ($posts) {
 
     require_once($CFG->dirroot.'/rating/lib.php');
 
@@ -127,15 +128,14 @@ if ($posts = forum_search_posts($searchterms, $searchcourse, $page*$perpage, $pe
 
     $discussions = array();
     $forums      = array();
-    $cms         = array();
 
     //todo Rather than retrieving the ratings for each post individually it would be nice to do them in groups
     //however this requires creating arrays of posts with each array containing all of the posts from a particular forum,
     //retrieving the ratings then reassembling them all back into a single array sorted by post.modified (descending)
     $rm = new rating_manager();
-    $ratingoptions = new stdclass();
-    $ratingoptions->plugintype = 'mod';
-    $ratingoptions->pluginname = 'forum';
+    $ratingoptions = new stdClass;
+    $ratingoptions->component = 'mod_forum';
+    $ratingoptions->ratingarea = 'post';
 
     foreach ($posts as $post) {
 
@@ -149,66 +149,57 @@ if ($posts = forum_search_posts($searchterms, $searchcourse, $page*$perpage, $pe
         }
 
         if (!isset($forums[$discussion->forum])) {
-            if (! $forum = $DB->get_record('forum', array('id' => $discussion->forum))) {
-                print_error('invalidforumid', 'forum');
-            }
-            //hold onto forum cm and context for when we load ratings
-            if ($forumcm = get_coursemodule_from_instance('forum', $forum->id)) {
-                $forum->cm = $forumcm;
-                $forumcontext = get_context_instance(CONTEXT_MODULE, $forum->cm->id);
-                $forum->context = $forumcontext;
-            }
+            $forum = $DB->get_record('forum', array('id' => $discussion->forum), '*', MUST_EXIST);
+            $forum->cm = get_coursemodule_from_instance('forum', $forum->id, 0, false, MUST_EXIST);
+            $forum->context = get_context_instance(CONTEXT_MODULE, $forum->cm->id);
             $forums[$discussion->forum] = $forum;
         } else {
             $forum = $forums[$discussion->forum];
         }
 
-        //load ratings
-        if ($forum->assessed!=RATING_AGGREGATE_NONE) {
+        $forumurl = new moodle_url('/mod/forum/view.php', array('id' => $forum->cm->id));
+        $discussionurl = new moodle_url('/mod/forum/discuss.php', array('d' => $discussion->id));
+
+        // load ratings
+        if ($forum->assessed != RATING_AGGREGATE_NONE) {
             $ratingoptions->context = $forum->context;
-            $ratingoptions->component = 'mod_forum';
             $ratingoptions->items = array($post);
             $ratingoptions->aggregate = $forum->assessed;//the aggregation method
             $ratingoptions->scaleid = $forum->scale;
             $ratingoptions->userid = $user->id;
+            $ratingoptions->assesstimestart = $forum->assesstimestart;
+            $ratingoptions->assesstimefinish = $forum->assesstimefinish;
             if ($forum->type == 'single' or !$discussion->id) {
-                $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id={$forum->cm->id}";
+                $ratingoptions->returnurl = $forumurl;
             } else {
-                $ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id";
+                $ratingoptions->returnurl = $discussionurl;
             }
-            $ratingoptions->assesstimestart = $forum->assesstimestart;
-            $ratingoptions->assesstimefinish = $forum->assesstimefinish;
 
             $updatedpost = $rm->get_ratings($ratingoptions);
             //updating the array this way because we're iterating over a collection and updating them one by one
             $posts[$updatedpost[0]->id] = $updatedpost[0];
         }
 
-        if (!isset($cms[$forum->id])) {
-            $cm = get_coursemodule_from_instance('forum', $forum->id, 0, false, MUST_EXIST);
-            $cms[$forum->id] = $cm;
-            unset($cm); // do not use cm directly, it would break caching
+        $fullsubjects = array();
+        if ($course->id == SITEID && has_capability('moodle/site:config', $syscontext)) {
+            $postcoursename = $DB->get_field('course', 'shortname', array('id'=>$forum->course));
+            $courseurl = new moodle_url('/course/view.php', array('id' => $forum->course));
+            $fullsubjects[] = html_writer::link($courseurl, $postcoursename);
         }
-
-        $fullsubject = "<a href=\"view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
+        $fullsubjects[] = html_writer::link($forumurl, format_string($forum->name, true));
         if ($forum->type != 'single') {
-            $fullsubject .= " -> <a href=\"discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a>";
+            $fullsubjects[] .= html_writer::link($discussionurl, format_string($discussion->name, true));
             if ($post->parent != 0) {
-                $fullsubject .= " -> <a href=\"discuss.php?d=$post->discussion&amp;parent=$post->id\">".format_string($post->subject,true)."</a>";
+                $parenturl = new moodle_url('/mod/forum/discuss.php', array('d' => $post->discussion, 'parent' => $post->id));
+                $fullsubjects[] .= html_writer::link($parenturl, format_string($post->subject, true));
             }
         }
 
-        if ($course->id == SITEID && has_capability('moodle/site:config', $syscontext)) {
-            $postcoursename = $DB->get_field('course', 'shortname', array('id'=>$forum->course));
-            $fullsubject = '<a href="'.$CFG->wwwroot.'/course/view.php?id='.$forum->course.'">'.$postcoursename.'</a> -> '. $fullsubject;
-        }
-
-        $post->subject = $fullsubject;
-
-        $fulllink = "<a href=\"discuss.php?d=$post->discussion#p$post->id\">".
-            get_string("postincontext", "forum")."</a>";
+        $post->subject = join(' -> ', $fullsubjects);
+        $discussionurl->set_anchor('p'.$post->id);
+        $fulllink = html_writer::link($discussionurl, get_string("postincontext", "forum"));
 
-        forum_print_post($post, $discussion, $forum, $cms[$forum->id], $course, false, false, false, $fulllink);
+        forum_print_post($post, $discussion, $forum, $forum->cm, $course, false, false, false, $fulllink);
         echo "<br />";
     }
 
index 52d0a5e..d831c13 100644 (file)
@@ -24,8 +24,6 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$module->version  = 2010111500;
-$module->requires = 2010111002;  // Requires this Moodle version
-$module->cron     = 60;
-
-
+$module->version  = 2011052300;
+$module->requires = 2011052300;  // Requires this Moodle version
+$module->cron     = 60;
\ No newline at end of file
index 70ac8ba..d3c93cd 100644 (file)
     $completion->set_module_viewed($cm);
 
 /// Print header.
-    /// Add ajax-related libs for ratings if required  MDL-20119
-    $PAGE->requires->yui2_lib('event');
-    $PAGE->requires->yui2_lib('connection');
-    $PAGE->requires->yui2_lib('json');
 
     $PAGE->set_title(format_string($forum->name));
     $PAGE->add_body_class('forumtype-'.$forum->type);
index e0186e0..3fa69cc 100644 (file)
@@ -61,7 +61,7 @@ class backup_glossary_activity_structure_step extends backup_activity_structure_
         $ratings = new backup_nested_element('ratings');
 
         $rating = new backup_nested_element('rating', array('id'), array(
-            'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
+            'component', 'ratingarea', 'scaleid', 'value', 'userid', 'timecreated', 'timemodified'));
 
         $categories = new backup_nested_element('categories');
 
@@ -101,8 +101,10 @@ class backup_glossary_activity_structure_step extends backup_activity_structure_
             $alias->set_source_table('glossary_alias', array('entryid' => backup::VAR_PARENTID));
             $alias->set_source_alias('alias', 'alias_text');
 
-            $rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
-                                      'itemid'    => backup::VAR_PARENTID));
+            $rating->set_source_table('rating', array('contextid'  => backup::VAR_CONTEXTID,
+                                                      'itemid'     => backup::VAR_PARENTID,
+                                                      'component'  => 'mod_glossary',
+                                                      'ratingarea' => 'entry'));
             $rating->set_source_alias('rating', 'value');
 
             $categoryentry->set_source_table('glossary_entries_categories', array('categoryid' => backup::VAR_PARENTID));
index 74e9f8c..719cfa0 100644 (file)
@@ -117,6 +117,15 @@ class restore_glossary_activity_structure_step extends restore_activity_structur
         $data->timecreated = $this->apply_date_offset($data->timecreated);
         $data->timemodified = $this->apply_date_offset($data->timemodified);
 
+        // Make sure that we have both component and ratingarea set. These were added in 2.1.
+        // Prior to that all ratings were for entries so we know what to set them too.
+        if (empty($data->component)) {
+            $data->component = 'mod_glossary';
+        }
+        if (empty($data->ratingarea)) {
+            $data->ratingarea = 'entry';
+        }
+
         $newitemid = $DB->insert_record('rating', $data);
     }
 
index 722bdf1..b3677de 100644 (file)
@@ -326,6 +326,29 @@ function xmldb_glossary_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2010111501, 'glossary');
     }
 
+    if ($oldversion < 2011052300) {
+        // rating.component and rating.ratingarea have now been added as mandatory fields.
+        // Presently you can only rate data entries so component = 'mod_glossary' and ratingarea = 'entry'
+        // for all ratings with a glossary context.
+        // We want to update all ratings that belong to a glossary context and don't already have a
+        // component set.
+        // This could take a while reset upgrade timeout to 5 min
+        upgrade_set_timeout(60 * 20);
+        $sql = "UPDATE {rating}
+                SET component = 'mod_glossary', ratingarea = 'entry'
+                WHERE contextid IN (
+                    SELECT ctx.id
+                      FROM {context} ctx
+                      JOIN {course_modules} cm ON cm.id = ctx.instanceid
+                      JOIN {modules} m ON m.id = cm.module
+                     WHERE ctx.contextlevel = 70 AND
+                           m.name = 'glossary'
+                ) AND component = 'unknown'";
+        $DB->execute($sql);
+
+        upgrade_mod_savepoint(true, 2011052300, 'glossary');
+    }
+
     return true;
 }
 
index fe1b709..e02f291 100644 (file)
@@ -104,8 +104,10 @@ if ($confirm and confirm_sesskey()) { // the operation was confirmed.
 
         //delete glossary entry ratings
         require_once($CFG->dirroot.'/rating/lib.php');
-        $delopt = new stdclass();
+        $delopt = new stdClass;
         $delopt->contextid = $context->id;
+        $delopt->component = 'mod_glossary';
+        $delopt->ratingarea = 'entry';
         $delopt->itemid = $entry->id;
         $rm = new rating_manager();
         $rm->delete_ratings($delopt);
index edd9e42..d5fe1bf 100644 (file)
@@ -23,7 +23,6 @@
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-require_once($CFG->dirroot . '/rating/lib.php');
 require_once($CFG->libdir . '/completionlib.php');
 
 define("GLOSSARY_SHOW_ALL_CATEGORIES", 0);
@@ -445,13 +444,14 @@ function glossary_get_user_grades($glossary, $userid=0) {
     global $CFG;
 
     require_once($CFG->dirroot.'/rating/lib.php');
-    $rm = new rating_manager();
 
-    $ratingoptions = new stdclass();
+    $ratingoptions = new stdClass;
 
     //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
     $ratingoptions->modulename = 'glossary';
     $ratingoptions->moduleid   = $glossary->id;
+    $ratingoptions->component  = 'mod_glossary';
+    $ratingoptions->ratingarea = 'entry';
 
     $ratingoptions->userid = $userid;
     $ratingoptions->aggregationmethod = $glossary->assessed;
@@ -459,31 +459,40 @@ function glossary_get_user_grades($glossary, $userid=0) {
     $ratingoptions->itemtable = 'glossary_entries';
     $ratingoptions->itemtableusercolumn = 'userid';
 
+    $rm = new rating_manager();
     return $rm->get_user_grades($ratingoptions);
 }
 
 /**
  * Return rating related permissions
- * @param string $options the context id
+ *
+ * @param int $contextid the context id
+ * @param string $component The component we want to get permissions for
+ * @param string $ratingarea The ratingarea that we want to get permissions for
  * @return array an associative array of the user's rating permissions
  */
-function glossary_rating_permissions($options) {
-    $contextid = $options;
-    $context = get_context_instance_by_id($contextid);
-
-    if (!$context) {
-        print_error('invalidcontext');
+function glossary_rating_permissions($contextid, $component, $ratingarea) {
+    if ($component != 'mod_glossary' || $ratingarea != 'entry') {
+        // We don't know about this component/ratingarea so just return null to get the
+        // default restrictive permissions.
         return null;
-    } else {
-        return array('view'=>has_capability('mod/glossary:viewrating',$context), 'viewany'=>has_capability('mod/glossary:viewanyrating',$context), 'viewall'=>has_capability('mod/glossary:viewallratings',$context), 'rate'=>has_capability('mod/glossary:rate',$context));
     }
+    $context = get_context_instance_by_id($contextid);
+    return array(
+        'view'    => has_capability('mod/glossary:viewrating', $context),
+        'viewany' => has_capability('mod/glossary:viewanyrating', $context),
+        'viewall' => has_capability('mod/glossary:viewallratings', $context),
+        'rate'    => has_capability('mod/glossary:rate', $context)
+    );
 }
 
 /**
  * Validates a submitted rating
  * @param array $params submitted data
  *            context => object the context in which the rated items exists [required]
- *            itemid => int the ID of the object being rated
+ *            component => The component for this module - should always be mod_forum [required]
+ *            ratingarea => object the context in which the rated items exists [required]
+ *            itemid => int the ID of the object being rated [required]
  *            scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
  *            rating => int the submitted rating
  *            rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
@@ -493,44 +502,43 @@ function glossary_rating_permissions($options) {
 function glossary_rating_validate($params) {
     global $DB, $USER;
 
-    if (!array_key_exists('itemid', $params)
-            || !array_key_exists('context', $params)
-            || !array_key_exists('rateduserid', $params)
-            || !array_key_exists('scaleid', $params)) {
-        throw new rating_exception('missingparameter');
+    // Check the component is mod_forum
+    if ($params['component'] != 'mod_glossary') {
+        throw new rating_exception('invalidcomponent');
     }
 
-    $glossarysql = "SELECT g.id as gid, g.scale, e.userid as userid, e.approved, e.timecreated, g.assesstimestart, g.assesstimefinish
+    // Check the ratingarea is post (the only rating area in forum)
+    if ($params['ratingarea'] != 'entry') {
+        throw new rating_exception('invalidratingarea');
+    }
+
+    // Check the rateduserid is not the current user .. you can't rate your own posts
+    if ($params['rateduserid'] == $USER->id) {
+        throw new rating_exception('nopermissiontorate');
+    }
+
+    $glossarysql = "SELECT g.id as glossaryid, g.scale, g.course, e.userid as userid, e.approved, e.timecreated, g.assesstimestart, g.assesstimefinish
                       FROM {glossary_entries} e
                       JOIN {glossary} g ON e.glossaryid = g.id
                      WHERE e.id = :itemid";
-    $glossaryparams = array('itemid'=>$params['itemid']);
-    if (!$info = $DB->get_record_sql($glossarysql, $glossaryparams)) {
+    $glossaryparams = array('itemid' => $params['itemid']);
+    $info = $DB->get_record_sql($glossarysql, $glossaryparams);
+    if (!$info) {
         //item doesn't exist
         throw new rating_exception('invaliditemid');
     }
-
+        
     if ($info->scale != $params['scaleid']) {
         //the scale being submitted doesnt match the one in the database
         throw new rating_exception('invalidscaleid');
     }
 
-    if ($info->userid == $USER->id) {
-        //user is attempting to rate their own glossary entry
-        throw new rating_exception('nopermissiontorate');
-    }
-
-    if ($info->userid != $params['rateduserid']) {
-        //supplied user ID doesnt match the user ID from the database
-        throw new rating_exception('invaliduserid');
-    }
-
     //check that the submitted rating is valid for the scale
     if ($params['rating'] < 0) {
         throw new rating_exception('invalidnum');
     } else if ($info->scale < 0) {
         //its a custom scale
-        $scalerecord = $DB->get_record('scale', array('id' => -$params['scaleid']));
+        $scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
         if ($scalerecord) {
             $scalearray = explode(',', $scalerecord->scale);
             if ($params['rating'] > count($scalearray)) {
@@ -556,16 +564,11 @@ function glossary_rating_validate($params) {
         }
     }
 
-    $glossaryid = $info->gid;
-
-    $cm = get_coursemodule_from_instance('glossary', $glossaryid);
-    if (empty($cm)) {
-        throw new rating_exception('unknowncontext');
-    }
-    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+    $cm = get_coursemodule_from_instance('glossary', $info->glossaryid, $info->course, false, MUST_EXIST);
+    $context = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
 
-    //if the supplied context doesnt match the item's context
-    if (empty($context) || $context->id != $params['context']->id) {
+    // if the supplied context doesnt match the item's context
+    if ($context->id != $params['context']->id) {
         throw new rating_exception('invalidcontext');
     }
 
@@ -683,7 +686,8 @@ function glossary_grade_item_delete($glossary) {
  * Returns the users with data in one glossary
  * (users with records in glossary_entries, students)
  *
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $glossaryid
  * @return array
  */
@@ -2190,31 +2194,49 @@ function glossary_full_tag($tag,$level=0,$endline=true,$content) {
 /**
  * How many unrated entries are in the given glossary for a given user?
  *
- * @global object
+ * @global moodle_database $DB
  * @param int $glossaryid
  * @param int $userid
  * @return int
  */
 function glossary_count_unrated_entries($glossaryid, $userid) {
     global $DB;
-    if ($entries = $DB->get_record_sql("SELECT count('x') as num
-                                          FROM {glossary_entries}
-                                         WHERE glossaryid = ? AND userid <> ?", array($glossaryid, $userid))) {
-
-        if (!$cm = get_coursemodule_from_instance('glossary', $glossaryid)) {
-            return 0;
-        }
-        $context = get_context_instance(CONTEXT_MODULE, $cm->id);
-
-        if ($rated = $DB->get_record_sql("SELECT count(*) as num
-                                            FROM {glossary_entries} e, {ratings} r
-                                           WHERE e.glossaryid = :glossaryid AND e.id = r.itemid
-                                                 AND r.userid = :userid and r.contextid = :contextid",
-                array('glossaryid'=>$glossaryid, 'userid'=>$userid, 'contextid'=>$context->id))) {
 
-            $difference = $entries->num - $rated->num;
-            if ($difference > 0) {
-                return $difference;
+    $sql = "SELECT COUNT('x') as num
+              FROM {glossary_entries}
+             WHERE glossaryid = :glossaryid AND
+                   userid <> :userid";
+    $params = array('glossaryid' => $glossaryid, 'userid' => $userid);
+    $entries = $DB->count_records_sql($sql, $params);
+
+    if ($entries) {
+        // We need to get the contextid for the glossaryid we have been given.
+        $sql = "SELECT ctx.id
+                  FROM {context} ctx
+                  JOIN {course_modules} cm ON cm.id = ctx.instanceid
+                  JOIN {modules} m ON m.id = cm.module
+                  JOIN {glossary} g ON g.id = cm.instance
+                 WHERE ctx.contextlevel = :contextlevel AND
+                       m.name = 'glossary' AND
+                       g.id = :glossaryid";
+        $contextid = $DB->get_field_sql($sql, array('glossaryid' => $glossaryid, 'contextlevel' => CONTEXT_MODULE));
+
+        // Now we need to count the ratings that this user has made
+        $sql = "SELECT COUNT('x') AS num
+                  FROM {glossary_entries} e
+                  JOIN {ratings} r ON r.itemid = e.id
+                 WHERE e.glossaryid = :glossaryid AND
+                       r.userid = :userid AND
+                       r.component = 'mod_glossary' AND
+                       r.ratingarea = 'entry' AND
+                       r.contextid = :contextid";
+        $params = array('glossaryid' => $glossaryid, 'userid' => $userid, 'contextid' => $context->id);
+        $rated = $DB->count_records_sql($sql, $params);
+        if ($rated) {
+            // The number or enties minus the number or rated entries equals the number of unrated
+            // entries
+            if ($entries->num > $rated->num) {
+                return $entries->num - $rated->num;
             } else {
                 return 0;    // Just in case there was a counting error
             }
@@ -2466,7 +2488,9 @@ function glossary_reset_userdata($data) {
     $fs = get_file_storage();
 
     $rm = new rating_manager();
-    $ratingdeloptions = new stdclass();
+    $ratingdeloptions = new stdClass;
+    $ratingdeloptions->component = 'mod_glossary';
+    $ratingdeloptions->ratingarea = 'entry';
 
     // delete entries if requested
     if (!empty($data->reset_glossary_all)
index 5f7050f..6210db6 100644 (file)
@@ -5,8 +5,8 @@
 ///  This fragment is called by moodle_needs_upgrading() and /admin/index.php
 /////////////////////////////////////////////////////////////////////////////////
 
-$module->version  = 2010111501;
-$module->requires = 2010080300;  // Requires this Moodle version
+$module->version  = 2011052300;
+$module->requires = 2011052300;  // Requires this Moodle version
 $module->cron     = 0;           // Period for cron to check this module (secs)
 
 
index c77941b..f5d8687 100644 (file)
@@ -394,13 +394,13 @@ if ($allentries) {
     echo $paging;
     echo '</div>';
 
-
     //load ratings
     require_once($CFG->dirroot.'/rating/lib.php');
-    if ($glossary->assessed!=RATING_AGGREGATE_NONE) {
-        $ratingoptions = new stdclass();
+    if ($glossary->assessed != RATING_AGGREGATE_NONE) {
+        $ratingoptions = new stdClass;
         $ratingoptions->context = $context;
         $ratingoptions->component = 'mod_glossary';
+        $ratingoptions->ratingarea = 'entry';
         $ratingoptions->items = $allentries;
         $ratingoptions->aggregate = $glossary->assessed;//the aggregation method
         $ratingoptions->scaleid = $glossary->scale;
index 94dc67a..5662730 100644 (file)
@@ -249,6 +249,8 @@ function imscp_user_complete($course, $user, $mod, $imscp) {
 /**
  * Returns the users with data in one imscp
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $imscpid
  * @return bool false
  */
index 2e21e13..d11ba19 100644 (file)
@@ -116,6 +116,8 @@ function label_delete_instance($id) {
  * Returns the users with data in one resource
  * (NONE, but must exist on EVERY mod !!)
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $labelid
  */
 function label_get_participants($labelid) {
index 4c866b7..efaec0f 100644 (file)
@@ -528,8 +528,8 @@ function lesson_grade_item_delete($lesson) {
  * for a given instance of lesson. Must include every user involved
  * in the instance, independent of his role (student, teacher, admin...)
  *
- * @global stdClass
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $lessonid
  * @return array
  */
index ce8aef1..b94116e 100644 (file)
@@ -231,6 +231,8 @@ function page_user_complete($course, $user, $mod, $page) {
 /**
  * Returns the users with data in one page
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $pageid
  * @return bool false
  */
index be6b2b6..5524773 100644 (file)
@@ -745,8 +745,8 @@ function quiz_get_grading_options() {
 /**
  * Returns an array of users who have data in a given quiz
  *
- * @global stdClass
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $quizid
  * @return array
  */
index cc4e315..79091f5 100644 (file)
@@ -210,6 +210,8 @@ function resource_user_complete($course, $user, $mod, $resource) {
 /**
  * Returns the users with data in one resource
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $resourceid
  * @return bool false
  */
index fa51e16..2537066 100644 (file)
@@ -271,7 +271,8 @@ function survey_print_recent_activity($course, $viewfullnames, $timestart) {
  * Returns the users with data in one survey
  * (users with records in survey_analysis and survey_answers, students)
  *
- * @global object
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $surveyid
  * @return array
  */
index f79913b..39b10d4 100644 (file)
@@ -237,6 +237,8 @@ function url_user_complete($course, $user, $mod, $url) {
 /**
  * Returns the users with data in one url
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $urlid
  * @return bool false
  */
index 024f2be..230e7bd 100644 (file)
@@ -382,6 +382,8 @@ function wiki_grades($wikiid) {
  * in the instance, independient of his role (student, teacher, admin...)
  * See other modules as example.
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $wikiid ID of an instance of this module
  * @return mixed boolean/array of students
  **/
index 83aded8..f586dec 100644 (file)
@@ -898,6 +898,8 @@ function workshop_cron () {
  * are not returned as the example submission is considered non-user
  * data for the purpose of workshop backup.
  *
+ * @todo: deprecated - to be deleted in 2.2
+ *
  * @param int $workshopid ID of an instance of this module
  * @return array of user ids, empty if there are no participants
  */
index ff9b121..bdf3994 100644 (file)
 require_once("../config.php");
 require_once("lib.php");
 
-$contextid   = required_param('contextid', PARAM_INT);
-$itemid   = required_param('itemid', PARAM_INT);
-$scaleid   = required_param('scaleid', PARAM_INT);
-$sort = optional_param('sort', '', PARAM_ALPHA);
-$popup = optional_param('popup', 0, PARAM_INT);//==1 if in a popup window?
+$contextid  = required_param('contextid', PARAM_INT);
+$component  = required_param('component', PARAM_ALPHAEXT);
+$ratingarea = optional_param('ratingarea', null, PARAM_ALPHANUMEXT);
+$itemid     = required_param('itemid', PARAM_INT);
+$scaleid    = required_param('scaleid', PARAM_INT);
+$sort       = optional_param('sort', '', PARAM_ALPHA);
+$popup      = optional_param('popup', 0, PARAM_INT); //==1 if in a popup window?
 
 list($context, $course, $cm) = get_context_info_array($contextid);
 require_login($course, false, $cm);
@@ -69,8 +71,10 @@ $strtime    = get_string('time');
 $PAGE->set_title(get_string('allratingsforitem','rating'));
 echo $OUTPUT->header();
 
-$ratingoptions = new stdclass();
+$ratingoptions = new stdClass;
 $ratingoptions->context = $context;
+$ratingoptions->component = $component;
+$ratingoptions->ratingarea = $ratingarea;
 $ratingoptions->itemid = $itemid;
 $ratingoptions->sort = $sqlsort;
 
@@ -80,17 +84,23 @@ if (!$ratings) {
     $msg = get_string('noratings','rating');
     echo html_writer::tag('div', $msg, array('class'=>'mdl-align'));
 } else {
-    $sortargs = "contextid=$contextid&amp;itemid=$itemid&amp;scaleid=$scaleid";
-    if($popup) {
-        $sortargs.="&amp;popup=$popup";
+    $sorturl  = new moodle_url('/index.php', array('contextid' => $contextid, 'itemid' => $itemid, 'scaleid' => $scaleid));
+    if ($popup) {
+        $sorturl->param('popup', $popup);
     }
-    echo "<table border=\"0\" cellpadding=\"3\" cellspacing=\"3\" class=\"generalbox\" style=\"width:100%\">";
-    echo "<tr>";
-    echo "<th class=\"header\" scope=\"col\">&nbsp;</th>";
-    echo "<th class=\"header\" scope=\"col\"><a href=\"index.php?$sortargs&amp;sort=firstname\">$strname</a></th>";
-    echo "<th class=\"header\" scope=\"col\" style=\"width:100%\"><a href=\"index.php?$sortargs&amp;sort=rating\">$strrating</a></th>";
-    echo "<th class=\"header\" scope=\"col\"><a href=\"index.php?$sortargs&amp;sort=time\">$strtime</a></th>";
-    echo "</tr>";
+    
+    $table = new html_table;
+    $table->cellpadding = 3;
+    $table->cellspacing = 3;
+    $table->attributes['class'] = 'generalbox ratingtable';
+    $table->head = array(
+        '',
+        html_writer::link(new moodle_url($sorturl, array('sort' => 'firstname')), $strname),
+        html_writer::link(new moodle_url($sorturl, array('sort' => 'rating')), $strrating),
+        html_writer::link(new moodle_url($sorturl, array('sort' => 'time')), $strtime)
+    );
+    $table->colclasses = array('', 'firstname', 'rating', 'time');
+    $table->data = array();
 
     //if the scale was changed after ratings were submitted some ratings may have a value above the current maximum
     $maxrating = count($scalemenu) - 1;
@@ -100,29 +110,24 @@ if (!$ratings) {
         //but we don't
         $rating->id = $rating->userid;
 
-        echo '<tr class="ratingitemheader">';
-        echo "<td>";
-        if($course && $course->id) {
-            echo $OUTPUT->user_picture($rating, array('courseid'=>$course->id));
+        $row = new html_table_row();
+        $row->attributes['class'] = 'ratingitemheader';
+        if ($course && $course->id) {
+            $row->cells[] = $OUTPUT->user_picture($rating, array('courseid' => $course->id));
         } else {
-            echo $OUTPUT->user_picture($rating);
+            $row->cells[] = $OUTPUT->user_picture($rating);
         }
-        echo '</td><td>'.fullname($rating).'</td>';
-        
-        //if they've switched to rating out of 5 but there were ratings submitted out of 10 for example
-        //Not doing this within $rm->get_all_ratings_for_item to allow access to the raw data
+        $row->cells[] = fullname($rating);
         if ($rating->rating > $maxrating) {
             $rating->rating = $maxrating;
         }
-        echo '<td style="white-space:nowrap" align="center" class="rating">'.$scalemenu[$rating->rating]."</td>";
-        echo '<td style="white-space:nowrap" align="center" class="time">'.userdate($rating->timemodified)."</td>";
-        echo "</tr>\n";
+        $row->cells[] = $scalemenu[$rating->rating];
+        $row->cells[] = userdate($rating->timemodified);
+        $table->data[] = $row;
     }
-    echo "</table>";
-    echo "<br />";
+    echo html_writer::table($table);
 }
-
 if ($popup) {
     echo $OUTPUT->close_window_button();
 }
-echo $OUTPUT->footer();
+echo $OUTPUT->footer();
\ No newline at end of file
index 881c10d..9cad2ca 100644 (file)
@@ -46,16 +46,23 @@ class rating implements renderable {
 
     /**
      * The context in which this rating exists
-     * @var context
+     * @var stdClass
      */
     public $context;
 
     /**
      * The component using ratings. For example "mod_forum"
-     * @var component
+     * @var string
      */
     public $component;
 
+    /**
+     * The rating area to associate this rating with.
+     * This allows a plugin to rate more than one thing by specifying different rating areas.
+     * @var string
+     */
+    public $ratingarea = null;
+
     /**
      * The id of the item (forum post, glossary item etc) being rated
      * @var int
@@ -81,78 +88,288 @@ class rating implements renderable {
     public $settings;
 
     /**
-    * Constructor.
-    * @param object $options {
-    *            context => context context to use for the rating [required]
-    *            component => component using ratings ie mod_forum [required]
-    *            itemid  => int the id of the associated item (forum post, glossary item etc) [required]
-    *            scaleid => int The scale in use when the rating was submitted [required]
-    *            userid  => int The id of the user who submitted the rating [required]
-    * }
-    */
+     * The Id of this rating within the rating table.
+     * This is only set if the rating already exists
+     * @var int
+     */
+    public $id = null;
+
+    /**
+     * The aggregate of the combined ratings for the associated item.
+     * This is only set if the rating already exists
+     *
+     * @var int
+     */
+    public $aggregate = null;
+
+    /**
+     * The total number of ratings for the associated item.
+     * This is only set if the rating already exists
+     *
+     * @var int
+     */
+    public $count = 0;
+
+    /**
+     * The rating the associated user gave the associated item
+     * This is only set if the rating already exists
+     *
+     * @var int
+     */
+    public $rating = null;
+
+    /**
+     * The time the associated item was created
+     *
+     * @var int
+     */
+    public $itemtimecreated = null;
+
+    /**
+     * The id of the user who submitted the rating
+     *
+     * @var int
+     */
+    public $itemuserid = null;
+
+    /**
+     * Constructor.
+     * @param object $options {
+     *            context => context context to use for the rating [required]
+     *            component => component using ratings ie mod_forum [required]
+     *            ratingarea => ratingarea to associate this rating with [required]
+     *            itemid  => int the id of the associated item (forum post, glossary item etc) [required]
+     *            scaleid => int The scale in use when the rating was submitted [required]
+     *            userid  => int The id of the user who submitted the rating [required]
+     *            settings => Settings for the rating object [optional]
+     *            id => The id of this rating (if the rating is from the db) [optional]
+     *            aggregate => The aggregate for the rating [optional]
+     *            count => The number of ratings [optional]
+     *            rating => The rating given by the user [optional]
+     * }
+     */
     public function __construct($options) {
-        $this->context = $options->context;
-        $this->component = $options->component;
-        $this->itemid = $options->itemid;
-        $this->scaleid = $options->scaleid;
-        $this->userid = $options->userid;
+        $this->context =    $options->context;
+        $this->component =  $options->component;
+        $this->ratingarea = $options->ratingarea;
+        $this->itemid =     $options->itemid;
+        $this->scaleid =    $options->scaleid;
+        $this->userid =     $options->userid;
+
+        if (isset($options->settings)) {
+            $this->settings = $options->settings;
+        }
+        if (isset($options->id)) {
+            $this->id = $options->id;
+        }
+        if (isset($options->aggregate)) {
+            $this->aggregate = $options->aggregate;
+        }
+        if (isset($options->count)) {
+            $this->count = $options->count;
+        }
+        if (isset($options->rating)) {
+            $this->rating = $options->rating;
+        }
     }
 
     /**
-    * Update this rating in the database
-    * @param int $rating the integer value of this rating
-    * @return void
-    */
+     * Update this rating in the database
+     * @param int $rating the integer value of this rating
+     * @return void
+     */
     public function update_rating($rating) {
         global $DB;
 
-        $data = new stdclass();
-        $table = 'rating';
+        $time = time();
+
+        $data = new stdClass;
+        $data->rating       = $rating;
+        $data->timemodified = $time;
 
         $item = new stdclass();
         $item->id = $this->itemid;
         $items = array($item);
 
-        $ratingoptions = new stdclass();
+        $ratingoptions = new stdClass;
         $ratingoptions->context = $this->context;
         $ratingoptions->component = $this->component;
+        $ratingoptions->ratingarea = $this->ratingarea;
         $ratingoptions->items = $items;
         $ratingoptions->aggregate = RATING_AGGREGATE_AVERAGE;//we dont actually care what aggregation method is applied
         $ratingoptions->scaleid = $this->scaleid;
         $ratingoptions->userid = $this->userid;
 
-        $rm = new rating_manager();
+        $rm = new rating_manager();;
         $items = $rm->get_ratings($ratingoptions);
-        if( empty($items) || empty($items[0]->rating) || empty($items[0]->rating->id) ) {
+        $firstitem = $items[0]->rating;
+
+        if (empty($firstitem->id)) {
+            // Insert a new rating
             $data->contextid    = $this->context->id;
+            $data->component    = $this->component;
+            $data->ratingarea   = $this->ratingarea;
             $data->rating       = $rating;
             $data->scaleid      = $this->scaleid;
             $data->userid       = $this->userid;
             $data->itemid       = $this->itemid;
-
-            $time = time();
-            $data->timecreated = $time;
+            $data->timecreated  = $time;
             $data->timemodified = $time;
+            $DB->insert_record('rating', $data);
+        } else {
+            // Update the rating
+            $data->id           = $firstitem->id;
+            $DB->update_record('rating', $data);
+        }
+    }
 
-            $DB->insert_record($table, $data);
+    /**
+     * Retreive the integer value of this rating
+     * @return int the integer value of this rating object
+     */
+    public function get_rating() {
+        return $this->rating;
+    }
+
+    /**
+     * Returns this ratings aggregate value as a string.
+     *
+     * @return string
+     */
+    public function get_aggregate_string() {
+
+        $aggregate = $this->aggregate;
+        $method = $this->settings->aggregationmethod;
+
+        // only display aggregate if aggregation method isn't COUNT
+        $aggregatestr = '';
+        if ($aggregate && $method != RATING_AGGREGATE_COUNT) {
+            if ($method != RATING_AGGREGATE_SUM && !$this->settings->scale->isnumeric) {
+                $aggregatestr .= $this->settings->scale->scaleitems[round($aggregate)]; //round aggregate as we're using it as an index
+            } else { // aggregation is SUM or the scale is numeric
+                $aggregatestr .= round($aggregate, 1);
+            }
         }
-        else {
-            $data->id       = $items[0]->rating->id;
-            $data->rating       = $rating;
 
-            $time = time();
-            $data->timemodified = $time;
+        return $aggregatestr;
+    }
 
-            $DB->update_record($table, $data);
+    /**
+     * Returns true if the user is able to rate this rating object
+     *
+     * @param int $userid Current user assumed if left empty
+     * @return bool
+     */
+    public function user_can_rate($userid = null) {
+        if (empty($userid)) {
+            global $USER;
+            $userid = $USER->id;
         }
+        // You can't rate your item
+        if ($this->itemuserid == $userid) {
+            return false;
+        }
+        // You can't rate if you don't have the system cap
+        if (!$this->settings->permissions->rate) {
+            return false;
+        }
+        // You can't rate if you don't have the plugin cap
+        if (!$this->settings->pluginpermissions->rate) {
+            return false;
+        }
+
+        // You can't rate if the item was outside of the assessment times
+        $timestart = $this->settings->assesstimestart;
+        $timefinish = $this->settings->assesstimefinish;
+        $timecreated = $this->itemtimecreated;
+        if (!empty($timestart) && !empty($timefinish) && ($timecreated < $timestart || $timecreated > $timefinish)) {
+            return false;
+        }
+        return true;
     }
 
     /**
-    * Retreive the integer value of this rating
-    * @return int the integer value of this rating object
-    */
-    public function get_rating() {
-        return $this->rating;
+     * Returns true if the user is able to view the aggregate for this rating object.
+     *
+     * @param int|null $userid If left empty the current user is assumed.
+     * @return bool
+     */
+    public function user_can_view_aggregate($userid = null) {
+        if (empty($userid)) {
+            global $USER;
+            $userid = $USER->id;
+        }
+
+        // if the item doesnt belong to anyone or its another user's items and they can see the aggregate on items they don't own
+        // Note that viewany doesnt mean you can see the aggregate or ratings of your own items
+        if ((empty($this->itemuserid) or $this->itemuserid != $userid) && $this->settings->permissions->viewany && $this->settings->pluginpermissions->viewany ) {
+            return true;
+        }
+
+        // if its the current user's item and they have permission to view the aggregate on their own items
+        if ($this->itemuserid == $userid && $this->settings->permissions->view && $this->settings->pluginpermissions->view) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a URL to view all of the ratings for the item this rating is for.
+     *
+     * If this is a rating of a post then this URL will take the user to a page that shows all
+     * of the ratings for the post (this one included).
+     *
+     * @param bool $popup
+     * @return moodle_url
+     */
+    public function get_view_ratings_url($popup = false) {
+        $attributes = array(
+            'contextid'  => $this->context->id,
+            'component'  => $this->component,
+            'ratingarea' => $this->ratingarea,
+            'itemid'     => $this->itemid,
+            'scaleid'    => $this->settings->scale->id
+        );
+        if ($popup) {
+            $attributes['popup'] = 1;
+        }
+        return new moodle_url('/rating/index.php', $attributes);
+    }
+
+    /**
+     * Returns a URL that can be used to rate the associated item.
+     *
+     * @param int|null $rating The rating to give the item, if null then no rating
+     *                         param is added.
+     * @param moodle_url|string $returnurl The URL to return to.
+     * @return moodle_url
+     */
+    public function get_rate_url($rating = null, $returnurl = null) {
+        if (empty($returnurl)) {
+            if (!empty($this->settings->returnurl)) {
+                $returnurl = $this->settings->returnurl;
+            } else {
+                global $PAGE;
+                $returnurl = $PAGE->url;
+            }
+        }
+        $args = array(
+            'contextid'   => $this->context->id,
+            'component'   => $this->component,
+            'ratingarea'  => $this->ratingarea,
+            'itemid'      => $this->itemid,
+            'scaleid'     => $this->settings->scale->id,
+            'returnurl'   => $returnurl,
+            'rateduserid' => $this->itemuserid,
+            'aggregation' => $this->settings->aggregationmethod,
+            'sesskey'     => sesskey()
+        );
+        if (!empty($rating)) {
+            $args['rating'] = $rating;
+        }
+        $url = new moodle_url('/rating/rate.php', $args);
+        return $url;
     }
 
     /**
@@ -174,273 +391,365 @@ class rating implements renderable {
 class rating_manager {
 
     /**
-    * Delete one or more ratings. Specify either a rating id, an item id or just the context id.
-    * @param object $options {
-    *            contextid => int the context in which the ratings exist [required]
-    *            ratingid => int the id of an individual rating to delete [optional]
-    *            userid => int delete the ratings submitted by this user. May be used in conjuction with itemid [optional]
-    *            itemid => int delete all ratings attached to this item [optional]
-    * }
-    * @return void
-    */
+     * An array of calculated scale options to save us generating them for each request.
+     * @var array
+     */
+    protected $scales = array();
+
+    /**
+     * Gets set to true when the JavaScript that controls AJAX rating has been
+     * initialised (so that it only gets initialised once.
+     * @var int
+     */
+    protected $javascriptinitialised = false;
+
+    /**
+     * Delete one or more ratings. Specify either a rating id, an item id or just the context id.
+     *
+     * @global moodle_database $DB
+     * @param stdClass $options {
+     *            contextid => int the context in which the ratings exist [required]
+     *            ratingid => int the id of an individual rating to delete [optional]
+     *            userid => int delete the ratings submitted by this user. May be used in conjuction with itemid [optional]
+     *            itemid => int delete all ratings attached to this item [optional]
+     *            component => string The component to delete ratings from [optional]
+     *            ratingarea => string The ratingarea to delete ratings from [optional]
+     * }
+     * @return void
+     */
     public function delete_ratings($options) {
         global $DB;
 
-        if( !empty($options->ratingid) ) {
-            //delete a single rating
-            $DB->delete_records('rating', array('contextid'=>$options->contextid, 'id'=>$options->ratingid) );
-        }
-        else if( !empty($options->itemid) && !empty($options->userid) ) {
-            //delete the rating for an item submitted by a particular user
-            $DB->delete_records('rating', array('contextid'=>$options->contextid, 'itemid'=>$options->itemid, 'userid'=>$options->userid) );
+        if (empty($options->contextid)) {
+            throw new coding_exception('The context option is a required option when deleting ratings.');
         }
-        else if( !empty($options->itemid) ) {
-            //delete all ratings for an item
-            $DB->delete_records('rating', array('contextid'=>$options->contextid, 'itemid'=>$options->itemid) );
-        }
-        else if( !empty($options->userid) ) {
-            //delete all ratings submitted by a user
-            $DB->delete_records('rating', array('contextid'=>$options->contextid, 'userid'=>$options->userid) );
-        }
-        else {
-            //delete all ratings for this context
-            $DB->delete_records('rating', array('contextid'=>$options->contextid) );
+
+        $conditions = array('contextid' => $options->contextid);
+        $possibleconditions = array(
+            'ratingid'   => 'id',
+            'userid'     => 'userid',
+            'itemid'     => 'itemid',
+            'component'  => 'component',
+            'ratingarea' => 'ratingarea'
+        );
+        foreach ($possibleconditions as $option => $field) {
+            if (isset($options->{$option})) {
+                $conditions[$field] = $options->{$option};
+            }
         }
+        $DB->delete_records('rating', $conditions);
     }
 
     /**
-    * Returns an array of ratings for a given item (forum post, glossary entry etc)
-    * This returns all users ratings for a single item
-    * @param object $options {
-    *            context => context the context in which the ratings exists [required]
-    *            itemid  =>  int the id of the associated item (forum post, glossary item etc) [required]
-    *            sort    => string SQL sort by clause [optional]
-    * }
-    * @return array an array of ratings
-    */
+     * Returns an array of ratings for a given item (forum post, glossary entry etc)
+     * This returns all users ratings for a single item
+     * @param stdClass $options {
+     *            context => context the context in which the ratings exists [required]
+     *            component => component using ratings ie mod_forum [required]
+     *            ratingarea => ratingarea to associate this rating with [required]
+     *            itemid  =>  int the id of the associated item (forum post, glossary item etc) [required]
+     *            sort    => string SQL sort by clause [optional]
+     * }
+     * @return array an array of ratings
+     */
     public function get_all_ratings_for_item($options) {
         global $DB;
 
+        if (!isset($options->context)) {
+            throw new coding_exception('The context option is a required option when getting ratings for an item.');
+        }
+        if (!isset($options->itemid)) {
+            throw new coding_exception('The itemid option is a required option when getting ratings for an item.');
+        }
+        if (!isset($options->component)) {
+            throw new coding_exception('The component option is now a required option when getting ratings for an item.');
+        }
+        if (!isset($options->ratingarea)) {
+            throw new coding_exception('The ratingarea option is now a required option when getting ratings for an item.');
+        }
+
         $sortclause = '';
         if( !empty($options->sort) ) {
             $sortclause = "ORDER BY $options->sort";
         }
 
+        $params = array(
+            'contextid'  => $options->context->id,
+            'itemid'     => $options->itemid,
+            'component'  => $options->component,
+            'ratingarea' => $options->ratingarea,
+        );
         $userfields = user_picture::fields('u', null, 'userid');
-        $sql = "SELECT r.id, r.rating, r.itemid, r.userid, r.timemodified, $userfields
-                FROM {rating} r
-                LEFT JOIN {user} u ON r.userid = u.id
-                WHERE r.contextid = :contextid AND
-                      r.itemid  = :itemid
-                {$sortclause}";
-
-        $params['contextid'] = $options->context->id;
-        $params['itemid'] = $options->itemid;
+        $sql = "SELECT r.id, r.rating, r.itemid, r.userid, r.timemodified, r.component, r.ratingarea, $userfields
+                  FROM {rating} r
+             LEFT JOIN {user} u ON r.userid = u.id
+                 WHERE r.contextid = :contextid AND
+                       r.itemid  = :itemid AND
+                       r.component = :component AND
+                       r.ratingarea = :ratingarea
+                       {$sortclause}";
 
         return $DB->get_records_sql($sql, $params);
     }
 
     /**
-    * Adds rating objects to an array of items (forum posts, glossary entries etc)
-    * Rating objects are available at $item->rating
-    * @param object $options {
-    *            context => context the context in which the ratings exists [required]
-    *            component => the component name ie mod_forum [required]
-    *            items  => array an array of items such as forum posts or glossary items. They must have an 'id' member ie $items[0]->id[required]
-    *            aggregate    => int what aggregation method should be applied. RATING_AGGREGATE_AVERAGE, RATING_AGGREGATE_MAXIMUM etc [required]
-    *            scaleid => int the scale from which the user can select a rating [required]
-    *            userid => int the id of the current user [optional]
-    *            returnurl => string the url to return the user to after submitting a rating. Can be left null for ajax requests [optional]
-    *            assesstimestart => int only allow rating of items created after this timestamp [optional]
-    *            assesstimefinish => int only allow rating of items created before this timestamp [optional]
-    * @return array the array of items with their ratings attached at $items[0]->rating
-    */
+     * Adds rating objects to an array of items (forum posts, glossary entries etc)
+     * Rating objects are available at $item->rating
+     * @param stdClass $options {
+     *            context          => context the context in which the ratings exists [required]
+     *            component        => the component name ie mod_forum [required]
+     *            ratingarea       => the ratingarea we are interested in [required]
+     *            items            => array an array of items such as forum posts or glossary items. They must have an 'id' member ie $items[0]->id[required]
+     *            aggregate        => int what aggregation method should be applied. RATING_AGGREGATE_AVERAGE, RATING_AGGREGATE_MAXIMUM etc [required]
+     *            scaleid          => int the scale from which the user can select a rating [required]
+     *            userid           => int the id of the current user [optional]
+     *            returnurl        => string the url to return the user to after submitting a rating. Can be left null for ajax requests [optional]
+     *            assesstimestart  => int only allow rating of items created after this timestamp [optional]
+     *            assesstimefinish => int only allow rating of items created before this timestamp [optional]
+     * @return array the array of items with their ratings attached at $items[0]->rating
+     */
     public function get_ratings($options) {
-        global $DB, $USER, $PAGE, $CFG;
+        global $DB, $USER;
 
-        //are ratings enabled?
-        if ($options->aggregate==RATING_AGGREGATE_NONE) {
-            return $options->items;
+        if (!isset($options->context)) {
+            throw new coding_exception('The context option is a required option when getting ratings.');
+        }
+
+        if (!isset($options->component)) {
+            throw new coding_exception('The component option is a required option when getting ratings.');
+        }
+
+        if (!isset($options->ratingarea)) {
+            throw new coding_exception('The ratingarea option is a required option when getting ratings.');
         }
-        $aggregatestr = $this->get_aggregation_method($options->aggregate);
 
-        if(empty($options->items)) {
+        if (!isset($options->scaleid)) {
+            throw new coding_exception('The scaleid option is a required option when getting ratings.');
+        }
+
+        if (!isset($options->items)) {
+            throw new coding_exception('The items option is a required option when getting ratings.');
+        } else if (empty($options->items)) {
+            return array();
+        }
+
+        if (!isset($options->aggregate)) {
+            throw new coding_exception('The aggregate option is a required option when getting ratings.');
+        } else if ($options->aggregate == RATING_AGGREGATE_NONE) {
+            // Ratings arn't enabled.
             return $options->items;
         }
+        $aggregatestr = $this->get_aggregation_method($options->aggregate);
 
-        $userid = null;
+        // Default the userid to the current user if it is not set
         if (empty($options->userid)) {
             $userid = $USER->id;
         } else {
             $userid = $options->userid;
         }
 
-        //create an array of item ids
+        // Get the item table name, the item id field, and the item user field for the given rating item
+        // from the related component.
+        list($type, $name) = normalize_component($options->component);
+        $default = array(null, 'id', 'userid');
+        list($itemtablename, $itemidcol, $itemuseridcol) = plugin_callback($type, $name, 'rating', 'get_item_fields', array($options), $default);
+
+        // Create an array of item ids
         $itemids = array();
-        foreach($options->items as $item) {
-            $itemids[] = $item->id;
-        }
-
-        //get the items from the database
-        list($itemidtest, $params) = $DB->get_in_or_equal(
-                $itemids, SQL_PARAMS_NAMED, 'itemid0000');
-
-       //note: all the group bys arent really necessary but PostgreSQL complains
-       //about selecting a mixture of grouped and non-grouped columns
-        $sql = "SELECT r.itemid, ur.id, ur.userid, ur.scaleid,
-        $aggregatestr(r.rating) AS aggrrating,
-        COUNT(r.rating) AS numratings,
-        ur.rating AS usersrating
-    FROM {rating} r
-    LEFT JOIN {rating} ur ON ur.contextid = r.contextid AND
-            ur.itemid = r.itemid AND
-            ur.userid = :userid
-    WHERE
-        r.contextid = :contextid AND
-        r.itemid $itemidtest
-    GROUP BY r.itemid, ur.rating, ur.id, ur.userid, ur.scaleid
-    ORDER BY r.itemid";
-
-        $params['userid'] = $userid;
-        $params['contextid'] = $options->context->id;
+        foreach ($options->items as $item) {
+            $itemids[] = $item->{$itemidcol};
+        }
 
+        // get the items from the database
+        list($itemidtest, $params) = $DB->get_in_or_equal($itemids, SQL_PARAMS_NAMED);
+        $params['contextid'] = $options->context->id;
+        $params['userid']    = $userid;
+        $params['component']    = $options->component;
+        $params['ratingarea'] = $options->ratingarea;
+
+        $sql = "SELECT r.itemid, r.component, r.ratingarea, r.contextid,
+                       $aggregatestr(r.rating) AS aggrrating, COUNT(r.rating) AS numratings,
+                       ur.id, ur.userid, ur.scaleid, ur.rating AS usersrating
+                  FROM {rating} r
+             LEFT JOIN {rating} ur ON ur.contextid = r.contextid AND 
+                                      ur.itemid = r.itemid AND 
+                                      ur.component = r.component AND 
+                                      ur.ratingarea = r.ratingarea AND 
+                                      ur.userid = :userid
+                 WHERE r.contextid = :contextid AND
+                       r.itemid {$itemidtest} AND
+                       r.component = :component AND
+                       r.ratingarea = :ratingarea
+              GROUP BY r.itemid, r.component, r.ratingarea, r.contextid, ur.id, ur.userid, ur.scaleid
+              ORDER BY r.itemid";
         $ratingsrecords = $DB->get_records_sql($sql, $params);
 
-        //now create the rating sub objects
-        $scaleobj = new stdClass();
-        $scalemax = null;
-
-        //we could look for a scale id on each item to allow each item to use a different scale
-        if($options->scaleid < 0 ) { //if its a scale (not numeric)
-            $scalerecord = $DB->get_record('scale', array('id' => -$options->scaleid));
-            if ($scalerecord) {
-                $scalearray = explode(',', $scalerecord->scale);
-
-                //is there a more efficient way to get the indexes to start at 1 instead of 0?
-                //this will go away when scales are refactored
-                $c = count($scalearray);
-                $n = null;
-                for($i=0; $i<$c; $i++) {
-                    $n = $i+1;
-                    $scaleobj->scaleitems["$n"] = $scalearray[$i];//treat index as a string to allow sorting without changing the value
-                }
-                krsort($scaleobj->scaleitems);//have the highest grade scale item appear first
-
-                $scaleobj->id = $options->scaleid;//dont use the one from the record or we "forget" that its negative
-                $scaleobj->name = $scalerecord->name;
-                $scaleobj->courseid = $scalerecord->courseid;
+        $ratingoptions = new stdClass;
+        $ratingoptions->context = $options->context;
+        $ratingoptions->component = $options->component;
+        $ratingoptions->ratingarea = $options->ratingarea;
+        $ratingoptions->settings = $this->generate_rating_settings_object($options);
+        foreach ($options->items as $item) {
+            if (array_key_exists($item->{$itemidcol}, $ratingsrecords)) {
+                // Note: rec->scaleid = the id of scale at the time the rating was submitted
+                // may be different from the current scale id
+                $rec = $ratingsrecords[$item->{$itemidcol}];
+                $ratingoptions->itemid = $item->{$itemidcol};
+                $ratingoptions->scaleid = $rec->scaleid;
+                $ratingoptions->userid = $rec->userid;
+                $ratingoptions->id = $rec->id;
+                $ratingoptions->aggregate = min($rec->aggrrating, $ratingoptions->settings->scale->max);
+                $ratingoptions->count = $rec->numratings;
+                $ratingoptions->rating = min($rec->usersrating, $ratingoptions->settings->scale->max);
+            } else {
+                $ratingoptions->itemid = $item->{$itemidcol};
+                $ratingoptions->scaleid = null;
+                $ratingoptions->userid = null;
+                $ratingoptions->id = null;
+                $ratingoptions->aggregate = null;
+                $ratingoptions->count = 0;
+                $ratingoptions->rating =  null;
+            }
 
-                $scalemax = count($scaleobj->scaleitems);
+            $rating = new rating($ratingoptions);
+            $rating->itemtimecreated = $this->get_item_time_created($item);
+            if (!empty($item->{$itemuseridcol})) {
+                $rating->itemuserid = $item->{$itemuseridcol};
             }
+            $item->rating = $rating;
         }
-        else { //its numeric
-            $scaleobj->scaleitems = $options->scaleid;
-            $scaleobj->id = $options->scaleid;
-            $scaleobj->name = null;
 
-            $scalemax = $options->scaleid;
+        return $options->items;
+    }
+
+    /**
+     * Generates a rating settings object based upon the options it is provided.
+     *
+     * @param stdClass $options {
+     *      context           => context the context in which the ratings exists [required]
+     *      component         => string The component the items belong to [required]
+     *      ratingarea        => string The ratingarea the items belong to [required]
+     *      aggregate         => int what aggregation method should be applied. RATING_AGGREGATE_AVERAGE, RATING_AGGREGATE_MAXIMUM etc [required]
+     *      scaleid           => int the scale from which the user can select a rating [required]
+     *      returnurl         => string the url to return the user to after submitting a rating. Can be left null for ajax requests [optional]
+     *      assesstimestart   => int only allow rating of items created after this timestamp [optional]
+     *      assesstimefinish  => int only allow rating of items created before this timestamp [optional]
+     *      plugintype        => string plugin type ie 'mod' Used to find the permissions callback [optional]
+     *      pluginname        => string plugin name ie 'forum' Used to find the permissions callback [optional]
+     * }
+     * @return stdClass
+     */
+    protected function generate_rating_settings_object($options) {
+
+        if (!isset($options->context)) {
+            throw new coding_exception('The context option is a required option when generating a rating settings object.');
+        }
+        if (!isset($options->component)) {
+            throw new coding_exception('The component option is now a required option when generating a rating settings object.');
+        }
+        if (!isset($options->ratingarea)) {
+            throw new coding_exception('The ratingarea option is now a required option when generating a rating settings object.');
+        }
+        if (!isset($options->aggregate)) {
+            throw new coding_exception('The aggregate option is now a required option when generating a rating settings object.');
+        }
+        if (!isset($options->scaleid)) {
+            throw new coding_exception('The scaleid option is now a required option when generating a rating settings object.');
         }
 
-        //should $settings and $settings->permissions be declared as proper classes?
-        $settings = new stdclass(); //settings that are common to all ratings objects in this context
-        $settings->component =$options->component;
-        $settings->scale = $scaleobj; //the scale to use now
+        // settings that are common to all ratings objects in this context
+        $settings = new stdClass;
+        $settings->scale             = $this->generate_rating_scale_object($options->scaleid); // the scale to use now
         $settings->aggregationmethod = $options->aggregate;
-        if( !empty($options->returnurl) ) {
-            $settings->returnurl = $options->returnurl;
-        }
+        $settings->assesstimestart   = null;
+        $settings->assesstimefinish  = null;
 
-        $settings->assesstimestart = $settings->assesstimefinish = null;
-        if( !empty($options->assesstimestart) ) {
+        // Collect options into the settings object
+        if (!empty($options->assesstimestart)) {
             $settings->assesstimestart = $options->assesstimestart;
         }
-        if( !empty($options->assesstimefinish) ) {
+        if (!empty($options->assesstimefinish)) {
             $settings->assesstimefinish = $options->assesstimefinish;
         }
+        if (!empty($options->returnurl)) {
+            $settings->returnurl = $options->returnurl;
+        }
 
-        //check site capabilities
-        $settings->permissions = new stdclass();
-        $settings->permissions->view = has_capability('moodle/rating:view',$options->context);//can view the aggregate of ratings of their own items
-        $settings->permissions->viewany = has_capability('moodle/rating:viewany',$options->context);//can view the aggregate of ratings of other people's items
-        $settings->permissions->viewall = has_capability('moodle/rating:viewall',$options->context);//can view individual ratings
-        $settings->permissions->rate = has_capability('moodle/rating:rate',$options->context);//can submit ratings
-
-        //check module capabilities (mostly for backwards compatability with old modules that previously implemented their own ratings)
-        $pluginpermissionsarray = $this->get_plugin_permissions_array($options->context->id, $options->component);
-
-        $settings->pluginpermissions = new stdclass();
-        $settings->pluginpermissions->view = $pluginpermissionsarray['view'];
+        // check site capabilities
+        $settings->permissions = new stdClass;
+        $settings->permissions->view    = has_capability('moodle/rating:view', $options->context); // can view the aggregate of ratings of their own items
+        $settings->permissions->viewany = has_capability('moodle/rating:viewany', $options->context); // can view the aggregate of ratings of other people's items
+        $settings->permissions->viewall = has_capability('moodle/rating:viewall', $options->context); // can view individual ratings
+        $settings->permissions->rate    = has_capability('moodle/rating:rate', $options->context); // can submit ratings
+
+        // check module capabilities (mostly for backwards compatability with old modules that previously implemented their own ratings)
+        $pluginpermissionsarray = $this->get_plugin_permissions_array($options->context->id, $options->component, $options->ratingarea);
+        $settings->pluginpermissions = new stdClass;
+        $settings->pluginpermissions->view    = $pluginpermissionsarray['view'];
         $settings->pluginpermissions->viewany = $pluginpermissionsarray['viewany'];
         $settings->pluginpermissions->viewall = $pluginpermissionsarray['viewall'];
-        $settings->pluginpermissions->rate = $pluginpermissionsarray['rate'];
+        $settings->pluginpermissions->rate    = $pluginpermissionsarray['rate'];
 
-        $rating = null;
-        $ratingoptions = new stdclass();
-        $ratingoptions->context = $options->context;//context is common to all ratings in the set
-        $ratingoptions->component = $options->component;
-        foreach($options->items as $item) {
-            $rating = null;
-            //match the item with its corresponding rating
-            foreach($ratingsrecords as $rec) {
-                if( $item->id==$rec->itemid ) {
-                    //Note: rec->scaleid = the id of scale at the time the rating was submitted
-                    //may be different from the current scale id
-                    $ratingoptions->itemid = $item->id;
-                    $ratingoptions->scaleid = $rec->scaleid;
-                    $ratingoptions->userid = $rec->userid;
-
-                    $rating = new rating($ratingoptions);
-                    $rating->id         = $rec->id;    //unset($rec->id);
-                    $rating->aggregate  = $rec->aggrrating; //unset($rec->aggrrating);
-                    $rating->count      = $rec->numratings; //unset($rec->numratings);
-                    $rating->rating     = $rec->usersrating; //unset($rec->usersrating);
-                    $rating->itemtimecreated = $this->get_item_time_created($item);
-
-                    break;
-                }
-            }
-            //if there are no ratings for this item
-            if( !$rating ) {
-                $ratingoptions->itemid = $item->id;
-                $ratingoptions->scaleid = null;
-                $ratingoptions->userid = null;
-
-                $rating = new rating($ratingoptions);
-                $rating->id         = null;
-                $rating->aggregate  = null;
-                $rating->count      = 0;
-                $rating->rating     = null;
-
-                $rating->itemid     = $item->id;
-                $rating->userid     = null;
-                $rating->scaleid     = null;
-                $rating->itemtimecreated = $this->get_item_time_created($item);
-            }
+        return $settings;
+    }
 
-            if( !empty($item->userid) ) {
-                $rating->itemuserid = $item->userid;
+    /**
+     * Generates a scale object that can be returned
+     *
+     * @global moodle_database $DB
+     * @param type $scaleid
+     * @return stdClass
+     */
+    protected function generate_rating_scale_object($scaleid) {
+        global $DB;
+        if (!array_key_exists('s'.$scaleid, $this->scales)) {
+            $scale = new stdClass;
+            $scale->id = $scaleid;
+            $scale->name = null;
+            $scale->courseid = null;
+            $scale->scaleitems = array();
+            $scale->isnumeric = true;
+            $scale->max = $scaleid;
+
+            if ($scaleid < 0) {
+                // It is a proper scale (not numeric)
+                $scalerecord = $DB->get_record('scale', array('id' => abs($scaleid)));
+                if ($scalerecord) {
+                    // We need to generate an array with string keys starting at 1
+                    $scalearray = explode(',', $scalerecord->scale);
+                    $c = count($scalearray);
+                    for ($i = 0; $i < $c; $i++) {
+                        // treat index as a string to allow sorting without changing the value
+                        $scale->scaleitems[(string)($i + 1)] = $scalearray[$i];
+                    }
+                    krsort($scale->scaleitems); // have the highest grade scale item appear first
+                    $scale->isnumeric = false;
+                    $scale->name = $scalerecord->name;
+                    $scale->courseid = $scalerecord->courseid;
+                    $scale->max = count($scale->scaleitems);
+                }
             } else {
-                $rating->itemuserid = null;
-            }
-            $rating->settings = $settings;
-            $item->rating = $rating;
-
-            //Below is a nasty hack presumably here to handle scales being changed (out of 10 to out of 5 for example)
-            //
-            // it could throw off the grading if count and sum returned a grade higher than scale
-            // so to prevent it we review the results and ensure that grade does not exceed the scale, if it does we set grade = scale (i.e. full credit)
-            if ($rating->rating > $scalemax) {
-                $rating->rating = $scalemax;
-            }
-            if ($rating->aggregate > $scalemax) {
-                $rating->aggregate = $scalemax;
+                //generate an array of values for numeric scales
+                for($i = 0; $i <= (int)$scaleid; $i++) {
+                    $scale->scaleitems[(string)$i] = $i;
+                }
             }
+            $this->scales['s'.$scaleid] = $scale;
         }
-
-        return $options->items;
+        return $this->scales['s'.$scaleid];
     }
 
-    private function get_item_time_created($item) {
+    /**
+     * Gets the time the given item was created
+     *
+     * TODO: Find a better solution for this, its not ideal to test for fields really we should be
+     * asking the component the item belongs to what field to look for or even the value we
+     * are looking for.
+     *
+     * @param stdClass $item
+     * @return mixed
+     */
+    protected function get_item_time_created($item) {
         if( !empty($item->created) ) {
             return $item->created;//the forum_posts table has created instead of timecreated
         }
@@ -453,26 +762,35 @@ class rating_manager {
     }
 
     /**
-    * Returns an array of grades calculated by aggregating item ratings.
-    * @param object $options {
-    *            userid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
-    *            aggregationmethod => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [required]
-    *            scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
-    *            itemtable => int the table containing the items [required]
-    *            itemtableusercolum => int the column of the user table containing the item owner's user id [required]
-    *
-    *            contextid => int the context in which the rated items exist [optional]
-    *
-    *            modulename => string the name of the module [optional]
-    *            moduleid => int the id of the module instance [optional]
-    *
-    * @return array the array of the user's grades
-    */
+     * Returns an array of grades calculated by aggregating item ratings.
+     * @param object $options {
+     *            userid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
+     *            aggregationmethod => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [required]
+     *            scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
+     *            itemtable => int the table containing the items [required]
+     *            itemtableusercolum => int the column of the user table containing the item owner's user id [required]
+     *            component => The component for the ratings [required]
+     *            ratingarea => The ratingarea for the ratings [required]
+     *
+     *            contextid => int the context in which the rated items exist [optional]
+     *
+     *            modulename => string the name of the module [optional]
+     *            moduleid => int the id of the module instance [optional]
+     *
+     * @return array the array of the user's grades
+     */
     public function get_user_grades($options) {
         global $DB;
 
         $contextid = null;
 
+        if (!isset($options->component)) {
+            throw new coding_exception('The component option is now a required option when getting user grades from ratings.');
+        }
+        if (!isset($options->ratingarea)) {
+            throw new coding_exception('The ratingarea option is now a required option when getting user grades from ratings.');
+        }
+
         //if the calling code doesn't supply a context id we'll have to figure it out
         if( !empty($options->contextid) ) {
             $contextid = $options->contextid;
@@ -489,24 +807,27 @@ class rating_manager {
             //going direct to the db for the context id seems wrong
             list($ctxselect, $ctxjoin) = context_instance_preload_sql('cm.id', CONTEXT_MODULE, 'ctx');
             $sql = "SELECT cm.* $ctxselect
-            FROM {course_modules} cm
-            LEFT JOIN {modules} mo ON mo.id = cm.module
-            LEFT JOIN {{$modulename}} m ON m.id = cm.instance $ctxjoin
-            WHERE mo.name=:modulename AND m.id=:moduleid";
+                      FROM {course_modules} cm
+                 LEFT JOIN {modules} mo ON mo.id = cm.module
+                 LEFT JOIN {{$modulename}} m ON m.id = cm.instance $ctxjoin
+                     WHERE mo.name=:modulename AND
+                           m.id=:moduleid";
             $contextrecord = $DB->get_record_sql($sql, array('modulename'=>$modulename, 'moduleid'=>$moduleid), '*', MUST_EXIST);
             $contextid = $contextrecord->ctxid;
         }
 
         $params = array();
-        $params['contextid']= $contextid;
-        $itemtable          = $options->itemtable;
-        $itemtableusercolumn= $options->itemtableusercolumn;
-        $scaleid            = $options->scaleid;
-        $aggregationstring = $this->get_aggregation_method($options->aggregationmethod);
+        $params['contextid']  = $contextid;
+        $params['component']  = $options->component;
+        $params['ratingarea'] = $options->ratingarea;
+        $itemtable            = $options->itemtable;
+        $itemtableusercolumn  = $options->itemtableusercolumn;
+        $scaleid              = $options->scaleid;
+        $aggregationstring    = $this->get_aggregation_method($options->aggregationmethod);
 
         //if userid is not 0 we only want the grade for a single user
         $singleuserwhere = '';
-        if ($options->userid!=0) {
+        if ($options->userid != 0) {
             $params['userid1'] = intval($options->userid);
             $singleuserwhere = "AND i.{$itemtableusercolumn} = :userid1";
         }
@@ -515,13 +836,14 @@ class rating_manager {
         //r.contextid will be null for users who haven't been rated yet
         //no longer including users who haven't been rated to reduce memory requirements
         $sql = "SELECT u.id as id, u.id AS userid, $aggregationstring(r.rating) AS rawgrade
-                FROM {user} u
-                LEFT JOIN {{$itemtable}} i ON u.id=i.{$itemtableusercolumn}
-                LEFT JOIN {rating} r ON r.itemid=i.id
-                WHERE r.contextid=:contextid
-                $singleuserwhere
-                GROUP BY u.id";
-
+                  FROM {user} u
+             LEFT JOIN {{$itemtable}} i ON u.id=i.{$itemtableusercolumn}
+             LEFT JOIN {rating} r ON r.itemid=i.id
+                 WHERE r.contextid = :contextid AND
+                       r.component = :component AND
+                       r.ratingarea = :ratingarea
+                       $singleuserwhere
+              GROUP BY u.id";
         $results = $DB->get_records_sql($sql, $params);
 
         if ($results) {
@@ -568,19 +890,19 @@ class rating_manager {
      * @return array
      */
     public function get_aggregate_types() {
-        return array (RATING_AGGREGATE_NONE  => get_string('aggregatenone', 'rating'),
-                      RATING_AGGREGATE_AVERAGE   => get_string('aggregateavg', 'rating'),
-                      RATING_AGGREGATE_COUNT => get_string('aggregatecount', 'rating'),
-                      RATING_AGGREGATE_MAXIMUM   => get_string('aggregatemax', 'rating'),
-                      RATING_AGGREGATE_MINIMUM   => get_string('aggregatemin', 'rating'),
-                      RATING_AGGREGATE_SUM   => get_string('aggregatesum', 'rating'));
+        return array (RATING_AGGREGATE_NONE     => get_string('aggregatenone', 'rating'),
+                      RATING_AGGREGATE_AVERAGE  => get_string('aggregateavg', 'rating'),
+                      RATING_AGGREGATE_COUNT    => get_string('aggregatecount', 'rating'),
+                      RATING_AGGREGATE_MAXIMUM  => get_string('aggregatemax', 'rating'),
+                      RATING_AGGREGATE_MINIMUM  => get_string('aggregatemin', 'rating'),
+                      RATING_AGGREGATE_SUM      => get_string('aggregatesum', 'rating'));
     }
 
     /**
-    * Converts an aggregation method constant into something that can be included in SQL
-    * @param int $aggregate An aggregation constant. For example, RATING_AGGREGATE_AVERAGE.
-    * @return string an SQL aggregation method
-    */
+     * Converts an aggregation method constant into something that can be included in SQL
+     * @param int $aggregate An aggregation constant. For example, RATING_AGGREGATE_AVERAGE.
+     * @return string an SQL aggregation method
+     */
     public function get_aggregation_method($aggregate) {
         $aggregatestr = null;
         switch($aggregate){
@@ -607,17 +929,18 @@ class rating_manager {
     }
 
     /**
-    * Looks for a callback like forum_rating_permissions() to retrieve permissions from the plugin whose items are being rated
-    * @param int $contextid The current context id
-    * @param string component the name of the component that is using ratings ie 'mod_forum'
-    * @return array rating related permissions
-    */
-    public function get_plugin_permissions_array($contextid, $component=null) {
+     * Looks for a callback like forum_rating_permissions() to retrieve permissions from the plugin whose items are being rated
+     * @param int $contextid The current context id
+     * @param string component the name of the component that is using ratings ie 'mod_forum'
+     * @param string ratingarea The area the rating is associated with
+     * @return array rating related permissions
+     */
+    public function get_plugin_permissions_array($contextid, $component, $ratingarea) {
         $pluginpermissionsarray = null;
         $defaultpluginpermissions = array('rate'=>false,'view'=>false,'viewany'=>false,'viewall'=>false);//deny by default
         if (!empty($component)) {
             list($type, $name) = normalize_component($component);
-            $pluginpermissionsarray = plugin_callback($type, $name, 'rating', 'permissions', array($contextid), $defaultpluginpermissions);
+            $pluginpermissionsarray = plugin_callback($type, $name, 'rating', 'permissions', array($contextid, $component, $ratingarea), $defaultpluginpermissions);
         } else {
             $pluginpermissionsarray = $defaultpluginpermissions;
         }
@@ -628,28 +951,100 @@ class rating_manager {
      * Validates a submitted rating
      * @param array $params submitted data
      *            context => object the context in which the rated items exists [required]
-     *            itemid => int the ID of the object being rated
+     *            component => The component the rating belongs to [required]
+     *            ratingarea => The ratingarea the rating is associated with [required]
+     *            itemid => int the ID of the object being rated [required]
      *            scaleid => int the scale from which the user can select a rating. Used for bounds checking. [required]
      *            rating => int the submitted rating
      *            rateduserid => int the id of the user whose items have been rated. NOT the user who submitted the ratings. 0 to update all. [required]
      *            aggregation => int the aggregation method to apply when calculating grades ie RATING_AGGREGATE_AVERAGE [optional]
      * @return boolean true if the rating is valid. False if callback wasnt found and will throw rating_exception if rating is invalid
      */
-    public function check_rating_is_valid($component, $params) {
-        list($plugintype, $pluginname) = normalize_component($component);
+    public function check_rating_is_valid($params) {
+
+        if (!isset($params['context'])) {
+            throw new coding_exception('The context option is a required option when checking rating validity.');
+        }
+        if (!isset($params['component'])) {
+            throw new coding_exception('The component option is now a required option when checking rating validity');
+        }
+        if (!isset($params['ratingarea'])) {
+            throw new coding_exception('The ratingarea option is now a required option when checking rating validity');
+        }
+        if (!isset($params['itemid'])) {
+            throw new coding_exception('The itemid option is now a required option when checking rating validity');
+        }
+        if (!isset($params['scaleid'])) {
+            throw new coding_exception('The scaleid option is now a required option when checking rating validity');
+        }
+        if (!isset($params['rateduserid'])) {
+            throw new coding_exception('The rateduserid option is now a required option when checking rating validity');
+        }
 
-        //this looks for a function like forum_rating_is_valid() in mod_forum lib.php
+        list($plugintype, $pluginname) = normalize_component($params['component']);
+
+        //this looks for a function like forum_rating_validate() in mod_forum lib.php
         //wrapping the params array in another array as call_user_func_array() expands arrays into multiple arguments
         $isvalid = plugin_callback($plugintype, $pluginname, 'rating', 'validate', array($params), null);
 
         //if null then the callback doesn't exist
         if ($isvalid === null) {
             $isvalid = false;
-            debugging('plugin rating_add() function not found');
+            debugging('rating validation callback not found for component '.  clean_param($component, PARAM_ALPHANUMEXT));
         }
-
         return $isvalid;
     }
+
+    /**
+     * Initialises JavaScript to enable AJAX ratings on the provided page
+     *
+     * @param moodle_page $page
+     * @return true
+     */
+    public function initialise_rating_javascript(moodle_page $page) {
+        global $CFG;
+
+        if ($this->javascriptinitialised) {
+            return true;
+        }
+
+        if (!empty($CFG->enableajax)) {
+            $page->requires->js_init_call('M.core_rating.init');
+        }
+
+        $this->javascriptinitialised = true;
+        return true;
+    }
+
+    /**
+     * Returns a string that describes the aggregation method that was provided.
+     *
+     * @param string $aggregationmethod
+     * @return string
+     */
+    public function get_aggregate_label($aggregationmethod) {
+        $aggregatelabel = '';
+        switch ($aggregationmethod) {
+            case RATING_AGGREGATE_AVERAGE :
+                $aggregatelabel .= get_string("aggregateavg", "rating");
+                break;
+            case RATING_AGGREGATE_COUNT :
+                $aggregatelabel .= get_string("aggregatecount", "rating");
+                break;
+            case RATING_AGGREGATE_MAXIMUM :
+                $aggregatelabel .= get_string("aggregatemax", "rating");
+                break;
+            case RATING_AGGREGATE_MINIMUM :
+                $aggregatelabel .= get_string("aggregatemin", "rating");
+                break;
+            case RATING_AGGREGATE_SUM :
+                $aggregatelabel .= get_string("aggregatesum", "rating");
+                break;
+        }
+        $aggregatelabel .= get_string('labelsep', 'langconfig');
+        return $aggregatelabel;
+    }
+
 }//end rating_manager class definition
 
 class rating_exception extends moodle_exception {
index f9b881a..ad6f66c 100644 (file)
  */
 
 require_once('../config.php');
-require_once('lib.php');
-
-$contextid = required_param('contextid', PARAM_INT);
-$component = required_param('component', PARAM_ALPHAEXT);
-$itemid = required_param('itemid', PARAM_INT);
-$scaleid = required_param('scaleid', PARAM_INT);
-$userrating = required_param('rating', PARAM_INT);
+require_once($CFG->dirroot.'/rating/lib.php');
+
+$contextid   = required_param('contextid', PARAM_INT);
+$component   = required_param('component', PARAM_ALPHAEXT);
+$ratingarea  = required_param('ratingarea', PARAM_ALPHANUMEXT);
+$itemid      = required_param('itemid', PARAM_INT);
+$scaleid     = required_param('scaleid', PARAM_INT);
+$userrating  = required_param('rating', PARAM_INT);
 $rateduserid = required_param('rateduserid', PARAM_INT);//which user is being rated. Required to update their grade
-$returnurl = required_param('returnurl', PARAM_LOCALURL);//required for non-ajax requests
+$returnurl   = required_param('returnurl', PARAM_LOCALURL);//required for non-ajax requests
 
 $result = new stdClass;
 
@@ -44,7 +45,7 @@ require_login($course, false, $cm);
 
 $contextid = null;//now we have a context object throw away the id from the user
 $PAGE->set_context($context);
-$PAGE->set_url('/rating/rate.php', array('contextid'=>$context->id));
+$PAGE->set_url('/rating/rate.php', array('contextid' => $context->id));
 
 if (!confirm_sesskey() || !has_capability('moodle/rating:rate',$context)) {
     echo $OUTPUT->header();
@@ -57,7 +58,7 @@ $rm = new rating_manager();
 
 //check the module rating permissions
 //doing this check here rather than within rating_manager::get_ratings() so we can return a json error response
-$pluginpermissionsarray = $rm->get_plugin_permissions_array($context->id, $component);
+$pluginpermissionsarray = $rm->get_plugin_permissions_array($context->id, $component, $ratingarea);
 
 if (!$pluginpermissionsarray['rate']) {
     $result->error = get_string('ratepermissiondenied', 'rating');
@@ -65,13 +66,15 @@ if (!$pluginpermissionsarray['rate']) {
     die();
 } else {
     $params = array(
-        'context' => $context,
-        'itemid' => $itemid,
-        'scaleid' => $scaleid,
-        'rating' => $userrating,
-        'rateduserid' => $rateduserid);
-
-    if (!$rm->check_rating_is_valid($component, $params)) {
+        'context'     => $context,
+        'component'   => $component,
+        'ratingarea'  => $ratingarea,
+        'itemid'      => $itemid,
+        'scaleid'     => $scaleid,
+        'rating'      => $userrating,
+        'rateduserid' => $rateduserid
+    );
+    if (!$rm->check_rating_is_valid($params)) {
         echo $OUTPUT->header();
         echo get_string('ratinginvalid', 'rating');
         echo $OUTPUT->footer();
@@ -80,9 +83,10 @@ if (!$pluginpermissionsarray['rate']) {
 }
 
 if ($userrating != RATING_UNSET_RATING) {
-    $ratingoptions = new stdClass();
+    $ratingoptions = new stdClass;
     $ratingoptions->context = $context;
     $ratingoptions->component = $component;
+    $ratingoptions->ratingarea = $ratingarea;
     $ratingoptions->itemid  = $itemid;
     $ratingoptions->scaleid = $scaleid;
     $ratingoptions->userid  = $USER->id;
@@ -90,9 +94,10 @@ if ($userrating != RATING_UNSET_RATING) {
     $rating = new rating($ratingoptions);
     $rating->update_rating($userrating);
 } else { //delete the rating if the user set to Rate...
-    $options = new stdClass();
+    $options = new stdClass;
     $options->contextid = $context->id;
     $options->component = $component;
+    $options->ratingarea = $ratingarea;
     $options->userid = $USER->id;
     $options->itemid = $itemid;
 
@@ -101,15 +106,13 @@ if ($userrating != RATING_UNSET_RATING) {
 
 //todo add a setting to turn grade updating off for those who don't want them in gradebook
 //note that this needs to be done in both rate.php and rate_ajax.php
-if(true){
+if (!empty($cm) && $context->contextlevel == CONTEXT_MODULE) {
     //tell the module that its grades have changed
-    if ( !$modinstance = $DB->get_record($cm->modname, array('id' => $cm->instance)) ) {
-        print_error('invalidid');
-    }
+    $modinstance = $DB->get_record($cm->modname, array('id' => $cm->instance), '*', MUST_EXIST);
     $modinstance->cmidnumber = $cm->id; //MDL-12961
     $functionname = $cm->modname.'_update_grades';
     require_once($CFG->dirroot."/mod/{$cm->modname}/lib.php");
-    if(function_exists($functionname)) {
+    if (function_exists($functionname)) {
         $functionname($modinstance, $rateduserid);
     }
 }
index 3a26223..c3e5bf8 100644 (file)
 define('AJAX_SCRIPT', true);
 
 require_once('../config.php');
-require_once('lib.php');
-
-$contextid = required_param('contextid', PARAM_INT);
-$component = required_param('component', PARAM_ALPHAEXT);
-$itemid = required_param('itemid', PARAM_INT);
-$scaleid = required_param('scaleid', PARAM_INT);
-$userrating = required_param('rating', PARAM_INT);
-$rateduserid = required_param('rateduserid', PARAM_INT);//which user is being rated. Required to update their grade
+require_once($CFG->dirroot.'/rating/lib.php');
+
+$contextid         = required_param('contextid', PARAM_INT);
+$component         = required_param('component', PARAM_ALPHAEXT);
+$ratingarea        = required_param('ratingarea', PARAM_ALPHANUMEXT);
+$itemid            = required_param('itemid', PARAM_INT);
+$scaleid           = required_param('scaleid', PARAM_INT);
+$userrating        = required_param('rating', PARAM_INT);
+$rateduserid       = required_param('rateduserid', PARAM_INT);//which user is being rated. Required to update their grade
 $aggregationmethod = optional_param('aggregation', RATING_AGGREGATE_NONE, PARAM_INT);//we're going to calculate the aggregate and return it to the client
 
 $result = new stdClass;
@@ -66,7 +67,7 @@ $rm = new rating_manager();
 
 //check the module rating permissions
 //doing this check here rather than within rating_manager::get_ratings() so we can return a json error response
-$pluginpermissionsarray = $rm->get_plugin_permissions_array($context->id, $component);
+$pluginpermissionsarray = $rm->get_plugin_permissions_array($context->id, $component, $ratingarea);
 
 if (!$pluginpermissionsarray['rate']) {
     $result->error = get_string('ratepermissiondenied', 'rating');
@@ -74,14 +75,16 @@ if (!$pluginpermissionsarray['rate']) {
     die();
 } else {
     $params = array(
-        'context' => $context,
-        'itemid' => $itemid,
-        'scaleid' => $scaleid,
-        'rating' => $userrating,
+        'context'     => $context,
+        'component'   => $component,
+        'ratingarea'  => $ratingarea,
+        'itemid'      => $itemid,
+        'scaleid'     => $scaleid,
+        'rating'      => $userrating,
         'rateduserid' => $rateduserid,
-        'aggregation' => $aggregationmethod);
-
-    if (!$rm->check_rating_is_valid($component, $params)) {
+        'aggregation' => $aggregationmethod
+    );
+    if (!$rm->check_rating_is_valid($params)) {
         $result->error = get_string('ratinginvalid', 'rating');
         echo json_encode($result);
         die();
@@ -89,8 +92,9 @@ if (!$pluginpermissionsarray['rate']) {
 }
 
 //rating options used to update the rating then retrieve the aggregate
-$ratingoptions = new stdClass();
+$ratingoptions = new stdClass;
 $ratingoptions->context = $context;
+$ratingoptions->ratingarea = $ratingarea;
 $ratingoptions->component = $component;
 $ratingoptions->itemid  = $itemid;
 $ratingoptions->scaleid = $scaleid;
@@ -100,56 +104,57 @@ if ($userrating != RATING_UNSET_RATING) {
     $rating = new rating($ratingoptions);
     $rating->update_rating($userrating);
 } else { //delete the rating if the user set to Rate...
-    $options = new stdClass();
+    $options = new stdClass;
     $options->contextid = $context->id;
     $options->component = $component;
+    $options->ratingarea = $ratingarea;
     $options->userid = $USER->id;
     $options->itemid = $itemid;
 
     $rm->delete_ratings($options);
 }
 
-//Future possible enhancement: add a setting to turn grade updating off for those who don't want them in gradebook
-//note that this would need to be done in both rate.php and rate_ajax.php
-    if ($context->contextlevel==CONTEXT_MODULE) {
-        //tell the module that its grades have changed
-        if ( $modinstance = $DB->get_record($cm->modname, array('id' => $cm->instance)) ) {
-            $modinstance->cmidnumber = $cm->id; //MDL-12961
-            $functionname = $cm->modname.'_update_grades';
-            require_once("../mod/{$cm->modname}/lib.php");
-            if(function_exists($functionname)) {
-                $functionname($modinstance, $rateduserid);
-            }
+// Future possible enhancement: add a setting to turn grade updating off for those who don't want them in gradebook
+// note that this would need to be done in both rate.php and rate_ajax.php
+if ($context->contextlevel == CONTEXT_MODULE) {
+    //tell the module that its grades have changed
+    $modinstance = $DB->get_record($cm->modname, array('id' => $cm->instance));
+    if ($modinstance) {
+        $modinstance->cmidnumber = $cm->id; //MDL-12961
+        $functionname = $cm->modname.'_update_grades';
+        require_once($CFG->dirroot."/mod/{$cm->modname}/lib.php");
+        if (function_exists($functionname)) {
+            $functionname($modinstance, $rateduserid);
         }
     }
+}
 
 //object to return to client as json
-$result = new stdClass;
 $result->success = true;
 
 //need to retrieve the updated item to get its new aggregate value
-$item = new stdclass();
+$item = new stdClass;
 $item->id = $itemid;
-$items = array($item);
 
 //most of $ratingoptions variables were previously set
-$ratingoptions->items = $items;
+$ratingoptions->items = array($item);
 $ratingoptions->aggregate = $aggregationmethod;
 
 $items = $rm->get_ratings($ratingoptions);
+$firstrating = $items[0]->rating;
 
 //for custom scales return text not the value
 //this scales weirdness will go away when scales are refactored
 $scalearray = null;
-$aggregatetoreturn = round($items[0]->rating->aggregate,1);
+$aggregatetoreturn = round($firstrating->aggregate, 1);
 
 // Output a dash if aggregation method == COUNT as the count is output next to the aggregate anyway
-if ($items[0]->rating->settings->aggregationmethod==RATING_AGGREGATE_COUNT or $items[0]->rating->count == 0) {
+if ($firstrating->settings->aggregationmethod == RATING_AGGREGATE_COUNT or $firstrating->count == 0) {
     $aggregatetoreturn = ' - ';
-} else if($items[0]->rating->settings->scale->id < 0) { //if its non-numeric scale
+} else if ($firstrating->settings->scale->id < 0) { //if its non-numeric scale
     //dont use the scale item if the aggregation method is sum as adding items from a custom scale makes no sense
-    if ($items[0]->rating->settings->aggregationmethod!= RATING_AGGREGATE_SUM) {
-        $scalerecord = $DB->get_record('scale', array('id' => -$items[0]->rating->settings->scale->id));
+    if ($firstrating->settings->aggregationmethod != RATING_AGGREGATE_SUM) {
+        $scalerecord = $DB->get_record('scale', array('id' => -$firstrating->settings->scale->id));
         if ($scalerecord) {
             $scalearray = explode(',', $scalerecord->scale);
             $aggregatetoreturn = $scalearray[$aggregatetoreturn-1];
@@ -158,17 +163,9 @@ if ($items[0]->rating->settings->aggregationmethod==RATING_AGGREGATE_COUNT or $i
 }
 
 //See if the user has permission to see the rating aggregate
-//we could do this check as "if $userid==$rateduserid" but going to the database to determine item owner id seems more secure
-//if we accept the item owner user id from the http request a user could alter the URL and erroneously get access to the rating aggregate
-
-//if its their own item and they have view permission
-if (($USER->id==$items[0]->rating->itemuserid && has_capability('moodle/rating:view',$context)
-        && (empty($pluginpermissionsarray) or $pluginpermissionsarray['view']))
-    //or if its not their item or if no user created the item (the hub did) and they have viewany permission
-    || (($USER->id!=$items[0]->rating->itemuserid or empty($items[0]->rating->itemuserid)) && has_capability('moodle/rating:viewany',$context)
-        && (empty($pluginpermissionsarray) or $pluginpermissionsarray['viewany']))) {
+if ($firstrating->user_can_view_aggregate()) {
     $result->aggregate = $aggregatetoreturn;
-    $result->count = $items[0]->rating->count;
+    $result->count = $firstrating->count;
     $result->itemid = $itemid;
 }
 
index 2be2bd9..bc677cf 100644 (file)
@@ -779,3 +779,8 @@ sup {vertical-align: super;}
 .dir-rtl .o2k7Skin *, 
 .dir-rtl .o2k7Skin .mceText,
 .dir-rtl .o2k7Skin .mceListBox .mceText {text-align:right;}
+
+.path-rating .ratingtable {width:100%;margin-bottom:1em;}
+.path-rating .ratingtable th.rating {width:100%;}
+.path-rating .ratingtable td.rating,
+.path-rating .ratingtable td.time {white-space:nowrap; text-align:center;}
\ No newline at end of file
index 89dfcb5..c31cf4b 100644 (file)
@@ -30,7 +30,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2011051800.00;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2011052300.02;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes