Merge remote-tracking branch 'moodle/master' into MDL-20636_master_new_question_engine

Conflicts:
	lib/db/upgrade.php
	mod/quiz/lib.php
This commit is contained in:
Tim Hunt 2011-06-06 17:14:59 +01:00
commit 54771d89d1
290 changed files with 4637 additions and 1419 deletions

View file

@ -1316,6 +1316,7 @@ abstract class role_allow_role_page {
foreach ($rs as $allow) {
$this->allowed[$allow->roleid][$allow->{$this->targetcolname}] = true;
}
$rs->close();
}
/**

View file

@ -0,0 +1,34 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Base class for course report backup plugins.
*
* NOTE: When you back up a course, it potentially may run backup for all
* course reports. In order to control whether a particular report gets
* backed up, a course report should make use of the second and third
* parameters in get_plugin_element().
*
* @package moodlecore
* @subpackage backup-moodle2
* @copyright 2011 onwards The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class backup_coursereport_plugin extends backup_plugin {
// Use default parent behaviour
}

View file

@ -36,6 +36,7 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_subplugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');

View file

@ -417,6 +417,10 @@ class backup_course_structure_step extends backup_structure_step {
// save course data (in case of user theme, legacy theme, etc)
$this->add_plugin_structure('theme', $course, true);
// attach course report plugin structure to $course element; multiple
// course reports can save course data if required
$this->add_plugin_structure('coursereport', $course, true);
// attach plagiarism plugin structure to $course element, only one allowed
$this->add_plugin_structure('plagiarism', $course, false);

View file

@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
defined('MOODLE_INTERNAL') || die();
/**
* Restore for course plugin: course report.
*
* @package moodlecore
* @subpackage backup-moodle2
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class restore_coursereport_plugin extends restore_plugin {
// Use default parent behaviour
}

View file

@ -35,11 +35,13 @@ require_once($CFG->dirroot . '/backup/moodle2/restore_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_theme_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_theme_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_coursereport_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/backup_plagiarism_plugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');

View file

@ -80,6 +80,23 @@ abstract class restore_plugin {
}
}
/**
* after_restore dispatcher for any restore_plugin class
*
* This method will dispatch execution to the corresponding
* after_restore_xxx() method when available, with xxx
* being the connection point of the instance, so plugin
* classes with multiple connection points will support
* multiple after_restore methods, one for each connection point
*/
public function launch_after_restore_methods() {
// Check if the after_restore method exists and launch it
$afterrestore = 'after_restore_' . basename($this->connectionpoint->get_path());
if (method_exists($this, $afterrestore)) {
$this->$afterrestore();
}
}
/**
* Returns one array with all the decode contents
* to be processed by the links decoder

View file

@ -1010,6 +1010,9 @@ class restore_course_structure_step extends restore_structure_step {
// Apply for 'theme' plugins optional paths at course level
$this->add_plugin_structure('theme', $course);
// Apply for 'course report' plugins optional paths at course level
$this->add_plugin_structure('coursereport', $course);
// Apply for plagiarism plugins optional paths at course level
$this->add_plugin_structure('plagiarism', $course);

View file

@ -360,6 +360,43 @@ abstract class restore_structure_step extends restore_step {
}
/**
* Launch all the after_restore methods present in all the processing objects
*
* This method will launch all the after_restore methods that can be defined
* both in restore_plugin class
*
* For restore_plugin classes the name of the method to be executed will be
* "after_restore_" + connection point (as far as can be multiple connection
* points in the same class)
*/
public function launch_after_restore_methods() {
$alreadylaunched = array(); // To avoid multiple executions
foreach ($this->pathelements as $pathelement) {
// Get the processing object
$pobject = $pathelement->get_processing_object();
// Skip null processors (child of grouped ones for sure)
if (is_null($pobject)) {
continue;
}
// Skip restore structure step processors (this)
if ($pobject instanceof restore_structure_step) {
continue;
}
// Skip already launched processing objects
if (in_array($pobject, $alreadylaunched, true)) {
continue;
}
// Add processing object to array of launched ones
$alreadylaunched[] = $pobject;
// If the processing object has support for
// launching after_restore methods, use it
if (method_exists($pobject, 'launch_after_restore_methods')) {
$pobject->launch_after_restore_methods();
}
}
}
/**
* This method will be executed after the whole structure step have been processed
*

View file

@ -100,6 +100,13 @@ abstract class restore_task extends base_task {
* method if available
*/
public function execute_after_restore() {
if ($this->executed) {
foreach ($this->steps as $step) {
if (method_exists($step, 'launch_after_restore_methods')) {
$step->launch_after_restore_methods();
}
}
}
if ($this->executed && method_exists($this, 'after_restore')) {
$this->after_restore();
}

View file

@ -71,20 +71,23 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
}
/**
* Notify start of path if selected and not under grouped
* The parser fires this each time one path is going to be parsed
*
* @param string $path xml path which parsing has started
*/
public function before_path($path) {
if ($this->path_is_selected($path) && !$this->grouped_parent_exists($path)) {
if (!$this->grouped_parent_exists($path)) {
parent::before_path($path);
}
}
/**
* Dispatch grouped chunks safely once their end tag happens.
* Also notify end of path if selected and not under grouped
* The parser fires this each time one path has been parsed
*
* @param string $path xml path which parsing has ended
*/
public function after_path($path) {
// Have finished one grouped path, dispatch it
if ($this->path_is_grouped($path)) {
// Any accumulated information must be in
// currentdata, properly built
@ -95,7 +98,7 @@ abstract class grouped_parser_processor extends simplified_parser_processor {
}
// Normal notification of path end
// Only if path is selected and not child of grouped
if ($this->path_is_selected($path) && !$this->grouped_parent_exists($path)) {
if (!$this->grouped_parent_exists($path)) {
parent::after_path($path);
}
}

View file

@ -41,12 +41,14 @@ abstract class simplified_parser_processor extends progressive_parser_processor
protected $paths; // array of paths we are interested on
protected $parentpaths; // array of parent paths of the $paths
protected $parentsinfo; // array of parent attributes to be added as child tags
protected $startendinfo;// array (stack) of startend information
public function __construct(array $paths) {
public function __construct(array $paths = array()) {
parent::__construct();
$this->paths = array();
$this->parentpaths = array();
$this->parentsinfo = array();
$this->startendinfo = array();
// Add paths and parentpaths. We are looking for attributes there
foreach ($paths as $key => $path) {
$this->add_path($path);
@ -95,6 +97,10 @@ abstract class simplified_parser_processor extends progressive_parser_processor
// If the path is a registered one, let's process it
if ($this->path_is_selected($path)) {
// Send all the pending notify_path_start/end() notifications
$this->process_pending_startend_notifications($path, 'start');
// First of all, look for attributes available at parentsinfo
// in order to get them available as normal tags
if (isset($this->parentsinfo[$parentpath][$tag]['attrs'])) {
@ -146,29 +152,86 @@ abstract class simplified_parser_processor extends progressive_parser_processor
} else {
$this->chunks--; // Chunk skipped
}
return true;
}
/**
* The parser fires this each time one path is going to be parsed
*
* @param string $path xml path which parsing has started
*/
public function before_path($path) {
if ($this->path_is_selected($path)) {
$this->notify_path_start($path);
$this->startendinfo[] = array('path' => $path, 'action' => 'start');
}
}
/**
* The parser fires this each time one path has been parsed
*
* @param string $path xml path which parsing has ended
*/
public function after_path($path) {
$toprocess = false;
// If the path being closed matches (same or parent) the first path in the stack
// we process pending startend notifications until one matching end is found
if ($element = reset($this->startendinfo)) {
$elepath = $element['path'];
$eleaction = $element['action'];
if (strpos($elepath, $path) === 0) {
$toprocess = true;
}
// Also, if the stack of startend notifications is empty, we can process current end
// path safely
} else {
$toprocess = true;
}
if ($this->path_is_selected($path)) {
$this->notify_path_end($path);
$this->startendinfo[] = array('path' => $path, 'action' => 'end');
}
// Send all the pending startend notifications if decided to do so
if ($toprocess) {
$this->process_pending_startend_notifications($path, 'end');
}
}
// Protected API starts here
/**
* Adjust start/end til finding one match start/end path (included)
*
* This will trigger all the pending {@see notify_path_start} and
* {@see notify_path_end} calls for one given path and action
*
* @param string path the path to look for as limit
* @param string action the action to look for as limit
*/
protected function process_pending_startend_notifications($path, $action) {
// Iterate until one matching path and action is found (or the array is empty)
$elecount = count($this->startendinfo);
$elematch = false;
while ($elecount > 0 && !$elematch) {
$element = array_shift($this->startendinfo);
$elecount--;
$elepath = $element['path'];
$eleaction = $element['action'];
if ($elepath == $path && $eleaction == $action) {
$elematch = true;
}
if ($eleaction == 'start') {
$this->notify_path_start($elepath);
} else {
$this->notify_path_end($elepath);
}
}
}
protected function postprocess_chunk($data) {
$this->dispatch_chunk($data);
}

View file

@ -0,0 +1,24 @@
<MOODLE_BACKUP>
<COURSE ID="100">
<SECTIONS>
<SECTION>
<ID>200</ID>
<MODS>
<MOD>
<ID>300</ID>
<ROLES_OVERRIDES>
</ROLES_OVERRIDES>
</MOD>
<MOD />
<MOD />
<MOD ID="400" />
<MOD>
<ID>500</ID>
</MOD>
<MOD />
<MOD />
</MODS>
</SECTION>
</SECTIONS>
</COURSE>
</MOODLE_BACKUP>

View file

@ -330,6 +330,80 @@ class progressive_parser_test extends UnitTestCase {
sort($snotifs);
sort($enotifs);
$this->assertEqual($snotifs, $enotifs);
// Now verify that the start/process/end order is correct
$allnotifs = $pr->get_all_notifications();
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
// Check integrity of the notifications
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
$this->assertEqual($errcount, 0); // No errors found, plz
}
/**
* test how the simplified processor and the order of start/process/end events happens
* with one real fragment of one backup 1.9 file, where some problems
* were found by David, hence we honor him in the name of the test ;-)
*/
function test_simplified_david_backup19_file_fragment() {
global $CFG;
// Instantiate progressive_parser
$pp = new progressive_parser();
// Instantiate grouped_parser_processor
$pr = new mock_simplified_parser_processor();
// Add interesting paths
$pr->add_path('/MOODLE_BACKUP/COURSE');
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
$this->assertTrue($pr instanceof progressive_parser_processor);
// Assign processor to parser
$pp->set_processor($pr);
// Set file from fixtures
$pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test5.xml');
// Process the file
$pp->process();
// Get all the simplified chunks and perform various validations
$chunks = $pr->get_chunks();
$this->assertEqual(count($chunks), 3); // Only 3, because 7 (COURSE, ROLES_OVERRIDES and 5 MOD) are empty, aka no chunk
// Now check start notifications
$snotifs = $pr->get_start_notifications();
// Check we have received the correct number of notifications
$this->assertEqual(count($snotifs), 10); // Start tags are dispatched for empties (ROLES_OVERRIDES)
// Check first and last notifications
$this->assertEqual($snotifs[0], '/MOODLE_BACKUP/COURSE');
$this->assertEqual($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
$this->assertEqual($snotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$this->assertEqual($snotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
$this->assertEqual($snotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$this->assertEqual($snotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$this->assertEqual($snotifs[9], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
// Now check end notifications
$enotifs = $pr->get_end_notifications();
// Check we have received the correct number of notifications
$this->assertEqual(count($snotifs), 10); // End tags are dispatched for empties (ROLES_OVERRIDES)
// Check first, and last notifications
$this->assertEqual($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
$this->assertEqual($enotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$this->assertEqual($enotifs[2], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$this->assertEqual($enotifs[3], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$this->assertEqual($enotifs[7], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$this->assertEqual($enotifs[8], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
$this->assertEqual($enotifs[9], '/MOODLE_BACKUP/COURSE');
// Check start and end notifications are balanced
sort($snotifs);
sort($enotifs);
$this->assertEqual($snotifs, $enotifs);
// Now verify that the start/process/end order is correct
$allnotifs = $pr->get_all_notifications();
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
// Check integrity of the notifications
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
$this->assertEqual($errcount, 0); // No errors found, plz
}
/*
@ -498,6 +572,127 @@ class progressive_parser_test extends UnitTestCase {
sort($snotifs);
sort($enotifs);
$this->assertEqual($snotifs, $enotifs);
// Now verify that the start/process/end order is correct
$allnotifs = $pr->get_all_notifications();
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
// Check integrity of the notifications
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
$this->assertEqual($errcount, 0); // No errors found, plz
}
/**
* test how the grouped processor and the order of start/process/end events happens
* with one real fragment of one backup 1.9 file, where some problems
* were found by David, hence we honor him in the name of the test ;-)
*/
function test_grouped_david_backup19_file_fragment() {
global $CFG;
// Instantiate progressive_parser
$pp = new progressive_parser();
// Instantiate grouped_parser_processor
$pr = new mock_grouped_parser_processor();
// Add interesting paths
$pr->add_path('/MOODLE_BACKUP/COURSE');
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION', true);
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD');
$pr->add_path('/MOODLE_BACKUP/COURSE/SECTIONS/SECTION/MODS/MOD/ROLES_OVERRIDES');
$this->assertTrue($pr instanceof progressive_parser_processor);
// Assign processor to parser
$pp->set_processor($pr);
// Set file from fixtures
$pp->set_file($CFG->dirroot . '/backup/util/xml/parser/simpletest/fixtures/test5.xml');
// Process the file
$pp->process();
// Get all the simplified chunks and perform various validations
$chunks = $pr->get_chunks();
$this->assertEqual(count($chunks), 1); // Only 1, the SECTION one
// Now check start notifications
$snotifs = $pr->get_start_notifications();
// Check we have received the correct number of notifications
$this->assertEqual(count($snotifs), 2);
// Check first and last notifications
$this->assertEqual($snotifs[0], '/MOODLE_BACKUP/COURSE');
$this->assertEqual($snotifs[1], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
// Now check end notifications
$enotifs = $pr->get_end_notifications();
// Check we have received the correct number of notifications
$this->assertEqual(count($snotifs), 2); // End tags are dispatched for empties (ROLES_OVERRIDES)
// Check first, and last notifications
$this->assertEqual($enotifs[0], '/MOODLE_BACKUP/COURSE/SECTIONS/SECTION');
$this->assertEqual($enotifs[1], '/MOODLE_BACKUP/COURSE');
// Check start and end notifications are balanced
sort($snotifs);
sort($enotifs);
$this->assertEqual($snotifs, $enotifs);
// Now verify that the start/process/end order is correct
$allnotifs = $pr->get_all_notifications();
$this->assertEqual(count($allnotifs), count($snotifs) + count($enotifs) + count($chunks)); // The count
// Check integrity of the notifications
$errcount = $this->helper_check_notifications_order_integrity($allnotifs);
$this->assertEqual($errcount, 0); // No errors found, plz
}
/**
* Helper function that given one array of ordered start/process/end notifications will
* check it of integrity like:
* - process only happens if start is the previous notification
* - end only happens if dispatch is the previous notification
* - start only happen with level > than last one and if there is no already started like that
*
* @param array $notifications ordered array of notifications with format [start|process|end]:path
* @return int number of integrity problems found (errors)
*/
function helper_check_notifications_order_integrity($notifications) {
$numerrors = 0;
$notifpile = array('pilebase' => 'start');
$lastnotif = 'start:pilebase';
foreach ($notifications as $notif) {
$lastpiletype = end($notifpile);
$lastpilepath = key($notifpile);
$lastpilelevel = strlen(preg_replace('/[^\/]/', '', $lastpilepath));
$lastnotiftype = preg_replace('/:.*/', '', $lastnotif);
$lastnotifpath = preg_replace('/.*:/', '', $lastnotif);
$lastnotiflevel = strlen(preg_replace('/[^\/]/', '', $lastnotifpath));
$notiftype = preg_replace('/:.*/', '', $notif);
$notifpath = preg_replace('/.*:/', '', $notif);
$notiflevel = strlen(preg_replace('/[^\/]/', '', $notifpath));
switch ($notiftype) {
case 'process':
if ($lastnotifpath != $notifpath or $lastnotiftype != 'start') {
$numerrors++; // Only start for same path from last notification is allowed before process
}
$notifpile[$notifpath] = 'process'; // Update the status in the pile
break;
case 'end':
if ($lastpilepath != $notifpath or ($lastpiletype != 'process' and $lastpiletype != 'start')) {
$numerrors++; // Only process and start for same path from last pile is allowed before end
}
unset($notifpile[$notifpath]); // Delete from the pile
break;
case 'start':
if (array_key_exists($notifpath, $notifpile) or $notiflevel <= $lastpilelevel) {
$numerrors++; // Only non existing in pile and with level > last pile is allowed on start
}
$notifpile[$notifpath] = 'start'; // Add to the pile
break;
default:
$numerrors++; // Incorrect type of notification => error
}
// Update lastnotif
$lastnotif = $notif;
}
return $numerrors;
}
}
@ -572,17 +767,21 @@ class mock_simplified_parser_processor extends simplified_parser_processor {
private $chunksarr = array(); // To accumulate the found chunks
private $startarr = array(); // To accumulate all the notified path starts
private $endarr = array(); // To accumulate all the notified path ends
private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
public function dispatch_chunk($data) {
$this->chunksarr[] = $data;
$this->allnotif[] = 'process:' . $data['path'];
}
public function notify_path_start($path) {
$this->startarr[] = $path;
$this->allnotif[] = 'start:' . $path;
}
public function notify_path_end($path) {
$this->endarr[] = $path;
$this->allnotif[] = 'end:' . $path;
}
public function get_chunks() {
@ -596,6 +795,10 @@ class mock_simplified_parser_processor extends simplified_parser_processor {
public function get_end_notifications() {
return $this->endarr;
}
public function get_all_notifications() {
return $this->allnotif;
}
}
/*
@ -606,17 +809,21 @@ class mock_grouped_parser_processor extends grouped_parser_processor {
private $chunksarr = array(); // To accumulate the found chunks
private $startarr = array(); // To accumulate all the notified path starts
private $endarr = array(); // To accumulate all the notified path ends
private $allnotif = array(); // To accumulate all the notified and dispatched events in an ordered way
public function dispatch_chunk($data) {
$this->chunksarr[] = $data;
$this->allnotif[] = 'process:' . $data['path'];
}
public function notify_path_start($path) {
$this->startarr[] = $path;
$this->allnotif[] = 'start:' . $path;
}
public function notify_path_end($path) {
$this->endarr[] = $path;
$this->allnotif[] = 'end:' . $path;
}
public function get_chunks() {
@ -630,4 +837,8 @@ class mock_grouped_parser_processor extends grouped_parser_processor {
public function get_end_notifications() {
return $this->endarr;
}
public function get_all_notifications() {
return $this->allnotif;
}
}

View file

@ -66,7 +66,75 @@ M.core_dock.init = function(Y) {
// Give the dock item class the event properties/methods
Y.augment(this.item, Y.EventTarget);
Y.augment(this, Y.EventTarget, true);
/**
* A 'dock:actionkey' Event.
* The event consists of the left arrow, right arrow, enter and space keys.
* More keys can be mapped to action meanings.
* actions: collapse , expand, toggle, enter.
*
* This event is subscribed to by dockitems.
* The on() method to subscribe allows specifying the desired trigger actions as JSON.
*
* This event can also be delegated if needed.
* Todo: This could be centralised, a similar Event is defined in blocks/navigation/yui/navigation/navigation.js
*/
Y.Event.define("dock:actionkey", {
// Webkit and IE repeat keydown when you hold down arrow keys.
// Opera links keypress to page scroll; others keydown.
// Firefox prevents page scroll via preventDefault() on either
// keydown or keypress.
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
_keys: {
//arrows
'37': 'collapse',
'39': 'expand',
//(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
'32': 'toggle',
'13': 'enter'
},
_keyHandler: function (e, notifier, args) {
if (!args.actions) {
var actObj = {collapse:true, expand:true, toggle:true, enter:true};
} else {
var actObj = args.actions;
}
if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
e.action = this._keys[e.keyCode];
notifier.fire(e);
}
},
on: function (node, sub, notifier) {
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
if (sub.args == null) {
//no actions given
sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
} else {
sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
}
},
detach: function (node, sub, notifier) {
//detach our _detacher handle of the subscription made in on()
sub._detacher.detach();
},
delegate: function (node, sub, notifier, filter) {
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
if (sub.args == null) {
//no actions given
sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
} else {
sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
}
},
detachDelegate: function (node, sub, notifier) {
sub._delegateDetacher.detach();
}
});
// Publish the events the dock has
this.publish('dock:beforedraw', {prefix:'dock'});
this.publish('dock:beforeshow', {prefix:'dock'});
@ -125,9 +193,10 @@ M.core_dock.init = function(Y) {
// Add a removeall button
// Must set the image src seperatly of we get an error with XML strict headers
var removeall = Y.Node.create('<img alt="'+M.str.block.undockall+'" title="'+M.str.block.undockall+'" />');
var removeall = Y.Node.create('<img alt="'+M.str.block.undockall+'" title="'+M.str.block.undockall+'" tabindex="0"/>');
removeall.setAttribute('src',this.cfg.removeallicon);
removeall.on('removeall|click', this.remove_all, this);
removeall.on('dock:actionkey', this.remove_all, this, {actions:{enter:true}});
this.nodes.buttons.appendChild(Y.Node.create('<div class="'+css.controls+'"></div>').append(removeall));
// Create a manager for the height of the tabs. Once set this can be forgotten about
@ -590,7 +659,7 @@ M.core_dock.resetFirstItem = function() {
* @function
* @return {boolean}
*/
M.core_dock.remove_all = function() {
M.core_dock.remove_all = function(e) {
for (var i in this.items) {
this.remove(i);
}
@ -840,9 +909,10 @@ M.core_dock.genericblock.prototype = {
}, this);
// Add a close icon
// Must set the image src seperatly of we get an error with XML strict headers
var closeicon = Y.Node.create('<span class="hidepanelicon"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
closeicon.one('img').setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
closeicon.on('forceclose|click', this.hide, this);
closeicon.on('dock:actionkey',this.hide, this, {actions:{enter:true,toggle:true}});
this.commands.append(closeicon);
}, dockitem);
// Register an event so that when it is removed we can put it back as a block
@ -955,7 +1025,8 @@ M.core_dock.item.prototype = {
this.nodes.docktitle = Y.Node.create('<div id="dock_item_'+this.id+'_title" class="'+css.dockedtitle+'"></div>');
this.nodes.docktitle.append(this.title);
this.nodes.dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+css.dockeditem+'"></div>');
this.nodes.dockitem = Y.Node.create('<div id="dock_item_'+this.id+'" class="'+css.dockeditem+'" tabindex="0"></div>');
this.nodes.dockitem.on('dock:actionkey', this.toggle, this);
if (M.core_dock.count === 1) {
this.nodes.dockitem.addClass('firstdockitem');
}
@ -999,6 +1070,19 @@ M.core_dock.item.prototype = {
M.core_dock.getPanel().hide();
this.fire('dockeditem:hidecomplete');
},
/**
* A toggle between calling show and hide functions based on css.activeitem
* Applies rules to key press events (dock:actionkey)
* @param {Event} e
*/
toggle : function(e) {
var css = M.core_dock.css;
if (this.nodes.docktitle.hasClass(css.activeitem) && !(e.type == 'dock:actionkey' && e.action=='expand')) {
this.hide();
} else if (!this.nodes.docktitle.hasClass(css.activeitem) && !(e.type == 'dock:actionkey' && e.action=='collapse')) {
this.show();
}
},
/**
* This function removes the node and destroys it's bits
* @param {Event} e

View file

@ -52,6 +52,7 @@ class block_navigation_renderer extends plugin_renderer_base {
$attributes['class'] = 'dimmed_text';
}
if (is_string($item->action) || empty($item->action) || ($item->type === navigation_node::TYPE_CATEGORY && empty($options['linkcategories']))) {
$attributes['tabindex'] = '0'; //add tab support to span but still maintain character stream sequence.
$content = html_writer::tag('span', $content, $attributes);
} else if ($item->action instanceof action_link) {
//TODO: to be replaced with something else

View file

@ -1,5 +1,74 @@
YUI.add('moodle-block_navigation-navigation', function(Y){
/**
* A 'actionkey' Event to help with Y.delegate().
* The event consists of the left arrow, right arrow, enter and space keys.
* More keys can be mapped to action meanings.
* actions: collapse , expand, toggle, enter.
*
* This event is delegated to branches in the navigation tree.
* The on() method to subscribe allows specifying the desired trigger actions as JSON.
*
* Todo: This could be centralised, a similar Event is defined in blocks/dock.js
*/
Y.Event.define("actionkey", {
// Webkit and IE repeat keydown when you hold down arrow keys.
// Opera links keypress to page scroll; others keydown.
// Firefox prevents page scroll via preventDefault() on either
// keydown or keypress.
_event: (Y.UA.webkit || Y.UA.ie) ? 'keydown' : 'keypress',
_keys: {
//arrows
'37': 'collapse',
'39': 'expand',
//(@todo: lrt/rtl/M.core_dock.cfg.orientation decision to assign arrow to meanings)
'32': 'toggle',
'13': 'enter'
},
_keyHandler: function (e, notifier, args) {
if (!args.actions) {
var actObj = {collapse:true, expand:true, toggle:true, enter:true};
} else {
var actObj = args.actions;
}
if (this._keys[e.keyCode] && actObj[this._keys[e.keyCode]]) {
e.action = this._keys[e.keyCode];
notifier.fire(e);
}
},
on: function (node, sub, notifier) {
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
if (sub.args == null) {
//no actions given
sub._detacher = node.on(this._event, this._keyHandler,this, notifier, {actions:false});
} else {
sub._detacher = node.on(this._event, this._keyHandler,this, notifier, sub.args[0]);
}
},
detach: function (node, sub, notifier) {
//detach our _detacher handle of the subscription made in on()
sub._detacher.detach();
},
delegate: function (node, sub, notifier, filter) {
// subscribe to _event and ask keyHandler to handle with given args[0] (the desired actions).
if (sub.args == null) {
//no actions given
sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, {actions:false});
} else {
sub._delegateDetacher = node.delegate(this._event, this._keyHandler,filter, this, notifier, sub.args[0]);
}
},
detachDelegate: function (node, sub, notifier) {
sub._delegateDetacher.detach();
}
});
var EXPANSIONLIMIT_EVERYTHING = 0,
EXPANSIONLIMIT_COURSE = 20,
EXPANSIONLIMIT_SECTION = 30,
@ -36,6 +105,7 @@ TREE.prototype = {
// Delegate event to toggle expansion
var self = this;
Y.delegate('click', function(e){self.toggleExpansion(e);}, node.one('.block_tree'), '.tree_item.branch');
Y.delegate('actionkey', function(e){self.toggleExpansion(e);}, node.one('.block_tree'), '.tree_item.branch');
// Gather the expandable branches ready for initialisation.
var expansions = [];
@ -71,8 +141,8 @@ TREE.prototype = {
// First check if they managed to click on the li iteslf, then find the closest
// LI ancestor and use that
if (e.target.test('a')) {
// A link has been clicked don't fire any more events just do the default.
if (e.target.test('a') && (e.keyCode == 0 || e.keyCode == 13)) {
// A link has been clicked (or keypress is 'enter') don't fire any more events just do the default.
e.stopPropagation();
return;
}
@ -88,8 +158,22 @@ TREE.prototype = {
// Toggle expand/collapse providing its not a root level branch.
if (!target.hasClass('depth_1')) {
if (e.type == 'actionkey') {
switch (e.action) {
case 'expand' :
target.removeClass('collapsed');
break;
case 'collapse' :
target.addClass('collapsed');
break;
default :
target.toggleClass('collapsed');
}
e.halt();
} else {
target.toggleClass('collapsed');
}
}
// If the accordian feature has been enabled collapse all siblings.
if (this.get('accordian')) {
@ -152,9 +236,10 @@ BRANCH.prototype = {
*/
node : null,
/**
* A reference to the ajax load event handle when created.
* A reference to the ajax load event handlers when created.
*/
event_ajaxload : null,
event_ajaxload_actionkey : null,
/**
* Initialises the branch when it is first created.
*/
@ -193,8 +278,13 @@ BRANCH.prototype = {
var isbranch = (this.get('expandable') || this.get('haschildren'));
var branchli = Y.Node.create('<li></li>');
var link = this.get('link');
var branchp = Y.Node.create('<p class="tree_item"></p>').setAttribute('id', this.get('id'));
if (!link) {
//add tab focus if not link (so still one focus per menu node).
// it was suggested to have 2 foci. one for the node and one for the link in MDL-27428.
branchp.setAttribute('tabindex', '0');
}
if (isbranch) {
branchli.addClass('collapsed').addClass('contains_branch');
branchp.addClass('branch');
@ -220,7 +310,6 @@ BRANCH.prototype = {
}
}
var link = this.get('link');
if (!link) {
if (branchicon) {
branchp.appendChild(branchicon);
@ -253,6 +342,7 @@ BRANCH.prototype = {
}
if (this.get('expandable')) {
this.event_ajaxload = this.node.on('ajaxload|click', this.ajaxLoad, this);
this.event_ajaxload_actionkey = this.node.on('actionkey', this.ajaxLoad, this);
}
return this;
},
@ -274,7 +364,16 @@ BRANCH.prototype = {
* request made here.
*/
ajaxLoad : function(e) {
if (e.type == 'actionkey' && e.action != 'enter') {
e.halt();
} else {
e.stopPropagation();
}
if (e.type = 'actionkey' && e.action == 'enter' && e.target.test('A')) {
this.event_ajaxload_actionkey.detach();
this.event_ajaxload.detach();
return true; // no ajaxLoad for enter
}
if (this.node.hasClass('loadingbranch')) {
return true;
@ -307,6 +406,7 @@ BRANCH.prototype = {
ajaxProcessResponse : function(tid, outcome) {
this.node.removeClass('loadingbranch');
this.event_ajaxload.detach();
this.event_ajaxload_actionkey.detach();
try {
var object = Y.JSON.parse(outcome.responseText);
if (object.children && object.children.length > 0) {

View file

@ -13,7 +13,7 @@ if (empty($CFG->enablecalendarexport)) {
}
//Fetch user information
if (!$user = get_complete_user_data('username', $username)) {
if (!$user = $DB->get_record('user', array('username' => $username), 'id,password')) {
//No such user
die('Invalid authentication');
}

View file

@ -127,16 +127,14 @@ class comment {
*/
protected $totalcommentcount = null;
/**
* By default a user must have the generic comment capabilities plus any capabilities the
* component being commented on requires.
* When set to true only the component capabilities are checked, the system capabilities are
* ignored.
* This can be toggled by the component defining a callback in its lib.php e.g.
* function forum_comment_allow_anonymous_access(comment $comment) {}
* Note: On the front page this defaults to true.
* When set to true any user to the system is able to view comments.
*
* This can be set to true by a plugin by implementing a allow_anonymous_access callback.
* By default it is false except on the front page.
*
* @var bool
*/
protected $ignoresystempermissions = false;
protected $allowanonymousaccess = false;
/**#@+
* static variable will be used by non-js comments UI

View file

@ -428,7 +428,7 @@ abstract class moodleform_mod extends moodleform {
if (!empty($CFG->enableavailability)) {
// Conditional availability
$mform->addElement('header', '', get_string('availabilityconditions', 'condition'));
$mform->addElement('header', 'availabilityconditionsheader', get_string('availabilityconditions', 'condition'));
$mform->addElement('date_selector', 'availablefrom', get_string('availablefrom', 'condition'), array('optional'=>true));
$mform->addHelpButton('availablefrom', 'availablefrom', 'condition');
$mform->addElement('date_selector', 'availableuntil', get_string('availableuntil', 'condition'), array('optional'=>true));
@ -522,7 +522,7 @@ abstract class moodleform_mod extends moodleform {
$completion = new completion_info($COURSE);
}
if ($completion->is_enabled()) {
$mform->addElement('header', '', get_string('activitycompletion', 'completion'));
$mform->addElement('header', 'activitycompletionheader', get_string('activitycompletion', 'completion'));
// Unlock button for if people have completed it (will
// be removed in definition_after_data if they haven't)

View 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';

View 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"
>
@ -2667,8 +2667,10 @@
<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"/>
@ -2681,7 +2683,7 @@
<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">

View file

@ -1062,7 +1062,7 @@ abstract class enrol_plugin {
if (!is_null($status)) {
$ue->status = $status;
}
$ue->modifier = $USER->id;
$ue->modifierid = $USER->id;
$ue->timemodified = time();
$DB->update_record('user_enrolments', $ue);
}
@ -1073,7 +1073,7 @@ abstract class enrol_plugin {
$ue->userid = $userid;
$ue->timestart = $timestart;
$ue->timeend = $timeend;
$ue->modifier = $USER->id;
$ue->modifierid = $USER->id;
$ue->timecreated = time();
$ue->timemodified = $ue->timecreated;
$ue->id = $DB->insert_record('user_enrolments', $ue);

View file

@ -1074,6 +1074,8 @@ function get_fast_modinfo(&$course, $userid=0) {
if (count($cache) > MAX_MODINFO_CACHE_SIZE) {
reset($cache);
$key = key($cache);
unset($cache[$key]->instances);
unset($cache[$key]->cms);
unset($cache[$key]);
}

View file

@ -237,8 +237,8 @@ class navigation_node implements renderable {
}
/**
* Adds a navigation node as a child of this node.
*
* Creates a navigation node, ready to add it as a child using add_node
* function. (The created node needs to be added before you can use it.)
* @param string $text
* @param moodle_url|action_link $action
* @param int $type
@ -247,11 +247,8 @@ class navigation_node implements renderable {
* @param pix_icon $icon
* @return navigation_node
*/
public function add($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=null, $key=null, pix_icon $icon=null) {
// First convert the nodetype for this node to a branch as it will now have children
if ($this->nodetype !== self::NODETYPE_BRANCH) {
$this->nodetype = self::NODETYPE_BRANCH;
}
public static function create($text, $action=null, $type=self::TYPE_CUSTOM,
$shorttext=null, $key=null, pix_icon $icon=null) {
// Properties array used when creating the new navigation node
$itemarray = array(
'text' => $text,
@ -269,18 +266,58 @@ class navigation_node implements renderable {
if ($icon!==null) {
$itemarray['icon'] = $icon;
}
// Default the key to the number of children if not provided
if ($key === null) {
$key = $this->children->count();
}
// Set the key
$itemarray['key'] = $key;
// Construct and return
return new navigation_node($itemarray);
}
/**
* Adds a navigation node as a child of this node.
*
* @param string $text
* @param moodle_url|action_link $action
* @param int $type
* @param string $shorttext
* @param string|int $key
* @param pix_icon $icon
* @return navigation_node
*/
public function add($text, $action=null, $type=self::TYPE_CUSTOM, $shorttext=null, $key=null, pix_icon $icon=null) {
// Create child node
$childnode = self::create($text, $action, $type, $shorttext, $key, $icon);
// Add the child to end and return
return $this->add_node($childnode);
}
/**
* Adds a navigation node as a child of this one, given a $node object
* created using the create function.
* @param navigation_node $childnode Node to add
* @param int|string $key The key of a node to add this before. If not
* specified, adds at end of list
* @return navigation_node The added node
*/
public function add_node(navigation_node $childnode, $beforekey=null) {
// First convert the nodetype for this node to a branch as it will now have children
if ($this->nodetype !== self::NODETYPE_BRANCH) {
$this->nodetype = self::NODETYPE_BRANCH;
}
// Set the parent to this node
$itemarray['parent'] = $this;
$childnode->parent = $this;
// Default the key to the number of children if not provided
if ($childnode->key === null) {
$childnode->key = $this->children->count();
}
// Add the child using the navigation_node_collections add method
$node = $this->children->add(new navigation_node($itemarray));
// If the node is a category node or the user is logged in and its a course
// then mark this node as a branch (makes it expandable by AJAX)
$node = $this->children->add($childnode, $beforekey);
// If added node is a category node or the user is logged in and it's a course
// then mark added node as a branch (makes it expandable by AJAX)
$type = $childnode->type;
if (($type==self::TYPE_CATEGORY) || (isloggedin() && $type==self::TYPE_COURSE)) {
$node->nodetype = self::NODETYPE_BRANCH;
}
@ -288,7 +325,7 @@ class navigation_node implements renderable {
if ($this->hidden) {
$node->hidden = true;
}
// Return the node (reference returned by $this->children->add()
// Return added node (reference returned by $this->children->add()
return $node;
}
@ -647,13 +684,16 @@ class navigation_node_collection implements IteratorAggregate {
/**
* Adds a navigation node to the collection
*
* @param navigation_node $node
* @return navigation_node
* @param navigation_node $node Node to add
* @param string $beforekey If specified, adds before a node with this key,
* otherwise adds at end
* @return navigation_node Added node
*/
public function add(navigation_node $node) {
public function add(navigation_node $node, $beforekey=null) {
global $CFG;
$key = $node->key;
$type = $node->type;
// First check we have a 2nd dimension for this type
if (!array_key_exists($type, $this->orderedcollection)) {
$this->orderedcollection[$type] = array();
@ -662,15 +702,50 @@ class navigation_node_collection implements IteratorAggregate {
if ($CFG->debug && array_key_exists($key, $this->orderedcollection[$type])) {
debugging('Navigation node intersect: Adding a node that already exists '.$key, DEBUG_DEVELOPER);
}
// Add the node to the appropriate place in the ordered structure.
// Find the key to add before
$newindex = $this->count;
$last = true;
if ($beforekey !== null) {
foreach ($this->collection as $index => $othernode) {
if ($othernode->key === $beforekey) {
$newindex = $index;
$last = false;
break;
}
}
if ($newindex === $this->count) {
$allkeys = '';
foreach ($this->collection as $othernode) {
$allkeys .= ' ' . $othernode->key;
}
debugging('Navigation node add_before: Reference node not found ' . $beforekey .
', options: ' . $allkeys, DEBUG_DEVELOPER);
}
}
// Add the node to the appropriate place in the by-type structure (which
// is not ordered, despite the variable name)
$this->orderedcollection[$type][$key] = $node;
if (!$last) {
// Update existing references in the ordered collection (which is the
// one that isn't called 'ordered') to shuffle them along if required
for ($oldindex = $this->count; $oldindex > $newindex; $oldindex--) {
$this->collection[$oldindex] = $this->collection[$oldindex - 1];
}
}
// Add a reference to the node to the progressive collection.
$this->collection[$this->count] = &$this->orderedcollection[$type][$key];
$this->collection[$newindex] = &$this->orderedcollection[$type][$key];
// Update the last property to a reference to this new node.
$this->last = &$this->orderedcollection[$type][$key];
// Reorder the array by index if needed
if (!$last) {
ksort($this->collection);
}
$this->count++;
// Return the reference to the now added node
return $this->last;
return $node;
}
/**
@ -1702,7 +1777,14 @@ class global_navigation extends navigation_node {
// Add a reports tab and then add reports the the user has permission to see.
$anyreport = has_capability('moodle/user:viewuseractivitiesreport', $usercontext);
$viewreports = ($anyreport || ($course->showreports && $iscurrentuser && $forceforcontext));
$outlinetreport = ($anyreport || has_capability('coursereport/outline:view', $coursecontext));
$logtodayreport = ($anyreport || has_capability('coursereport/log:viewtoday', $coursecontext));
$logreport = ($anyreport || has_capability('coursereport/log:view', $coursecontext));
$statsreport = ($anyreport || has_capability('coursereport/stats:view', $coursecontext));
$somereport = $outlinetreport || $logtodayreport || $logreport || $statsreport;
$viewreports = ($anyreport || $somereport || ($course->showreports && $iscurrentuser && $forceforcontext));
if ($viewreports) {
$reporttab = $usernode->add(get_string('activityreports'));
$reportargs = array('user'=>$user->id);
@ -1711,21 +1793,21 @@ class global_navigation extends navigation_node {
} else {
$reportargs['id'] = SITEID;
}
if ($viewreports || has_capability('coursereport/outline:view', $coursecontext)) {
if ($viewreports || $outlinetreport) {
$reporttab->add(get_string('outlinereport'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'outline'))));
$reporttab->add(get_string('completereport'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'complete'))));
}
if ($viewreports || has_capability('coursereport/log:viewtoday', $coursecontext)) {
if ($viewreports || $logtodayreport) {
$reporttab->add(get_string('todaylogs'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'todaylogs'))));
}
if ($viewreports || has_capability('coursereport/log:view', $coursecontext)) {
if ($viewreports || $logreport ) {
$reporttab->add(get_string('alllogs'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'alllogs'))));
}
if (!empty($CFG->enablestats)) {
if ($viewreports || has_capability('coursereport/stats:view', $coursecontext)) {
if ($viewreports || $statsreport) {
$reporttab->add(get_string('stats'), new moodle_url('/course/user.php', array_merge($reportargs, array('mode'=>'stats'))));
}
}

View 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) {
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 ($rating->user_can_view_aggregate()) {
//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;
}
$aggregatelabel = $ratingmanager->get_aggregate_label($rating->settings->aggregationmethod);
$aggregatestr = $rating->get_aggregate_string();
//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');
//$scalemax = 0;//no longer displaying scale max
$aggregatestr = '';
//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);
}
} else {
$aggregatestr = '';
}
$countstr = html_writer::start_tag('span', array('id'=>"ratingcount{$rating->itemid}"));
$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) {
$countstr .= "({$rating->count})";
$aggregatehtml .= "({$rating->count})";
} else {
$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);
@ -1510,79 +1453,43 @@ 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) {
if ($rating->user_can_rate()) {
$rateurl = $rating->get_rate_url();
$inputs = $rateurl->params();
//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"));
$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
$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);
$attributes['name'] = 'returnurl';
$attributes['value'] = $rating->settings->returnurl;
$formstart .= html_writer::empty_tag('input', $attributes);
$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();;
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')));
$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');
@ -2424,7 +2331,7 @@ EOD;
$content = $icon.$content; // use CSS for spacing of icons
}
if ($item->helpbutton !== null) {
$content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton'));
$content = trim($item->helpbutton).html_writer::tag('span', $content, array('class'=>'clearhelpbutton', 'tabindex'=>'0'));
}
if ($content === '') {
return '';
@ -2447,7 +2354,7 @@ EOD;
$content = html_writer::link($item->action, $content, $attributes);
} else if (is_string($item->action) || empty($item->action)) {
$attributes = array();
$attributes = array('tabindex'=>'0'); //add tab support to span but still maintain character stream sequence.
if ($title !== '') {
$attributes['title'] = $title;
}

View file

@ -347,10 +347,11 @@ class plugin_manager {
),
'theme' => array(
'anomaly', 'arialist', 'base', 'binarius', 'boxxie', 'brick',
'canvas', 'formal_white', 'formfactor', 'fusion',
'leatherbound', 'magazine', 'nimble', 'nonzero', 'overlay',
'serenity', 'sky_high', 'splash', 'standard', 'standardold'
'afterburner', 'anomaly', 'arialist', 'base', 'binarius',
'boxxie', 'brick', 'canvas', 'formal_white', 'formfactor',
'fusion', 'leatherbound', 'magazine', 'nimble', 'nonzero',
'overlay', 'serenity', 'sky_high', 'splash', 'standard',
'standardold'
),
'webservice' => array(

View 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

View file

@ -98,6 +98,28 @@ class navigation_node_test extends UnitTestCase {
$this->assertReference($node3, $this->node->get($node3->key, $node3->type));
}
public function test_add_before() {
global $CFG;
// Create 3 nodes
$node1 = navigation_node::create('test_add_1', null, navigation_node::TYPE_CUSTOM,
'test 1', 'testadd1');
$node2 = navigation_node::create('test_add_2', null, navigation_node::TYPE_CUSTOM,
'test 2', 'testadd2');
$node3 = navigation_node::create('test_add_3', null, navigation_node::TYPE_CUSTOM,
'test 3', 'testadd3');
// Add node 2, then node 1 before 2, then node 3 at end
$this->node->add_node($node2);
$this->node->add_node($node1, 'testadd2');
$this->node->add_node($node3);
// Check the last 3 nodes are in 1, 2, 3 order and have those indexes
foreach($this->node->children as $child) {
$keys[] = $child->key;
}
$this->assertEqual('testadd1', $keys[count($keys)-3]);
$this->assertEqual('testadd2', $keys[count($keys)-2]);
$this->assertEqual('testadd3', $keys[count($keys)-1]);
}
public function test_add_class() {
$node = $this->node->get('demo1');
$this->assertIsA($node, 'navigation_node');

View file

@ -1333,7 +1333,7 @@ function stats_get_report_options($courseid,$mode) {
case STATS_MODE_GENERAL:
$reportoptions[STATS_REPORT_ACTIVITY] = get_string('statsreport'.STATS_REPORT_ACTIVITY);
if ($courseid != SITEID && $context = get_context_instance(CONTEXT_COURSE, $courseid)) {
$sql = 'SELECT r.id, r.name FROM {role} r JOIN {stats_daily} s ON s.roleid = r.id WHERE s.courseid = :courseid GROUP BY s.roleid';
$sql = 'SELECT r.id, r.name FROM {role} r JOIN {stats_daily} s ON s.roleid = r.id WHERE s.courseid = :courseid GROUP BY r.id, r.name';
if ($roles = $DB->get_records_sql($sql, array('courseid' => $courseid))) {
foreach ($roles as $role) {
$reportoptions[STATS_REPORT_ACTIVITYBYROLE.$role->id] = get_string('statsreport'.STATS_REPORT_ACTIVITYBYROLE). ' '.$role->name;

View file

@ -152,7 +152,7 @@ function profiling_start() {
* Stop profiling, gathering results and storing them
*/
function profiling_stop() {
global $CFG, $SCRIPT;
global $CFG, $DB, $SCRIPT;
// If profiling isn't available, nothing to stop
if (!extension_loaded('xhprof') || !function_exists('xhprof_enable')) {
@ -176,6 +176,14 @@ function profiling_stop() {
profiling_is_running(false);
$data = xhprof_disable();
// We only save the run after ensuring the DB table exists
// (this prevents problems with profiling runs enabled in
// config.php before Moodle is installed. Rare but...
$tables = $DB->get_tables();
if (!in_array('profiling', $tables)) {
return false;
}
$run = new moodle_xhprofrun();
$run->prepare_run($script);
$runid = $run->save_run($data, null);

View file

@ -200,6 +200,8 @@ function message_get_blocked_users($user1=null, $user2=null) {
$user2->isblocked = false;
}
$blockedusers = array();
$userfields = user_picture::fields('u', array('lastaccess'));
$blockeduserssql = "SELECT $userfields, COUNT(m.id) AS messagecount
FROM {message_contacts} mc
@ -210,8 +212,6 @@ function message_get_blocked_users($user1=null, $user2=null) {
ORDER BY u.firstname ASC";
$rs = $DB->get_recordset_sql($blockeduserssql, array('user1id1' => $user1->id, 'user1id2' => $user1->id));
$blockedusers = array();
if (!empty($rs)) {
foreach($rs as $rd) {
$blockedusers[] = $rd;
@ -219,9 +219,7 @@ function message_get_blocked_users($user1=null, $user2=null) {
$user2->isblocked = true;
}
}
unset($rd);
$rs->close();
}
return $blockedusers;
}
@ -1968,6 +1966,8 @@ function message_post_message($userfrom, $userto, $message, $format) {
* Returns a list of all user ids who have used messaging in the site
* This was the simple way to code the SQL ... is it going to blow up
* on large datasets?
*
* @todo: deprecated - to be deleted in 2.2
*/
function message_get_participants() {
global $CFG, $DB;
@ -2129,6 +2129,8 @@ function message_mark_messages_read($touserid, $fromuserid){
foreach ($messages as $message) {
message_mark_message_read($message, time());
}
$messages->close();
}
/**

View file

@ -144,12 +144,13 @@ $string['noblogs'] = 'You have no blog entries to submit!';
$string['nofiles'] = 'No files were submitted';
$string['nofilesyet'] = 'No files submitted yet';
$string['nomoresubmissions'] = 'No further submissions are allowed.';
$string['nosubmitusers'] = 'No users were found with permissions to submit this assignment';
$string['notavailableyet'] = 'Sorry, this assignment is not yet available.<br />Assignment instructions will be displayed here on the date given below.';
$string['notes'] = 'Notes';
$string['notesempty'] = 'No entry';
$string['notesupdateerror'] = 'Error when updating notes';
$string['notgradedyet'] = 'Not graded yet';
$string['norequiregrading'] = 'There are no assignments that require grading';
$string['nosubmisson'] = 'No assignments have been submit';
$string['notsubmittedyet'] = 'Not submitted yet';
$string['onceassignmentsent'] = 'Once the assignment is sent for marking, you will no longer be able to delete or attach file(s). Do you want to continue?';
$string['operation'] = 'Operation';

View file

@ -1128,6 +1128,7 @@ class assignment_base {
$course = $this->course;
$assignment = $this->assignment;
$cm = $this->cm;
$hassubmission = false;
$tabindex = 1; //tabindex for quick grading tabbing; Not working for dropdowns yet
add_to_log($course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->cm->id, $this->assignment->id, $this->cm->id);
@ -1264,16 +1265,7 @@ class assignment_base {
// Start working -- this is necessary as soon as the niceties are over
$table->setup();
if (empty($users)) {
echo $OUTPUT->heading(get_string('nosubmitusers','assignment'));
echo '</div>';
return true;
}
if ($this->assignment->assignmenttype=='upload' || $this->assignment->assignmenttype=='online' || $this->assignment->assignmenttype=='uploadsingle') { //TODO: this is an ugly hack, where is the plugin spirit? (skodak)
echo '<div style="text-align:right"><a href="submissions.php?id='.$this->cm->id.'&amp;download=zip">'.get_string('downloadall', 'assignment').'</a></div>';
}
/// Construct the SQL
list($where, $params) = $table->get_sql_where();
if ($where) {
$where .= ' AND ';
@ -1290,7 +1282,7 @@ class assignment_base {
}
$ufields = user_picture::fields('u');
if (!empty($users)) {
$select = "SELECT $ufields,
s.id AS submissionid, s.grade, s.submissioncomment,
s.timemodified, s.timemarked,
@ -1333,6 +1325,7 @@ class assignment_base {
}
if (!empty($auser->submissionid)) {
$hassubmission = true;
///Prints student answer and student modified date
///attach file or print link to student answer, depending on the type of the assignment.
///Refer to print_student_answer in inherited classes.
@ -1388,11 +1381,13 @@ class assignment_base {
if ($final_grade->locked or $final_grade->overridden) {
$grade = '<div id="g'.$auser->id.'">'.$final_grade->formatted_grade . '</div>';
$hassubmission = true;
} else if ($quickgrade) { // allow editing
$attributes = array();
$attributes['tabindex'] = $tabindex++;
$menu = html_writer::select(make_grades_menu($this->assignment->grade), 'menu['.$auser->id.']', $auser->grade, array(-1=>get_string('nograde')), $attributes);
$grade = '<div id="g'.$auser->id.'">'.$menu.'</div>';
$hassubmission = true;
} else {
$grade = '<div id="g'.$auser->id.'">-</div>';
}
@ -1452,17 +1447,27 @@ class assignment_base {
if ($uses_outcomes) {
$row[] = $outcomes;
}
$table->add_data($row);
}
$currentposition++;
}
}
if ($hassubmission && ($this->assignment->assignmenttype=='upload' || $this->assignment->assignmenttype=='online' || $this->assignment->assignmenttype=='uploadsingle')) { //TODO: this is an ugly hack, where is the plugin spirit? (skodak)
echo html_writer::start_tag('div', array('class' => 'mod-assignment-download-link'));
echo html_writer::link(new moodle_url('/mod/assignment/submissions.php', array('id' => $this->cm->id, 'download' => 'zip')), get_string('downloadall', 'assignment'));
echo html_writer::end_tag('div');
}
$table->print_html(); /// Print the whole table
} else {
if ($filter == self::FILTER_SUBMITTED) {
echo html_writer::tag('div', get_string('nosubmisson', 'assignment'), array('class'=>'nosubmisson'));
} else if ($filter == self::FILTER_REQUIRE_GRADING) {
echo html_writer::tag('div', get_string('norequiregrading', 'assignment'), array('class'=>'norequiregrading'));
}
}
/// Print quickgrade form around the table
if ($quickgrade && $table->started_output){
if ($quickgrade && $table->started_output && !empty($users)){
$mailinfopref = false;
if (get_user_preferences('assignment_mailinfo', 1)) {
$mailinfopref = true;
@ -2767,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
*/

View file

@ -23,6 +23,7 @@
#page-mod-assignment-submissions .submissions .grade,
#page-mod-assignment-submissions .submissions .outcome,
#page-mod-assignment-submissions .submissions .finalgrade {text-align: right;}
#page-mod-assignment-submissions .submissions .header.noheader {display:none;}
#page-mod-assignment-submissions .qgprefs #optiontable {text-align:right;margin-left:auto;}
/** Styles for view.php **/
@ -43,3 +44,5 @@
#page-mod-assignment-submissions.dir-rtl .timemarked {text-align:right;}
#page-mod-assignment-submissions.dir-rtl .mform.optionspref .fitem .fitemtitle {text-align:left;}
#page-mod-assignment-type-uploadsingle-upload.dir-rtl .mdl-left {text-align:right;}
.mod-assignment-download-link {text-align:right;}

View 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

View file

@ -39,7 +39,7 @@ class backup_choice_activity_structure_step extends backup_activity_structure_st
// Define each element separated
$choice = new backup_nested_element('choice', array('id'), array(
'name', 'intro', 'introformat', 'publish',
'showresults', 'display', 'allowupdate', 'allowunanswered',
'showresults', 'display', 'allowupdate', 'showunanswered',
'limitanswers', 'timeopen', 'timeclose', 'timemodified',
'completionsubmit'));

View 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
*/

View file

@ -40,7 +40,7 @@ class mod_choice_renderer extends plugin_renderer_base {
$layoutclass = 'vertical';
}
$target = new moodle_url('/mod/choice/view.php');
$attributes = array('method'=>'POST', 'target'=>$target, 'class'=> $layoutclass);
$attributes = array('method'=>'POST', 'action'=>$target, 'class'=> $layoutclass);
$html = html_writer::start_tag('form', $attributes);
$html .= html_writer::start_tag('ul', array('class'=>'choices' ));

View 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);
@ -100,7 +100,9 @@ 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));
'itemid' => backup::VAR_PARENTID,
'component' => 'mod_data',
'ratingarea' => 'entry'));
$rating->set_source_alias('rating', 'value');
}

View 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);
}

View 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;
}

View 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;
}
@ -962,7 +962,16 @@ function data_user_outline($course, $user, $mod, $data) {
} else if ($grade) {
$result = new stdClass();
$result->info = get_string('grade') . ': ' . $grade->str_long_grade;
//datesubmitted == time created. dategraded == time modified or time overridden
//if grade was last modified by the user themselves use date graded. Otherwise use date submitted
//TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
$result->time = $grade->dategraded;
} else {
$result->time = $grade->datesubmitted;
}
return $result;
}
return NULL;
@ -1007,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;
@ -1019,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);
}
@ -1135,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));
$params = array('dataid' => $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));
$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);
$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));
$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();
@ -1348,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)
);
}
/**
@ -1378,11 +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)) {
throw new rating_exception('missingparameter');
// Check the component is mod_data
if ($params['component'] != 'mod_data') {
throw new rating_exception('invalidcomponent');
}
$datasql = "SELECT d.id as did, d.course, r.userid as userid, d.approval, r.approved, r.timecreated, d.assesstimestart, d.assesstimefinish, r.groupid
// Check the ratingarea is entry (the only rating area in data module)
if ($params['ratingarea'] != 'entry') {
throw new rating_exception('invalidratingarea');
}
// 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";
@ -1392,14 +1438,33 @@ function data_rating_validate($params) {
throw new rating_exception('invaliditemid');
}
if ($info->userid == $USER->id) {
//user is attempting to rate their own glossary entry
throw new rating_exception('nopermissiontorate');
if ($info->scale != $params['scaleid']) {
//the scale being submitted doesnt match the one in the database
throw new rating_exception('invalidscaleid');
}
if ($params['rateduserid'] != $info->userid) {
//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
// lower limit
if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) {
throw new rating_exception('invalidnum');
}
// upper limit
if ($info->scale < 0) {
//its a custom scale
$scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
if ($scalerecord) {
$scalearray = explode(',', $scalerecord->scale);
if ($params['rating'] > count($scalearray)) {
throw new rating_exception('invalidnum');
}
} else {
throw new rating_exception('invalidscaleid');
}
} else if ($params['rating'] > $info->scale) {
//if its numeric and submitted rating is above maximum
throw new rating_exception('invalidnum');
}
if ($info->approval && !$info->approved) {
@ -1414,23 +1479,17 @@ function data_rating_validate($params) {
}
}
$dataid = $info->did;
$groupid = $info->groupid;
$courseid = $info->course;
$cm = get_coursemodule_from_instance('data', $dataid);
if (empty($cm)) {
throw new rating_exception('unknowncontext');
}
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
$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);
// if the supplied context doesnt match the item's context
if (empty($context) || $context->id != $params['context']->id) {
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
@ -2487,7 +2546,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)) {

View 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;

View file

@ -669,9 +669,10 @@ if ($showactivity) {
//attach ratings to data records
require_once($CFG->dirroot.'/rating/lib.php');
if ($data->assessed != RATING_AGGREGATE_NONE) {
$ratingoptions = new stdclass();
$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;

View file

@ -27,6 +27,9 @@ $messageproviders = array (
/// Submitting a feedback
'submission' => array (
),
/// Message to nonrespondents
'message' => array (
)
);

View file

@ -467,6 +467,7 @@ function feedback_cron () {
}
/**
* @todo: deprecated - to be deleted in 2.2
* @return bool false
*/
function feedback_get_participants($feedbackid) {

View file

@ -75,8 +75,8 @@
foreach ($messageuser as $userid) {
$senduser = $DB->get_record('user', array('id'=>$userid));
$eventdata = new stdClass();
$eventdata->name = 'feedback';
$eventdata->component = 'mod';
$eventdata->name = 'message';
$eventdata->component = 'mod_feedback';
$eventdata->userfrom = $USER;
$eventdata->userto = $senduser;
$eventdata->subject = $subject;

View file

@ -9,7 +9,7 @@
*/
$module->version = 2010112302; // The current module version (Date: YYYYMMDDXX)
$module->version = 2011051600; // The current module version (Date: YYYYMMDDXX)
$module->requires = 2010080300; // Requires this Moodle version
$feedback_version_intern = 1; //this version is used for restore older backups
$module->cron = 0; // Period for cron to check this module (secs)

View 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
*/

View 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');
@ -126,6 +126,8 @@ 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,
'component' => 'mod_forum',
'ratingarea' => 'post',
'itemid' => backup::VAR_PARENTID));
$rating->set_source_alias('rating', 'value');
}

View 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);
}

View 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;
}

View file

@ -1131,7 +1131,16 @@ function forum_user_outline($course, $user, $mod, $forum) {
} else if ($grade) {
$result = new stdClass();
$result->info = get_string('grade') . ': ' . $grade->str_long_grade;
//datesubmitted == time created. dategraded == time modified or time overridden
//if grade was last modified by the user themselves use date graded. Otherwise use date submitted
//TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
$result->time = $grade->dategraded;
} else {
$result->time = $grade->datesubmitted;
}
return $result;
}
return NULL;
@ -1440,21 +1449,21 @@ 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);
}
@ -1577,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
*/
@ -1586,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
$sql = "SELECT DISTINCT u.id, u.id
FROM {user} u,
{forum_subscriptions} s
WHERE s.forum = ? AND
u.id = s.userid", array($forumid));
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
$sql = "SELECT DISTINCT u.id, u.id
FROM {user} u,
{forum_discussions} d,
{forum_posts} p
WHERE d.forum = ? AND
WHERE d.forum = :forumid AND
p.discussion = d.id AND
u.id = p.userid", array($forumid));
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) {
@ -2041,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;
$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
@ -2447,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
$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
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 = ?
AND p.id = r.itemid
AND r.userid = ?", array($discussionid, $userid))) {
$difference = $posts->num - $rated->num;
if ($difference > 0) {
return $difference;
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
}
@ -3442,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]
@ -3470,79 +3496,82 @@ 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)) {
throw new rating_exception('missingparameter');
// Check the component is mod_forum
if ($params['component'] != 'mod_forum') {
throw new rating_exception('invalidcomponent');
}
$forumsql = "SELECT f.id as fid, f.course, 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 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 ($params['rateduserid'] != $info->userid) {
//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($info->assesstimestart) && !empty($info->assesstimefinish)) {
if ($info->timecreated < $info->assesstimestart || $info->timecreated > $info->assesstimefinish) {
if (!empty($forum->assesstimestart) && !empty($forum->assesstimefinish)) {
if ($post->created < $forum->assesstimestart || $post->created > $forum->assesstimefinish) {
throw new rating_exception('notavailable');
}
}
$forumid = $info->fid;
$discussionid = $info->did;
$groupid = $info->groupid;
$courseid = $info->course;
//check that the submitted rating is valid for the scale
$cm = get_coursemodule_from_instance('forum', $forumid);
if (empty($cm)) {
throw new rating_exception('unknowncontext');
// lower limit
if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) {
throw new rating_exception('invalidnum');
}
$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');
// upper limit
if ($forum->scale < 0) {
//its a custom scale
$scalerecord = $DB->get_record('scale', array('id' => -$forum->scale));
if ($scalerecord) {
$scalearray = explode(',', $scalerecord->scale);
if ($params['rating'] > count($scalearray)) {
throw new rating_exception('invalidnum');
}
} else {
throw new rating_exception('invalidscaleid');
}
} else if ($params['rating'] > $forum->scale) {
//if its numeric and submitted rating is above maximum
throw new rating_exception('invalidnum');
}
// 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)) {
throw new rating_exception('nopermissiontorate');
@ -4314,8 +4343,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);
@ -5350,32 +5381,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);
@ -5384,7 +5412,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();
@ -5418,9 +5446,10 @@ function forum_print_discussion($course, $cm, $forum, $discussion, $post, $mode,
//load ratings
if ($forum->assessed != RATING_AGGREGATE_NONE) {
$ratingoptions = new stdclass();
$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;
@ -7094,10 +7123,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) {
@ -7106,7 +7139,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)) {
@ -7154,8 +7186,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)) {

View 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];
}
$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;
if ($forum->type == 'single' or !$discussion->id) {
$ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/view.php?id={$forum->cm->id}";
} else {
$ratingoptions->returnurl = "$CFG->wwwroot/mod/forum/discuss.php?d=$discussion->id";
}
$ratingoptions->assesstimestart = $forum->assesstimestart;
$ratingoptions->assesstimefinish = $forum->assesstimefinish;
if ($forum->type == 'single' or !$discussion->id) {
$ratingoptions->returnurl = $forumurl;
} else {
$ratingoptions->returnurl = $discussionurl;
}
$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
}
$fullsubject = "<a href=\"view.php?f=$forum->id\">".format_string($forum->name,true)."</a>";
if ($forum->type != 'single') {
$fullsubject .= " -> <a href=\"discuss.php?d=$discussion->id\">".format_string($discussion->name,true)."</a>";
if ($post->parent != 0) {
$fullsubject .= " -> <a href=\"discuss.php?d=$post->discussion&amp;parent=$post->id\">".format_string($post->subject,true)."</a>";
}
}
$fullsubjects = array();
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;
$courseurl = new moodle_url('/course/view.php', array('id' => $forum->course));
$fullsubjects[] = html_writer::link($courseurl, $postcoursename);
}
$fullsubjects[] = html_writer::link($forumurl, format_string($forum->name, true));
if ($forum->type != 'single') {
$fullsubjects[] .= html_writer::link($discussionurl, format_string($discussion->name, true));
if ($post->parent != 0) {
$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));
}
}
$post->subject = $fullsubject;
$post->subject = join(' -> ', $fullsubjects);
$discussionurl->set_anchor('p'.$post->id);
$fulllink = html_writer::link($discussionurl, get_string("postincontext", "forum"));
$fulllink = "<a href=\"discuss.php?d=$post->discussion#p$post->id\">".
get_string("postincontext", "forum")."</a>";
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 />";
}

View 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->version = 2011052300;
$module->requires = 2011052300; // Requires this Moodle version
$module->cron = 60;

View file

@ -102,10 +102,6 @@
$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);

View 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');
@ -102,7 +102,9 @@ class backup_glossary_activity_structure_step extends backup_activity_structure_
$alias->set_source_alias('alias', 'alias_text');
$rating->set_source_table('rating', array('contextid' => backup::VAR_CONTEXTID,
'itemid' => backup::VAR_PARENTID));
'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));

View 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);
}

View 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;
}

View 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);

View 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);
@ -243,7 +242,16 @@ function glossary_user_outline($course, $user, $mod, $glossary) {
} else if ($grade) {
$result = new stdClass();
$result->info = get_string('grade') . ': ' . $grade->str_long_grade;
//datesubmitted == time created. dategraded == time modified or time overridden
//if grade was last modified by the user themselves use date graded. Otherwise use date submitted
//TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
$result->time = $grade->dategraded;
} else {
$result->time = $grade->datesubmitted;
}
return $result;
}
return NULL;
@ -436,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;
@ -450,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]
@ -484,28 +502,59 @@ 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)) {
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, 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)) {
$info = $DB->get_record_sql($glossarysql, $glossaryparams);
if (!$info) {
//item doesn't exist
throw new rating_exception('invaliditemid');
}
if ($info->userid == $USER->id) {
//user is attempting to rate their own glossary entry
throw new rating_exception('nopermissiontorate');
if ($info->scale != $params['scaleid']) {
//the scale being submitted doesnt match the one in the database
throw new rating_exception('invalidscaleid');
}
if ($params['rateduserid'] != $info->userid) {
//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
// lower limit
if ($params['rating'] < 0 && $params['rating'] != RATING_UNSET_RATING) {
throw new rating_exception('invalidnum');
}
// upper limit
if ($info->scale < 0) {
//its a custom scale
$scalerecord = $DB->get_record('scale', array('id' => -$info->scale));
if ($scalerecord) {
$scalearray = explode(',', $scalerecord->scale);
if ($params['rating'] > count($scalearray)) {
throw new rating_exception('invalidnum');
}
} else {
throw new rating_exception('invalidscaleid');
}
} else if ($params['rating'] > $info->scale) {
//if its numeric and submitted rating is above maximum
throw new rating_exception('invalidnum');
}
if (!$info->approved) {
@ -520,16 +569,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 ($context->id != $params['context']->id) {
throw new rating_exception('invalidcontext');
}
@ -647,7 +691,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
*/
@ -2154,31 +2199,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
$sql = "SELECT COUNT('x') as num
FROM {glossary_entries}
WHERE glossaryid = ? AND userid <> ?", array($glossaryid, $userid))) {
WHERE glossaryid = :glossaryid AND
userid <> :userid";
$params = array('glossaryid' => $glossaryid, 'userid' => $userid);
$entries = $DB->count_records_sql($sql, $params);
if (!$cm = get_coursemodule_from_instance('glossary', $glossaryid)) {
return 0;
}
$context = get_context_instance(CONTEXT_MODULE, $cm->id);
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));
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;
// 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
}
@ -2430,7 +2493,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)

