diff --git a/admin/settings/mnet.php b/admin/settings/mnet.php index 33684f25936..96811871606 100644 --- a/admin/settings/mnet.php +++ b/admin/settings/mnet.php @@ -1,5 +1,5 @@ dirroot.'/mnet/lib.php'); // This file defines settingpages and externalpages under the "mnet" category if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page diff --git a/course/moodleform_mod.php b/course/moodleform_mod.php index fb8f3b7ff25..d0e252b9bdd 100644 --- a/course/moodleform_mod.php +++ b/course/moodleform_mod.php @@ -93,6 +93,18 @@ abstract class moodleform_mod extends moodleform { * @param array $default_values passed by reference */ function data_preprocessing(&$default_values){ + if (empty($default_values['scale'])) { + $default_values['assessed'] = 0; + } + + if (empty($default_values['assessed'])){ + //$default_values['userating'] = 0;//this was used by glossary to check/uncheck a 'use ratings' checkbox + $default_values['ratingtime'] = 0; + } else { + //$default_values['userating'] = 1; + $default_values['ratingtime']= + ($default_values['assesstimestart'] && $default_values['assesstimefinish']) ? 1 : 0; + } } /** @@ -327,6 +339,30 @@ abstract class moodleform_mod extends moodleform { } } + if (plugin_supports('mod', $this->_modname, FEATURE_RATINGS, false)) { + $mform->addElement('header', 'modstandardratings', get_string('ratings', 'ratings')); + + //$mform->addElement('checkbox', 'assessed', get_string('allowratings', 'ratings') , get_string('ratingsuse', 'ratings')); + + $mform->addElement('select', 'assessed', get_string('aggregatetype', 'ratings') , forum_get_aggregate_types()); + $mform->setDefault('assessed', 0); + $mform->setHelpButton('assessed', array('assessaggregate', get_string('aggregatetype', 'ratings'), 'forum')); + + $mform->addElement('modgrade', 'scale', get_string('grade'), false); + $mform->disabledIf('scale', 'assessed', 'eq', 0); + + $mform->addElement('checkbox', 'ratingtime', get_string('ratingtime', 'forum')); + $mform->disabledIf('ratingtime', 'assessed', 'eq', 0); + + $mform->addElement('date_time_selector', 'assesstimestart', get_string('from')); + $mform->disabledIf('assesstimestart', 'assessed', 'eq', 0); + $mform->disabledIf('assesstimestart', 'ratingtime'); + + $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to')); + $mform->disabledIf('assesstimefinish', 'assessed', 'eq', 0); + $mform->disabledIf('assesstimefinish', 'ratingtime'); + } + $mform->addElement('header', 'modstandardelshdr', get_string('modstandardels', 'form')); if ($this->_features->groups) { $options = array(NOGROUPS => get_string('groupsnone'), diff --git a/lang/en_utf8/ratings.php b/lang/en_utf8/ratings.php new file mode 100644 index 00000000000..21a1046664a --- /dev/null +++ b/lang/en_utf8/ratings.php @@ -0,0 +1,11 @@ + array( 'admin' => CAP_ALLOW ) + ), + 'moodle/ratings:view' => array( + 'captype' => 'read', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'admin' => CAP_ALLOW + ) + ), + 'moodle/ratings:viewall' => array( + + 'riskbitmask' => RISK_PERSONAL, + 'captype' => 'read', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'admin' => CAP_ALLOW + ) + ), + 'moodle/ratings:rate' => array( + + 'riskbitmask' => RISK_DATALOSS, + 'captype' => 'write', + 'contextlevel' => CONTEXT_SYSTEM, + 'legacy' => array( + 'admin' => CAP_ALLOW + ) ) ); diff --git a/lib/db/install.xml b/lib/db/install.xml index 38443985436..9d83702121f 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -2374,7 +2374,7 @@ - +
@@ -2391,5 +2391,20 @@
+ + + + + + + + + + + + + + +
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index faaf0a923e5..c6dd863e8c7 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -3051,6 +3051,80 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL"); upgrade_main_savepoint($result, 2010021800); } + if ($result && $oldversion < 2010031600) { + //create the ratings table (replaces module specific ratings implementations) + $table = new xmldb_table('ratings'); + + /// Adding fields to table ratings + $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); + $table->add_field('contextid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + + $table->add_field('itemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + $table->add_field('scaleid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + $table->add_field('rating', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + + $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); + + /// Adding keys to table ratings + $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); + $table->add_key('contextid', XMLDB_KEY_FOREIGN, array('contextid'), 'context', array('id')); + $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id')); + + /// Adding indexes to table ratings + $table->add_index('itemid', XMLDB_INDEX_NOTUNIQUE, array('itemid')); + + /// Create table for ratings + $dbman->create_table($table); + + //migrate ratings out of the modules into the central ratings table + + //migrate forumratings + //forum ratings only have a single time column so use it for both time created and modified + $ratingssql = 'select r.id as rid, r.post as itemid, r.rating, r.userid, r.time as timecreated, r.time as timemodified, f.scale, f.id as mid from {forum_ratings} r +inner join {forum_posts} p on p.id=r.post +inner join {forum_discussions} d on d.id=p.discussion +inner join {forum} f on f.id=d.forum'; + echo "migrating forum ratings
"; + $result = $result && upgrade_module_ratings($ratingssql,'forum'); + + //migrate glossary_ratings + //glossary ratings only have a single time column so use it for both time created and modified + $ratingssql = 'select r.id as rid, r.entryid as itemid, r.rating, r.userid, r.time as timecreated, r.time as timemodified, g.id as mid, g.scale +from {glossary_ratings} r inner join {glossary_entries} ge on ge.id=r.entryid +inner join {glossary} g on g.id=ge.glossaryid'; + echo "migrating glossary ratings
"; + $result = $result && upgrade_module_ratings($ratingssql,'glossary'); + + //migrate data_ratings + //data ratings didnt store time created and modified so Im using the times from the record the rating was attached to + $ratingssql = 'select r.id as rid, r.recordid as itemid, r.rating, r.userid, re.timecreated, re.timemodified, d.scale, d.id as mid +from {data_ratings} r inner join {data_records} re on r.recordid=re.id +inner join {data} d on d.id=re.dataid'; + echo "migrating data ratings
"; + $result = $result && upgrade_module_ratings($ratingssql,'data'); + + //add assesstimestart and assesstimefinish columns to data + $table = new xmldb_table('data'); + $field = new xmldb_field('assesstimestart'); + if (!$dbman->field_exists($table, $field)) { + $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'assessed'); + $dbman->add_field($table, $field); + } + $field = new xmldb_field('assesstimefinish'); + if (!$dbman->field_exists($table, $field)) { + $field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'assesstimestart'); + $dbman->add_field($table, $field); + } + + //todo set permissions based on current value of glossary.assessed + + //todo drop forum_ratings, data_ratings and glossary_ratings + + upgrade_main_savepoint($result, 2010031600); + } + return $result; } diff --git a/lib/db/upgradelib.php b/lib/db/upgradelib.php index ba225e3e7cf..c46827d13a1 100644 --- a/lib/db/upgradelib.php +++ b/lib/db/upgradelib.php @@ -396,3 +396,47 @@ function upgrade_cleanup_unwanted_block_contexts($contextidarray) { return ($outcome1 && $outcome2 && $outcome4 && $outcome4); } + +function upgrade_module_ratings($ratingssql, $modulename) { + global $DB; + $contextid = null; + $contextarray = array(); + $result = true; + $i=0; + + $ratings = $DB->get_records_sql($ratingssql); + + foreach ($ratings as $old_rating) { + if($i++%500==0) { + upgrade_set_timeout(60);//prevent a timeout + } + + //all posts within a given forum will have the same context id so store them in an array + if( !array_key_exists($old_rating->mid, $contextarray) ) { + $sql = 'select cxt.id from {course_modules} cm inner join {modules} m on cm.module=m.id +inner join {context} cxt on cxt.instanceid=cm.id +where m.name=:modulename and cm.instance=:moduleinstanceid and cxt.contextlevel='.CONTEXT_MODULE; + $params = array(); + $params['moduleinstanceid'] = $old_rating->mid; + $params['modulename'] = $modulename; + $results = $DB->get_record_sql($sql, $params); + $contextarray[$old_rating->mid] = $results->id; + } + $contextid = $contextarray[$old_rating->mid]; + + $rating = new stdclass; + $rating->contextid = $contextid; + $rating->scaleid = $old_rating->scale; + $rating->itemid = $old_rating->itemid; + $rating->rating = $old_rating->rating; + $rating->userid = $old_rating->userid; + $rating->timecreated = $old_rating->timecreated; + $rating->timemodified = $old_rating->timemodified; + + $result = $result && $DB->insert_record('ratings', $rating); + } + + $ratings->close(); + + return $result; +} diff --git a/lib/moodlelib.php b/lib/moodlelib.php index d4d438c12da..d161ed1f9fe 100644 --- a/lib/moodlelib.php +++ b/lib/moodlelib.php @@ -364,6 +364,8 @@ define('FEATURE_MODEDIT_DEFAULT_COMPLETION', 'modedit_default_completion'); define('FEATURE_COMMENT', 'comment'); +define('FEATURE_RATINGS', 'ratings'); + /** Unspecified module archetype */ define('MOD_ARCHETYPE_OTHER', 0); /** Resource-like type module */ diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 97e12959aef..0882347a100 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -1229,6 +1229,116 @@ class core_renderer extends renderer_base { return html_writer::empty_tag('img', $attributes); } + /** + * Produces the html that represents this rating in the UI + * @param $page the page object on which this rating will appear + */ + function render_rating(rating $rating) { + global $CFG, $USER, $PAGE; + static $strrate;//holds the string "rate". Its static so we only fetch it once. + static $havesetupjavascript = false; + + if( !$havesetupjavascript && !empty($CFG->enableajax) ) { + $PAGE->requires->js_init_call('M.core_ratings.init'); + $havesetupjavascript = true; + } + + if (empty($strrate)) { + $strrate = get_string("rate", "forum"); + } + + $strratings = ''; + + if($rating->settings->permissions[RATING_VIEW] || $rating->settings->permissions[RATING_VIEW_ALL]) { + switch ($rating->settings->aggregationmethod) { + case RATING_AGGREGATE_AVERAGE : + $strratings .= get_string("aggregateavg", "forum"); + break; + case RATING_AGGREGATE_COUNT : + $strratings .= get_string("aggregatecount", "forum"); + break; + case RATING_AGGREGATE_MAXIMUM : + $strratings .= get_string("aggregatemax", "forum"); + break; + case RATING_AGGREGATE_MINIMUM : + $strratings .= get_string("aggregatemin", "forum"); + break; + case RATING_AGGREGATE_SUM : + $strratings .= get_string("aggregatesum", "forum"); + break; + } + + if (empty($strratings)) { + $strratings .= $strrate; + } + $strratings .= ': '; + + $scalemax = 0; + $ratingstr = null; + + if ( is_array($rating->settings->scale->scaleitems) ) { + $scalemax = $rating->settings->scale->scaleitems[ count($rating->settings->scale->scaleitems)-1 ]; + $ratingstr = $rating->settings->scale->scaleitems[$rating->rating]; + } + else { //its numeric + $scalemax = $rating->settings->scale->scaleitems; + $ratingstr = round($rating->aggregate,1); + } + + $aggstr = "{$ratingstr} / $scalemax ({$rating->count}) "; + + if ($rating->settings->permissions[RATING_VIEW_ALL]) { + $link = new moodle_url("/rating/index.php?contextid={$rating->context->id}&itemid={$rating->itemid}&scaleid={$rating->scaleid}"); + $action = new popup_action('click', $link, 'ratings', array('height' => 400, 'width' => 600)); + $strratings .= $this->action_link($link, $aggstr, $action); + } else if ($rating->settings->permissions[RATING_VIEW_ALL]) { + $strratings .= $aggstr; + } + } + + //todo andrew alter the below if to deny guest users the ability to post ratings. + //Petr to define "guest" + $formstart = null; + if($rating->settings->permissions[RATING_POST]) { + //dont use $rating->userid below as it will be null if the user hasnt already rated the item + $formstart = << +
+ + + + +END; + $strratings = $formstart.$strratings; + + //generate an array of values for numeric scales + $scalearray = $rating->settings->scale->scaleitems; + if( !is_array($scalearray) && is_int($scalearray) ) { + $scalearray = array(); + for($i=0; $i<=$rating->settings->scale->scaleitems; $i++) { + $scalearray[$i] = $i; + } + } + + $scalearray = array(RATING_UNSET_RATING => $strrate.'...') + $scalearray; + $strratings .= html_writer::select($scalearray, 'rating'.$rating->itemid, $rating->rating, false, array('class'=>'postratingmenu ratinginput')); + + //output submit button + $strratings .= ''; + + //ajax code is included by rating::load_ratings() + + if ( is_array($rating->settings->scale) ) { + //todo andrew where can we get the course id from? + //$strratings .= $this->help_icon_scale($course->id, $scale); + $strratings .= $this->help_icon_scale(1, $rating->settings->scale); + } + $strratings .= '
'; + } + + return $strratings; + } + /* * Centered heading with attached help button (same title text) * and optional icon attached diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php index f50e469f450..557de73c2cb 100644 --- a/lib/outputrequirementslib.php +++ b/lib/outputrequirementslib.php @@ -398,6 +398,11 @@ class page_requirements_manager { 'fullpath' => '/group/module.js', 'requires' => array('node', 'overlay', 'event-mouseenter')); break; + case 'core_ratings': + $module = array('name' => 'core_ratings', + 'fullpath' => '/rating/module.js', + 'requires' => array('node', 'event', 'overlay', 'io', 'json')); + break; } } else { diff --git a/mod/data/lib.php b/mod/data/lib.php index 814ce0de032..b88a25a8700 100755 --- a/mod/data/lib.php +++ b/mod/data/lib.php @@ -2579,6 +2579,7 @@ function data_supports($feature) { case FEATURE_GRADE_HAS_GRADE: return true; case FEATURE_GRADE_OUTCOMES: return true; case FEATURE_MOD_SUBPLUGINS: return array('datafield'=>'mod/data/field', 'datapreset'=>'mod/data/preset'); + case FEATURE_RATINGS: return true; default: return null; } diff --git a/mod/data/mod_form.php b/mod/data/mod_form.php index 62ed939146a..325722c0501 100644 --- a/mod/data/mod_form.php +++ b/mod/data/mod_form.php @@ -53,10 +53,10 @@ class mod_data_mod_form extends moodleform_mod { $mform->addElement('select', 'rssarticles', get_string('numberrssarticles', 'data') , $countoptions); } - $mform->addElement('checkbox', 'assessed', get_string('allowratings', 'data') , get_string('ratingsuse', 'data')); + //$mform->addElement('checkbox', 'assessed', get_string('allowratings', 'data') , get_string('ratingsuse', 'data')); - $mform->addElement('modgrade', 'scale', get_string('grade'), false); - $mform->disabledIf('scale', 'assessed'); + //$mform->addElement('modgrade', 'scale', get_string('grade'), false); + //$mform->disabledIf('scale', 'assessed'); $this->standard_coursemodule_elements(); diff --git a/mod/forum/lib.php b/mod/forum/lib.php index e0f06e06864..3c85af35cd7 100644 --- a/mod/forum/lib.php +++ b/mod/forum/lib.php @@ -42,8 +42,10 @@ define('FORUM_TRACKING_OFF', 0); define('FORUM_TRACKING_OPTIONAL', 1); define('FORUM_TRACKING_ON', 2); +//todo andrew remove this define('FORUM_UNSET_POST_RATING', -999); +//todo andrew and remove these define ('FORUM_AGGREGATE_NONE', 0); //no ratings define ('FORUM_AGGREGATE_AVG', 1); define ('FORUM_AGGREGATE_COUNT', 2); @@ -274,6 +276,7 @@ function forum_supports($feature) { case FEATURE_COMPLETION_HAS_RULES: return true; case FEATURE_GRADE_HAS_GRADE: return true; case FEATURE_GRADE_OUTCOMES: return true; + case FEATURE_RATINGS: return true; default: return null; } @@ -1453,6 +1456,7 @@ 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 */ + //todo andrew pretty sure I can remove this function forum_get_user_grades($forum, $userid=0) { global $CFG, $DB; @@ -4155,6 +4159,7 @@ function forum_get_ratings_summary($postid, $scale, $ratings=NULL) { * @param array $scale is an array of ratings * @param int $myrating */ + //todo andrew remove this function function forum_print_rating_menu($postid, $userid, $scale, $myrating=NULL) { static $strrate; diff --git a/mod/forum/mod_form.php b/mod/forum/mod_form.php index b07eebe5987..41f77c8f225 100644 --- a/mod/forum/mod_form.php +++ b/mod/forum/mod_form.php @@ -107,7 +107,7 @@ class mod_forum_mod_form extends moodleform_mod { } //------------------------------------------------------------------------------- - $mform->addElement('header', '', get_string('grade')); + /*$mform->addElement('header', '', get_string('grade')); $mform->addElement('select', 'assessed', get_string('aggregatetype', 'forum') , forum_get_aggregate_types()); $mform->setDefault('assessed', 0); @@ -125,7 +125,7 @@ class mod_forum_mod_form extends moodleform_mod { $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to')); $mform->disabledIf('assesstimefinish', 'assessed', 'eq', 0); - $mform->disabledIf('assesstimefinish', 'ratingtime'); + $mform->disabledIf('assesstimefinish', 'ratingtime');*/ //------------------------------------------------------------------------------- @@ -188,17 +188,6 @@ class mod_forum_mod_form extends moodleform_mod { } function data_preprocessing(&$default_values) { - if (empty($default_values['scale'])) { - $default_values['assessed'] = 0; - } - - if (empty($default_values['assessed'])) { - $default_values['ratingtime'] = 0; - } else { - $default_values['ratingtime']= - ($default_values['assesstimestart'] && $default_values['assesstimefinish']) ? 1 : 0; - } - // Set up the completion checkboxes which aren't part of standard data. // We also make the default value (if you turn on the checkbox) for those // numbers to be 1, this will not apply unless checkbox is ticked. diff --git a/mod/glossary/lib.php b/mod/glossary/lib.php index 30cd93a4bea..2fc8edae28c 100644 --- a/mod/glossary/lib.php +++ b/mod/glossary/lib.php @@ -2667,6 +2667,7 @@ function glossary_supports($feature) { case FEATURE_COMPLETION_TRACKS_VIEWS: return true; case FEATURE_GRADE_HAS_GRADE: return true; case FEATURE_GRADE_OUTCOMES: return true; + case FEATURE_RATINGS: return true; default: return null; } diff --git a/mod/glossary/mod_form.php b/mod/glossary/mod_form.php index 6e87f516461..c00dff98bbc 100644 --- a/mod/glossary/mod_form.php +++ b/mod/glossary/mod_form.php @@ -121,7 +121,7 @@ class mod_glossary_mod_form extends moodleform_mod { } //------------------------------------------------------------------------------- - $mform->addElement('header', '', get_string('grade')); + /*$mform->addElement('header', '', get_string('grade')); $mform->addElement('checkbox', 'userating', get_string('allowratings', 'glossary') , get_string('ratingsuse', 'glossary')); $options=array(); @@ -142,7 +142,7 @@ class mod_glossary_mod_form extends moodleform_mod { $mform->addElement('date_time_selector', 'assesstimefinish', get_string('to')); $mform->disabledIf('assesstimefinish', 'userating'); - $mform->disabledIf('assesstimefinish', 'ratingtime'); + $mform->disabledIf('assesstimefinish', 'ratingtime');*/ //------------------------------------------------------------------------------- $this->standard_coursemodule_elements(); @@ -172,18 +172,6 @@ class mod_glossary_mod_form extends moodleform_mod { } function data_preprocessing(&$default_values){ - if (empty($default_values['scale'])){ - $default_values['assessed'] = 0; - } - - if (empty($default_values['assessed'])){ - $default_values['userating'] = 0; - $default_values['ratingtime'] = 0; - } else { - $default_values['userating'] = 1; - $default_values['ratingtime']= - ($default_values['assesstimestart'] && $default_values['assesstimefinish']) ? 1 : 0; - } } } diff --git a/rating/index.php b/rating/index.php new file mode 100644 index 00000000000..75e08cfbf32 --- /dev/null +++ b/rating/index.php @@ -0,0 +1,103 @@ +. + +/** + * A page to display a list of ratings for a given item (forum post etc) + * + * @package moodlecore + * @copyright 2010 Andrew Davis + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +require_once("../config.php"); +require_once("ratinglib.php"); + +$itemcontextid = required_param('contextid', PARAM_INT); +$itemid = required_param('itemid', PARAM_INT); +$scaleid = required_param('scaleid', PARAM_INT); +$sort = optional_param('sort', '', PARAM_ALPHA); + +$context = get_context_instance_by_id($itemcontextid); +$cm = get_coursemodule_from_id('', $context->instanceid, 0, false, MUST_EXIST); +$courseid = $cm->course; +//$course = get_course_from_path($context->path); +require_login($courseid, false, $cm); + +$url = new moodle_url('/rating/index.php', array('contextid'=>$itemcontextid,'itemid'=>$itemid)); +if ($sort !== 0) { + $url->param('sort', $sort); +} +$PAGE->set_url($url); + +$permissions = rating::get_rating_permissions(context); +if (!$permissions[RATING_VIEW]) { + print_error('noviewrate', 'ratings'); +} +if (!$permissions[RATING_VIEW_ALL] and $USER->id != $item->userid) { + print_error('noviewanyrate', 'ratings'); +} + +switch ($sort) { + case 'firstname': $sqlsort = "u.firstname ASC"; break; + case 'rating': $sqlsort = "r.rating ASC"; break; + default: $sqlsort = "r.timemodified ASC"; +} + +$scalemenu = make_grades_menu($scaleid); + +$strratings = get_string('ratings', 'ratings'); +$strrating = get_string('rating', 'ratings'); +$strname = get_string('name'); +$strtime = get_string('time'); + +//Is there something more meaningful we can put in the title? +//$PAGE->set_title("$strratings: ".format_string($post->subject)); +$PAGE->set_title("$strratings: ".format_string($itemid)); +echo $OUTPUT->header(); + +//if (!$ratings = forum_get_ratings($post->id, $sqlsort)) { +$ratings = rating::load_ratings_for_item($context, $itemid, $sort); +if (!$ratings) { + //print_error('noresult', 'forum', '', format_string($post->subject)); + print_error('noresult'); +} else { + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + echo ""; + foreach ($ratings as $rating) { + echo ''; + echo "'; + echo '"; + echo '"; + echo "\n"; + } + echo "
 $strname$strrating$strtime
"; + if($courseid) { + echo $OUTPUT->user_picture($rating, array('courseid'=>$courseid)); + } else { + echo $OUTPUT->user_picture($rating); + } + echo ''.fullname($rating).''.$scalemenu[$rating->rating]."'.userdate($rating->timemodified)."
"; + 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