";
+ echo " ";
+}
+
+echo $OUTPUT->close_window_button();
+echo $OUTPUT->footer();
diff --git a/rating/module.js b/rating/module.js
new file mode 100644
index 00000000000..e627358ee91
--- /dev/null
+++ b/rating/module.js
@@ -0,0 +1,58 @@
+M.core_ratings={
+
+ Y : null,
+ transaction : [],
+
+ init : function(Y){
+ this.Y = Y;
+ Y.all('select.postratingmenu').each(this.attach_rating_events, this);
+
+ //hide the submit buttons
+ this.Y.all('input.postratingmenusubmit').setStyle('display', 'none');
+ },
+
+ attach_rating_events : function(selectnode) {
+ selectnode.on('change', this.submit_rating, this, selectnode);
+ },
+
+ submit_rating : function(e, selectnode){
+ var theinputs = selectnode.ancestor('form').all('.ratinginput')
+ var thedata = [];
+
+ var inputssize = theinputs.size();
+ for ( var i=0; i.
+
+/**
+ * This page receives rating submissions
+ *
+ * This page can be the target for either ajax or non-ajax rating submissions.
+ * If a return url is supplied the request is presumed to be a non-ajax request so a page
+ * is returned.
+ * If there is no return url the request is presumed to be ajax so a json response is returned.
+ *
+ * @package moodlecore
+ * @copyright 2010 Andrew Davis
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+
+$contextid = required_param('contextid', PARAM_INT);
+$itemid = required_param('itemid', PARAM_INT);
+$scaleid = required_param('scaleid', PARAM_INT);
+$userrating = required_param('rating'.$itemid, PARAM_INT);
+$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);//will only be supplied for non-ajax requests
+
+require_once('ratinglib.php');
+
+$result = new stdClass;
+
+if( !isloggedin() ){ //session has expired
+ $result->error = get_string('sessionexpired', 'ratings');
+ echo json_encode($result);
+ die();
+}
+
+$context = get_context_instance_by_id($contextid);
+
+$permissions = rating::get_rating_permissions($context);
+if( !$permissions[RATING_POST] ) {
+ //check if its a non-ajax request
+ if( $returnurl ) {
+ echo $OUTPUT->header();
+ echo get_string('ratepermissiondenied', 'ratings');
+ echo $OUTPUT->footer();
+ }
+ else {
+ $result->error = get_string('ratepermissiondenied', 'ratings');
+ echo json_encode($result);
+ }
+ die();
+}
+
+//todo andrew deny access to guest user. Petr to define "guest"
+
+$userid = $USER->id;
+
+$PAGE->set_url('/lib/rate.php', array(
+ 'contextid'=>$contextid,
+ 'itemid'=>$itemid,
+ 'scaleid'=>$scaleid,
+ 'rating'=>$userrating,
+ 'userid'=>$userid,
+ 'returnurl'=>$returnurl,
+ ));
+
+if( $returnurl ) {
+ //
+}
+
+//todo how can we validate the forum post,glossary entry or whatever id?
+//how do we know where to look for the item? how we we work from module to forum_posts, glossary_entries etc?
+//if ($rating_context->contextlevel == CONTEXT_COURSE) {
+// $courseid = $rating_context->instanceid;
+// $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+//if ($rating_context->contextlevel == CONTEXT_MODULE) {
+// $cm = get_coursemodule_from_id(false, $rating_context->instanceid, 0, false, MUST_EXIST);
+// $courseid = $cm->course;
+//}
+
+$rating = new Rating($context, $itemid, $scaleid, $userid);
+$rating->update_rating($userrating);
+
+//if its a non-ajax request
+if($returnurl) {
+ redirect($CFG->wwwroot.'/'.$returnurl);
+}
+else { //this is an ajax request
+ $result = new stdClass;
+ $result->success = true;
+ echo json_encode($result);
+ die();
+}
\ No newline at end of file
diff --git a/rating/ratinglib.php b/rating/ratinglib.php
new file mode 100644
index 00000000000..61680f5a725
--- /dev/null
+++ b/rating/ratinglib.php
@@ -0,0 +1,312 @@
+.
+
+/**
+ * A class representing a single rating and containing some static methods for manipulating ratings
+ *
+ * @package moodlecore
+ * @copyright 2010 Andrew Davis
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+ define('RATING_VIEW','view');
+ define('RATING_VIEW_ALL','viewall');
+ define('RATING_POST','post');
+
+define('RATING_UNSET_RATING', -999);
+
+//define ('RATING_AGGREGATE_NONE', 0); //no ratings
+define ('RATING_AGGREGATE_AVERAGE', 1);
+define ('RATING_AGGREGATE_COUNT', 2);
+define ('RATING_AGGREGATE_MAXIMUM', 3);
+define ('RATING_AGGREGATE_MINIMUM', 4);
+define ('RATING_AGGREGATE_SUM', 5);
+
+/**
+ * The rating class represents a single rating by a single user. It also contains a static method to retrieve sets of ratings.
+ *
+ * @copyright 2010 Andrew Davis
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @since Moodle 2.0
+ */
+class rating implements renderable {
+
+/**
+* Constructor.
+* @param $contextid the current context
+* @param $itemid the id of the associated item (forum post, glossary item etc)
+* @param $scaleid the scale to use
+* @param $userid the user submitting the rating
+*/
+function __construct($context, $itemid, $scaleid, $userid) {
+ $this->context = $context;
+ $this->itemid = $itemid;
+ $this->scaleid = $scaleid;
+ $this->userid = $userid;
+}
+
+/**
+* Update this rating in the database
+* @param integer $rating the integer value of this rating
+*/
+function update_rating($rating) {
+ global $DB;
+
+ $data = new stdclass();
+ $table = 'ratings';
+
+ $item = new stdclass();
+ $item->id = $this->itemid;
+ $items = array($item);
+
+ $items = rating::load_ratings($this->context, $items, null, $this->scaleid, $this->userid);
+ if( !isset($items[0]->rating) || !isset($items[0]->rating->id) ) {
+ $data->contextid = $this->context->id;
+ $data->rating = $rating;
+ $data->scaleid = $this->scaleid;
+ $data->userid = $this->userid;
+ $data->itemid = $this->itemid;
+
+ $time = time();
+ $data->timecreated = $time;
+ $data->timemodified = $time;
+
+ $DB->insert_record($table, $data);
+ }
+ else {
+ //$data->id = $this->id;
+ $data->id = $items[0]->rating->id;
+ /*$data->contextid = $this->context->id;
+ $data->scaleid = $this->scaleid;
+ $data->userid = $this->userid;*/
+ $data->rating = $rating;
+
+ $time = time();
+ $data->timemodified = $time;
+
+ $DB->update_record($table, $data);
+ }
+}
+
+/**
+* Retreive the integer value of this rating
+*/
+function get_rating() {
+ return $this->rating;
+}
+
+/**
+* Remove this rating from the database
+*/
+function delete_rating() {
+ //todo implement this if its actually needed
+}
+
+/**
+* Static method that converts an aggregation method constant into something that can be included in SQL
+* @param $aggregate An aggregation constant. For example, RATING_AGGREGATE_AVERAGE.
+*/
+public static function rating_get_aggregation_method($aggregate) {
+ $aggregatestr = null;
+ switch($aggregate){
+ case RATING_AGGREGATE_AVERAGE:
+ $aggregatestr = 'AVG';
+ break;
+ case RATING_AGGREGATE_COUNT:
+ $aggregatestr = 'CNT';
+ break;
+ case RATING_AGGREGATE_MAXIMUM:
+ $aggregatestr = 'MAX';
+ break;
+ case RATING_AGGREGATE_MINIMUM:
+ $aggregatestr = 'MIN';
+ break;
+ case RATING_AGGREGATE_SUM:
+ $aggregatestr = 'SUM';
+ break;
+ }
+ return $aggregatestr;
+}
+
+/**
+* Static method that returns an array of ratings for a given item (forum post, glossary entry etc)
+ * This returns all users ratings for a single item
+* @param $context the context in which the rating exists
+* @param $itemid The id of the forum posts, glossary items or whatever
+*/
+public static function load_ratings_for_item($context, $itemid, $sort) {
+ global $DB;
+
+ $sql = "SELECT r.id, r.rating, r.itemid, r.userid, r.timemodified,
+u.firstname, u.lastname, u.imagealt, u.picture
+FROM {ratings} r
+LEFT JOIN {user} u ON r.userid = u.id
+WHERE
+ r.contextid = :contextid AND
+ r.itemid = :itemid
+$sort";
+
+ $params['contextid'] = $context->id;
+ $params['itemid'] = $itemid;
+
+ return $DB->get_records_sql($sql, $params);
+}
+
+/**
+* Static method that adds rating objects to an array of items (forum posts, glossary entries etc)
+* Rating objects are available at $item->rating
+* @param $contextid the current context
+* @param $items an array of items such as forum posts or glossary items. They must have an 'id' member ie $items[0]->id
+* @param $aggregate what aggregation method should be applied. AVG, MAX etc
+* @param $scaleid the scale from which the user can select a rating
+* @param $userid the id of the current user
+*/
+public static function load_ratings($context, $items, $aggregate=RATING_AGGREGATE_AVERAGE, $scaleid=5, $userid = null, $returnurl = null) {
+ global $DB, $USER, $PAGE, $CFG;
+
+ if(empty($items)) {
+ return $items;
+ }
+
+ if (is_null($userid)) {
+ $userid = $USER->id;
+ }
+
+ $aggregatestr = rating::rating_get_aggregation_method($aggregate);
+
+ //create an array of item ids
+ $itemids = array();
+ foreach($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');
+
+ $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 {ratings} r
+LEFT JOIN {ratings} 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
+ORDER BY r.itemid";
+
+ $params['userid'] = $userid;
+ $params['contextid'] = $context->id;
+
+ $ratingsrecords = $DB->get_records_sql($sql, $params);
+
+ //now create the rating sub objects
+ $permissions = rating::get_rating_permissions($context);
+
+ $scaleobj = new stdClass();
+ $scalemax = null;
+
+ //todo we could look for a scale id on each item to allow each item to use a different scale
+
+ if($scaleid < 0 ) { //if its a scale (not numeric)
+ $scalerecord = $DB->get_record('scale', array('id' => -$scaleid));
+ if ($scalerecord) {
+ $scaleobj->scaleitems = explode(',', $scalerecord->scale);
+ $scaleobj->id = $scalerecord->id;
+ $scaleobj->name = $scalerecord->name;
+
+ $scalemax = count($scaleobj->scale)-1;
+ }
+ }
+ else { //its numeric
+ $scaleobj->scaleitems = $scaleid;
+ $scaleobj->id = $scaleid;
+ $scaleobj->name = null;
+
+ $scalemax = $scaleid;
+ }
+
+ $settings = new stdclass(); //settings that are common to all ratings objects in this context
+ $settings->scale = $scaleobj; //the scale to use now
+ $settings->permissions = $permissions;
+ $settings->aggregationmethod = $aggregate;
+ $settings->returnurl = $returnurl;
+
+ $rating = null;
+ foreach($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
+ $rating = new rating($context, $item->id, $rec->scaleid, $rec->userid);
+ $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);
+ break;
+ }
+ }
+ //if there are no ratings for this item
+ if( !$rating ) {
+ $scaleid = $userid = null;
+ $rating = new rating($context, $item->id, $scaleid, $userid);
+ $rating->id = null;
+ $rating->aggregate = null;
+ $rating->count = 0;
+ $rating->rating = null;
+
+ $rating->itemid = $item->id;
+ $rating->userid = null;
+ $rating->scaleid = 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;
+ }
+ }
+ return $items;
+}
+
+/**
+ * Iterate over $items (forum posts, glossary items etc) and create $item->rating, $item->rating->aggregate and $item->rating->count
+ * Similar to make_context_subobj()
+ * @param array $items array of items
+ * @param resultset $ratings resultset. probably from ratings_load_ratings()
+ */
+private static function make_rating_subobjs( $context, $items, $ratingsrecords, $aggregate, $scaleid, $returnurl) {
+ global $DB;
+
+
+}
+
+public static function get_rating_permissions($context) {
+ return array(RATING_VIEW=>has_capability('moodle/ratings:view',$context), RATING_VIEW_ALL=>has_capability('moodle/ratings:viewall',$context), RATING_POST=>has_capability('moodle/ratings:rate',$context));
+}
+
+} //end rating class definition
\ No newline at end of file
diff --git a/version.php b/version.php
index d02544a7541..1715960ca6a 100644
--- a/version.php
+++ b/version.php
@@ -6,7 +6,7 @@
// This is compared against the values stored in the database to determine
// whether upgrades should be performed (see lib/db/*.php)
- $version = 2010021901; // YYYYMMDD = date of the last version bump
+ $version = 2010031600; // YYYYMMDD = date of the last version bump
// XX = daily increments
$release = '2.0 dev (Build: 20100316)'; // Human-friendly version name