View 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)

View 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();
$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;

View 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
*/

View 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) {

View file

@ -46,41 +46,59 @@ if ($mode !== 'display') {
}
$PAGE->set_url($url);
$attempt = new stdClass();
$user = new stdClass();
$attemptid = optional_param('attemptid', 0, PARAM_INT);
if ($attemptid > 0) {
$attempt = $DB->get_record('lesson_attempts', array('id' => $attemptid));
$answer = $DB->get_record('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $attempt->pageid));
$user = $DB->get_record('user', array('id' => $attempt->userid));
$scoreoptions = array();
if ($lesson->custom) {
$i = $answer->score;
while ($i >= 0) {
$scoreoptions[$i] = (string)$i;
$i--;
}
} else {
$scoreoptions[0] = get_string('nocredit', 'lesson');
$scoreoptions[1] = get_string('credit', 'lesson');
}
}
/// Handle any preprocessing before header is printed - based on $mode
switch ($mode) {
case 'grade':
// Grading form - get the necessary data
require_sesskey();
$attemptid = required_param('attemptid', PARAM_INT);
if (!$attempt = $DB->get_record('lesson_attempts', array('id' => $attemptid))) {
if (empty($attempt)) {
print_error('cannotfindattempt', 'lesson');
}
$page = $lesson->load_page($attempt->pageid);
if (!$user = $DB->get_record('user', array('id' => $attempt->userid))) {
if (empty($user)) {
print_error('cannotfinduser', 'lesson');
}
if (!$answer = $DB->get_record('lesson_answers', array('lessonid' => $lesson->id, 'pageid' => $page->id))) {
if (empty($answer)) {
print_error('cannotfindanswer', 'lesson');
}
break;
case 'update':
require_sesskey();
$mform = new essay_grading_form();
if ($form = $mform->get_data()) {
if (optional_param('cancel', false, PARAM_RAW)) {
redirect("$CFG->wwwroot/mod/lesson/essay.php?id=$cm->id");
}
$attemptid = required_param('attemptid', PARAM_INT);
$score = optional_param('score', 0, PARAM_INT);
if (!$attempt = $DB->get_record('lesson_attempts', array('id' => $attemptid))) {
if (empty($attempt)) {
print_error('cannotfindattempt', 'lesson');
}
if (empty($user)) {
print_error('cannotfinduser', 'lesson');
}
$mform = new essay_grading_form(null, array('scoreoptions'=>$scoreoptions, 'user'=>$user));
if ($mform->is_cancelled()) {
redirect("$CFG->wwwroot/mod/lesson/essay.php?id=$cm->id");
}
if ($form = $mform->get_data()) {
if (!$grades = $DB->get_records('lesson_grades', array("lessonid"=>$lesson->id, "userid"=>$attempt->userid), 'completed', '*', $attempt->retry, 1)) {
print_error('cannotfindgrade', 'lesson');
}
@ -89,7 +107,7 @@ switch ($mode) {
$essayinfo = unserialize($attempt->useranswer);
$essayinfo->graded = 1;
$essayinfo->score = $score;
$essayinfo->score = $form->score;
$essayinfo->response = clean_param($form->response, PARAM_RAW);
$essayinfo->sent = 0;
if (!$lesson->custom && $essayinfo->score == 1) {
@ -368,22 +386,9 @@ switch ($mode) {
case 'grade':
// Grading form
// Expects the following to be set: $attemptid, $answer, $user, $page, $attempt
$essayinfo = unserialize($attempt->useranswer);
$options = array();
if ($lesson->custom) {
$i = $answer->score;
while ($i >= 0) {
$options[$i] = (string)$i;
$i--;
}
} else {
$options[0] = get_string('nocredit', 'lesson');
$options[1] = get_string('credit', 'lesson');
}
$mform = new essay_grading_form(null, array('scoreoptions'=>$options, 'user'=>$user));
$mform = new essay_grading_form(null, array('scoreoptions'=>$scoreoptions, 'user'=>$user));
$data = new stdClass;
$data->id = $cm->id;
$data->attemptid = $attemptid;

View file

@ -166,8 +166,15 @@ function lesson_user_outline($course, $user, $mod, $lesson) {
} else {
$grade = reset($grades->items[0]->grades);
$return->info = get_string("grade") . ': ' . $grade->str_long_grade;
$return->time = $grade->dategraded;
$return->info = get_string("no")." ".get_string("attempts", "lesson");
//datesubmitted == time created. dategraded == time modified or time overridden
//if grade was last modified by the user themselves use date graded. Otherwise use date submitted
//TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
$result->time = $grade->dategraded;
} else {
$result->time = $grade->datesubmitted;
}
}
return $return;
}
@ -521,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
*/

View 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
*/

View file

@ -366,7 +366,16 @@ function quiz_user_outline($course, $user, $mod, $quiz) {
$result = new stdClass();
$result->info = get_string('grade') . ': ' . $grade->str_long_grade;
//datesubmitted == time created. dategraded == time modified or time overridden
//if grade was last modified by the user themselves use date graded. Otherwise use date submitted
//TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
$result->time = $grade->dategraded;
} else {
$result->time = $grade->datesubmitted;
}
return $result;
}

View 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
*/

View file

@ -294,7 +294,16 @@ function scorm_user_outline($course, $user, $mod, $scorm) {
$grade = reset($grades->items[0]->grades);
$result = new stdClass();
$result->info = get_string('grade') . ': '. $grade->str_long_grade;
//datesubmitted == time created. dategraded == time modified or time overridden
//if grade was last modified by the user themselves use date graded. Otherwise use date submitted
//TODO: move this copied & pasted code somewhere in the grades API. See MDL-26704
if ($grade->usermodified == $user->id || empty($grade->datesubmitted)) {
$result->time = $grade->dategraded;
} else {
$result->time = $grade->datesubmitted;
}
return $result;
}
return null;

View 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
*/

View 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
*/

View 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
**/

View 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
*/

View file

@ -28,6 +28,8 @@ require_once("../config.php");
require_once("lib.php");
$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);
@ -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";
$sorturl = new moodle_url('/index.php', array('contextid' => $contextid, 'itemid' => $itemid, 'scaleid' => $scaleid));
if ($popup) {
$sortargs.="&amp;popup=$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,28 +110,23 @@ if (!$ratings) {
//but we don't
$rating->id = $rating->userid;
echo '<tr class="ratingitemheader">';
echo "<td>";
$row = new html_table_row();
$row->attributes['class'] = 'ratingitemheader';
if ($course && $course->id) {
echo $OUTPUT->user_picture($rating, array('courseid'=>$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();
}

File diff suppressed because it is too large Load diff

View file

@ -27,10 +27,11 @@
*/
require_once('../config.php');
require_once('lib.php');
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);
@ -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');
@ -66,12 +67,14 @@ if (!$pluginpermissionsarray['rate']) {
} else {
$params = array(
'context' => $context,
'component' => $component,
'ratingarea' => $ratingarea,
'itemid' => $itemid,
'scaleid' => $scaleid,
'rating' => $userrating,
'rateduserid' => $rateduserid);
if (!$rm->check_rating_is_valid($component, $params)) {
'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,11 +106,9 @@ 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");

View file

@ -29,10 +29,11 @@
define('AJAX_SCRIPT', true);
require_once('../config.php');
require_once('lib.php');
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);
@ -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');
@ -75,13 +76,15 @@ if (!$pluginpermissionsarray['rate']) {
} else {
$params = array(
'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,9 +104,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;
@ -113,10 +118,11 @@ if ($userrating != RATING_UNSET_RATING) {
// 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 = $DB->get_record($cm->modname, array('id' => $cm->instance));
if ($modinstance) {
$modinstance->cmidnumber = $cm->id; //MDL-12961
$functionname = $cm->modname.'_update_grades';
require_once("../mod/{$cm->modname}/lib.php");
require_once($CFG->dirroot."/mod/{$cm->modname}/lib.php");
if (function_exists($functionname)) {
$functionname($modinstance, $rateduserid);
}
@ -124,32 +130,31 @@ if ($userrating != RATING_UNSET_RATING) {
}
//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;
}

View file

@ -61,15 +61,15 @@ class repository_webdav extends repository {
public function check_login() {
return true;
}
public function get_file($path, $title) {
public function get_file($url, $title) {
global $CFG;
$path = urldecode($path);
$url = urldecode($url);
$path = $this->prepare_file($title);
$buffer = '';
if (!$this->dav->open()) {
return false;
}
$this->dav->get($path, $buffer);
$this->dav->get($url, $buffer);
$fp = fopen($path, 'wb');
fwrite($fp, $buffer);
return array('path'=>$path);

View file

@ -0,0 +1,133 @@
<?php
$THEME->name = 'afterburner';
$THEME->parents = array('base');
$THEME->sheets = array(
'afterburner_layout', /** Must come first: Page layout **/
'afterburner_styles', /** Must come second: default styles **/
'afterburner_menu',
'afterburner_blocks',
'afterburner_mod',
'afterburner_calendar',
'afterburner_dock',
'rtl'
);
$THEME->parents_exclude_sheets = array(
'base'=>array(
'pagelayout',
'dock'
),
);
$THEME->editor_sheets = array('editor');
$THEME->layouts = array(
// Most backwards compatible layout without the blocks - this is the layout used by default
'base' => array(
'file' => 'default.php',
'regions' => array(),
),
// Standard layout with blocks, this is recommended for most pages with default information
'standard' => array(
'file' => 'default.php',
'regions' => array('side-pre', 'side-post'),
'defaultregion' => 'side-post',
),
// Main course page
'course' => array(
'file' => 'default.php',
'regions' => array('side-pre', 'side-post'),
'defaultregion' => 'side-post',
'options' => array('langmenu'=>true),
),
'coursecategory' => array(
'file' => 'default.php',
'regions' => array('side-pre', 'side-post'),
'defaultregion' => 'side-post',
),
// part of course, typical for modules - default page layout if $cm specified in require_login()
'incourse' => array(
'file' => 'default.php',
'regions' => array('side-pre', 'side-post'),
'defaultregion' => 'side-post',
),
// The site home page.
'frontpage' => array(
'file' => 'default.php',
'regions' => array('side-pre', 'side-post'),
'defaultregion' => 'side-post',
),
// Server administration scripts.
'admin' => array(
'file' => 'default.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
// My dashboard page
'mydashboard' => array(
'file' => 'default.php',
'regions' => array('side-post'),
'defaultregion' => 'side-post',
'options' => array('langmenu'=>true),
),
// My public page
'mypublic' => array(
'file' => 'default.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
'login' => array(
'file' => 'default.php',
'regions' => array(),
'options' => array('langmenu'=>true),
),
// Pages that appear in pop-up windows - no navigation, no blocks, no header.
'popup' => array(
'file' => 'default.php',
'regions' => array(),
'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologininfo'=>true),
),
// No blocks and minimal footer - used for legacy frame layouts only!
'frametop' => array(
'file' => 'default.php',
'regions' => array(),
'options' => array('nofooter'=>true),
),
// Embedded pages, like iframe/object embeded in moodleform - it needs as much space as possible
'embedded' => array(
'file' => 'embedded.php',
'regions' => array()
),
// Used during upgrade and install, and for the 'This site is undergoing maintenance' message.
// This must not have any blocks, and it is good idea if it does not have links to
// other places - for example there should not be a home link in the footer...
'maintenance' => array(
'file' => 'default.php',
'regions' => array(),
'options' => array('noblocks'=>true, 'nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true),
),
// Should display the content and basic headers only.
'print' => array(
'file' => 'default.php',
'regions' => array(),
'options' => array('noblocks'=>true, 'nofooter'=>true, 'nonavbar'=>false, 'nocustommenu'=>true),
),
// The pagelayout used when a redirection is occuring.
'redirect' => array(
'file' => 'embedded.php',
'regions' => array()
),
// The pagelayout used for reports
'report' => array(
'file' => 'default.php',
'regions' => array('side-pre'),
'defaultregion' => 'side-pre',
),
);
$THEME->enable_dock = true;
$THEME->rendererfactory = 'theme_overridden_renderer_factory';

View file

@ -0,0 +1,55 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'theme_afterburner', language 'en'
*
* @package theme_afterburner
* @copyright 2011
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Afterburner';
$string['region-side-post'] = 'Right';
$string['region-side-pre'] = 'Left';
$string['choosereadme'] = '
<div class="clearfix">
<div class="theme_screenshot">
<h2>Afterburner</h2>
<img src="afterburner/pix/screenshot.jpg" />
<h3>Theme Discussion Forum:</h3>
<p><a href="http://moodle.org/mod/forum/view.php?id=46">http://moodle.org/mod/forum/view.php?id=46</a></p>
<h3>Theme Credits</h3>
<p><a href="http://docs.moodle.org/en/Theme_credits">http://docs.moodle.org/en/Theme_credits</a></p>
<h3>Theme Documentation:</h3>
<p><a href="http://docs.moodle.org/en/Themes">http://docs.moodle.org/en/Themes</a></p><h3>Report a bug:</h3><p><a href="http://tracker.moodle.org">http://tracker.moodle.org</a></p>
</div>
<div class="theme_description">
<h2>About</h2>
<p>Afterburner is a three-column, fluid-width theme coded for Moodle 2.0. It makes use of custom menus that appear below the site title on every page. An added function, which allows users to login and logout, has now been integrated into the custom menu for this theme.</p>
<h2>Parents</h2>
<p>This theme is built on Base, a parent theme included in the Moodle core. If you wish to modify aspects of this theme, beyond the settings offered, we advise creating a new theme using this theme and Base theme as parent themes, so any updates to these parent themes, in the core, will find their way into your new theme.</p>
<h2>Credits</h2>
<p>This design was originally created for Moodle 1.9 by Patrick Malley of NewSchool Learning (www.newschoollearning.com)from an original design by Rocket Themes (www.rockettheme.com) before being ported to Moodle 2.0 by Mary Evans of NewSchool Learning (contact@newschoollearning.com).</p>
<h2>License</h2>
<p>This, and all other themes included in the Moodle core, are licensed under the <a href="http://www.gnu.org/licenses/gpl.html">GNU General Public License</a>.</p>
</div>
</div>';

View file

@ -0,0 +1,128 @@
<?php
$hasheading = ($PAGE->heading);
$hasnavbar = (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar());
$hasfooter = (empty($PAGE->layout_options['nofooter']));
$hassidepre = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-pre', $OUTPUT));
$hassidepost = (empty($PAGE->layout_options['noblocks']) && $PAGE->blocks->region_has_content('side-post', $OUTPUT));
$haslogininfo = (empty($PAGE->layout_options['nologininfo']));
$showsidepre = ($hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT));
$showsidepost = ($hassidepost && !$PAGE->blocks->region_completely_docked('side-post', $OUTPUT));
$custommenu = $OUTPUT->custom_menu();
$hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu));
$bodyclasses = array();
if ($showsidepre && !$showsidepost) {
$bodyclasses[] = 'side-pre-only';
} else if ($showsidepost && !$showsidepre) {
$bodyclasses[] = 'side-post-only';
} else if (!$showsidepost && !$showsidepre) {
$bodyclasses[] = 'content-only';
}
if ($hascustommenu) {
$bodyclasses[] = 'has_custom_menu';
}
echo $OUTPUT->doctype() ?>
<html <?php echo $OUTPUT->htmlattributes() ?>>
<head>
<title><?php echo $PAGE->title ?></title>
<link rel="shortcut icon" href="<?php echo $OUTPUT->pix_url('favicon', 'theme')?>" />
<?php echo $OUTPUT->standard_head_html() ?>
</head>
<body id="<?php p($PAGE->bodyid) ?>" class="<?php p($PAGE->bodyclasses.' '.join(' ', $bodyclasses)) ?>">
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<div id="page-wrapper">
<div id="page">
<?php if ($hasheading || $hasnavbar) { ?>
<div id="page-header">
<?php if ($hasheading) { ?>
<div id="logo">
</div>
<div class="headermenu"><?php
if ($haslogininfo) {
echo $OUTPUT->login_info();
}
if (!empty($PAGE->layout_options['langmenu'])) {
echo $OUTPUT->lang_menu();
}
echo $PAGE->headingmenu
?></div>
<?php } ?>
</div>
<?php } ?>
<!-- END OF HEADER -->
<!-- START CUSTOMMENU AND NAVBAR -->
<div id="navcontainer">
<?php if ($hascustommenu) { ?>
<div id="custommenu" class="javascript-disabled"><?php echo $custommenu; ?></div>
<?php } ?>
</div>
<?php if ($hasnavbar) { ?>
<div class="navbar clearfix">
<div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
<div class="navbutton"> <?php echo $PAGE->button; ?></div>
</div>
<?php } ?>
<!-- END OF CUSTOMMENU AND NAVBAR -->
<div id="page-content">
<div id="region-main-box">
<div id="region-post-box">
<div id="region-main-wrap">
<div id="region-main-pad">
<div id="region-main">
<div class="region-content">
<?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
</div>
</div>
</div>
</div>
<?php if ($hassidepre) { ?>
<div id="region-pre" class="block-region">
<div class="region-content">
<?php echo $OUTPUT->blocks_for_region('side-pre') ?>
</div>
</div>
<?php } ?>
<?php if ($hassidepost) { ?>
<div id="region-post" class="block-region">
<div class="region-content">
<?php echo $OUTPUT->blocks_for_region('side-post') ?>
</div>
</div>
<?php } ?>
</div>
</div>
</div>
<!-- START OF FOOTER -->
<?php if ($hasfooter) { ?>
<div id="page-footer" class="clearfix">
<div class="footer-left">
<a href="http://moodle.org" title="Moodle">
<img src="<?php echo $OUTPUT->pix_url('footer/moodle-logo','theme')?>" alt="Moodle logo" />
</a>
</div>
<div class="footer-right">
<?php echo $OUTPUT->login_info();?>
</div>
<?php echo $OUTPUT->standard_footer_html(); ?>
</div>
<?php } ?>
<div class="clearfix"></div>
</div>
</div>
<?php echo $OUTPUT->standard_end_of_body_html() ?>
</body>
</html>

View file

@ -0,0 +1,23 @@
<?php echo $OUTPUT->doctype() ?>
<html <?php echo $OUTPUT->htmlattributes() ?>>
<head>
<title><?php echo $PAGE->title ?></title>
<link rel="shortcut icon" href="<?php echo $OUTPUT->pix_url('favicon', 'theme')?>" />
<?php echo $OUTPUT->standard_head_html() ?>
</head>
<body id="<?php p($PAGE->bodyid) ?>" class="<?php p($PAGE->bodyclasses) ?>">
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<div id="page">
<!-- END OF HEADER -->
<div id="content" class="clearfix">
<?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
</div>
<!-- START OF FOOTER -->
</div>
<?php echo $OUTPUT->standard_end_of_body_html() ?>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 655 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 894 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Some files were not shown because too many files have changed in this diff Show more