</PHP_SETTING>
</PHP_SETTINGS>
</MOODLE>
+ <MOODLE version="2.3" requires="2.2">
+ <UNICODE level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unicoderequired" />
+ </FEEDBACK>
+ </UNICODE>
+ <DATABASE level="required">
+ <VENDOR name="mysql" version="5.0.25">
+ <FEEDBACK>
+ <ON_ERROR message="mysql416required" />
+ </FEEDBACK>
+ </VENDOR>
+ <VENDOR name="postgres" version="8.3" />
+ <VENDOR name="mssql" version="9.0" />
+ <VENDOR name="odbc_mssql" version="9.0" />
+ <VENDOR name="mssql_n" version="9.0" />
+ <VENDOR name="oracle" version="10.2" />
+ <VENDOR name="sqlite" version="2.0" />
+ </DATABASE>
+ <PHP version="5.3.2" level="required">
+ </PHP>
+ <PHP_EXTENSIONS>
+ <PHP_EXTENSION name="iconv" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="iconvrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="mbstring" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="mbstringrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="curl" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="curlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="openssl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="opensslrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="tokenizer" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="tokenizerrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlrpc" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="xmlrpcrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="soap" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="soaprecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="ctype" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ctyperequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="zip" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ziprequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="gd" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="gdrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="simplexml" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="simplexmlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="spl" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="splrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="pcre" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="dom" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xml" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="intl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="intlrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="json" level="required">
+ </PHP_EXTENSION>
+ </PHP_EXTENSIONS>
+ <PHP_SETTINGS>
+ <PHP_SETTING name="memory_limit" value="40M" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="settingmemorylimit" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="safe_mode" value="0" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingsafemode" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="file_uploads" value="1" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingfileuploads" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ </PHP_SETTINGS>
+ </MOODLE>
</COMPATIBILITY_MATRIX>
$mform = $this->_form;
- $mform->addElement('text', 'wwwroot', get_string('hostname', 'mnet'));
+ $mform->addElement('text', 'wwwroot', get_string('hostname', 'mnet'), array('maxlength' => 255, 'size' => 50));
$mform->setType('wwwroot', PARAM_URL);
$mform->addRule('wwwroot', null, 'required', null, 'client');
+ $mform->addRule('wwwroot', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$mform->addElement('select', 'applicationid', get_string('applicationtype', 'mnet'),
$DB->get_records_menu('mnet_application', array(), 'id,display_name'));
$mform->addElement('hidden', 'applicationid');
$mform->addElement('hidden', 'oldpublickey');
- $mform->addElement('text', 'name', get_string('site'));
+ $mform->addElement('text', 'name', get_string('site'), array('maxlength' => 80, 'size' => 50));
$mform->setType('name', PARAM_NOTAGS);
+ $mform->addRule('name', get_string('maximumchars', '', 80), 'maxlength', 80, 'client');
- $mform->addElement('text', 'wwwroot', get_string('hostname', 'mnet'));
+ $mform->addElement('text', 'wwwroot', get_string('hostname', 'mnet'), array('maxlength' => 255, 'size' => 50));
$mform->setType('wwwroot', PARAM_URL);
+ $mform->addRule('wwwroot', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
$themes = array('' => get_string('forceno'));
foreach (array_keys(get_plugin_list('theme')) as $themename) {
$mform->addElement('textarea', 'public_key', get_string('publickey', 'mnet'), array('rows' => 17, 'cols' => 100, 'class' => 'smalltext'));
$mform->setType('public_key', PARAM_PEM);
- if ($mnet_peer && !empty($mnet_peer->deleted)) {
- $radioarray = array();
- $radioarray[] = MoodleQuickForm::createElement('radio', 'deleted', '', get_string('yes'), 1);
- $radioarray[] = MoodleQuickForm::createElement('radio', 'deleted', '', get_string('no'), 0);
- $mform->addGroup($radioarray, 'radioar', get_string('deleted'), array(' '), false);
- } else {
- $mform->addElement('hidden', 'deleted');
- }
-
// finished with form controls, now the static informational stuff
if ($mnet_peer && !empty($mnet_peer->bootstrapped)) {
$expires = '';
}
}
- $mform->addElement('static', 'certdetails', get_string('certdetails', 'mnet'), $OUTPUT->box('<pre>' . $credstr . '</pre>'));
+ $mform->addElement('static', 'certdetails', get_string('certdetails', 'mnet'),
+ $OUTPUT->box('<pre>' . $credstr . '</pre>', 'generalbox certdetails'));
+ }
+
+ if ($mnet_peer && !empty($mnet_peer->deleted)) {
+ $radioarray = array();
+ $radioarray[] = MoodleQuickForm::createElement('static', 'deletedinfo', '',
+ $OUTPUT->container(get_string('deletedhostinfo', 'mnet'), 'deletedhostinfo'));
+ $radioarray[] = MoodleQuickForm::createElement('radio', 'deleted', '', get_string('yes'), 1);
+ $radioarray[] = MoodleQuickForm::createElement('radio', 'deleted', '', get_string('no'), 0);
+ $mform->addGroup($radioarray, 'radioar', get_string('deleted'), array(' ', ' '), false);
+ } else {
+ $mform->addElement('hidden', 'deleted');
}
// finished with static stuff, print save button
// normal flow - just display all hosts with links
echo $OUTPUT->header();
-$hosts = mnet_get_hosts();
+$hosts = mnet_get_hosts(true);
// print the table to display the register all hosts setting
$table = new html_table();
);
$table->wrap = array('nowrap', 'nowrap', 'nowrap', 'nowrap');
$baseurl = new moodle_url('/admin/mnet/peers.php');
+$deleted = array();
foreach($hosts as $host) {
$hosturl = new moodle_url($baseurl, array('hostid' => $host->id));
+ if (trim($host->name) === '') {
+ // should not happen but...
+ $host->name = '???';
+ }
// process all hosts first since it's the easiest
if ($host->id == $CFG->mnet_all_hosts_id) {
- $table->data[] = array(html_writer::tag('a', $host->name, array('href'=>$hosturl)), '', '', '');
+ $table->data[] = array(html_writer::link($hosturl, get_string('allhosts', 'core_mnet')), '*', '', '');
+ continue;
+ }
+
+ // populate the list of deleted hosts
+ if ($host->deleted) {
+ $deleted[] = html_writer::link($hosturl, $host->name);
continue;
}
if ($host->last_connect_time == 0) {
$last_connect = get_string('never');
} else {
- $last_connect = date('H:i:s d/m/Y', $host->last_connect_time);
+ $last_connect = userdate($host->last_connect_time, get_string('strftimedatetime', 'core_langconfig'));
}
$table->data[] = array(
html_writer::link($hosturl, $host->name),
- html_writer::link($hosturl, $host->wwwroot),
+ html_writer::link($host->wwwroot, $host->wwwroot),
$last_connect,
$OUTPUT->single_button(new moodle_url('/admin/mnet/delete.php', array('hostid' => $host->id)), get_string('delete'))
);
}
echo html_writer::table($table);
+if ($deleted) {
+ echo $OUTPUT->box(get_string('deletedhosts', 'core_mnet', join(', ', $deleted)), 'deletedhosts');
+}
+
// finally, print the initial form to add a new host
echo $OUTPUT->box_start();
echo $OUTPUT->heading(get_string('addnewhost', 'mnet'), 3);
if (!array_key_exists($behaviour, $counts)) {
$counts[$behaviour] = 0;
}
- $needed[$behaviour] = ($counts[$behaviour] > 0) &&
+ $needed[$behaviour] = ($counts[$behaviour] > 0) ||
$pluginmanager->other_plugins_that_require('qbehaviour_' . $behaviour);
$archetypal[$behaviour] = question_engine::is_behaviour_archetypal($behaviour);
}
-
foreach ($counts as $behaviour => $count) {
if (!array_key_exists($behaviour, $behaviours)) {
- $counts['missingtype'] += $count;
+ $counts['missing'] += $count;
}
}
+$needed['missing'] = true;
// Work of the correct sort order.
$config = get_config('question');
unset($disabledbehaviours[$key]);
set_config('disabledbehaviours', implode(',', $disabledbehaviours), 'question');
}
- $behaviourorder = explode(',', $config->behavioursortorder);
+ $behaviourorder = array_keys($sortedbehaviours);
if (($key = array_search($delete, $behaviourorder)) !== false) {
unset($behaviourorder[$key]);
set_config('behavioursortorder', implode(',', $behaviourorder), 'question');
$row[] = '';
}
- // Are people allowed to create new questions of this type?
+ // Are people allowed to select this behaviour?
$rowclass = '';
if ($archetypal[$behaviour]) {
$enabled = array_search($behaviour, $disabledbehaviours) === false;
$output .= $this->maturity_info($maturity);
$output .= $this->insecure_dataroot_warning($insecuredataroot);
$output .= $this->display_errors_warning($errorsdisplayed);
- $output .= $this->cron_overdue_warning($errorsdisplayed);
+ $output .= $this->cron_overdue_warning($cronoverdue);
$output .= $this->db_problems($dbproblems);
$output .= $this->maintenance_mode_warning($maintenancemode);
$url->param('adminedit', 'on');
}
$buttons = $OUTPUT->single_button($url, $caption, 'get');
+ $PAGE->set_button($buttons);
}
$visiblepathtosection = array_reverse($settingspage->visiblepath);
$PAGE->set_title("$SITE->shortname: " . implode(": ",$visiblepathtosection));
$PAGE->set_heading($SITE->fullname);
- $PAGE->set_button($buttons);
echo $OUTPUT->header();
if ($errormsg !== '') {
$hosts = mnet_get_hosts();
foreach ($hosts as $host) {
+ if ($host->id == $CFG->mnet_all_hosts_id) {
+ $host->name = get_string('allhosts', 'core_mnet');
+ }
$ADMIN->add('mnetpeercat',
new admin_externalpage(
'mnetpeer' . $host->id,
$mform->addElement('text', 'dbuser', get_string('user'));
$mform->addElement('text', 'dbpass', get_string('password'));
$mform->addElement('text', 'prefix', get_string('dbprefix', 'install'));
+ $mform->addElement('text', 'dbport', get_string('dbport', 'install'));
+ $mform->addElement('text', 'dbsocket', get_string('databasesocket', 'install'));
$mform->addRule('dbhost', get_string('required'), 'required', null);
$mform->addRule('dbname', get_string('required'), 'required', null);
// Connect to the other database.
list($dbtype, $dblibrary) = explode('/', $data->driver);
$targetdb = moodle_database::get_driver_instance($dbtype, $dblibrary);
- if (!$targetdb->connect($data->dbhost, $data->dbuser, $data->dbpass, $data->dbname, $data->prefix, null)) {
+ $dboptions = array();
+ if ($data->dbport) {
+ $dboptions['dbport'] = $data->dbport;
+ }
+ if ($data->dbsocket) {
+ $dboptions['dbsocket'] = $data->dbsocket;
+ }
+ if (!$targetdb->connect($data->dbhost, $data->dbuser, $data->dbpass, $data->dbname, $data->prefix, $dboptions)) {
throw new dbtransfer_exception('notargetconectexception', null, "$CFG->wwwroot/$CFG->admin/tool/dbtransfer/");
}
if ($targetdb->get_tables()) {
ob_start(); //for whitespace test
require('../../../config.php');
-
- // extra whitespace test - intentionally breaks cookieless mode
- $extraws = '';
- while (ob_get_level()) {
- $extraws .= ob_get_contents();
- ob_end_clean();
- }
+ $extraws = ob_get_clean();
require_once($CFG->libdir.'/adminlib.php');
html_writer::link(new moodle_url('/course/view.php',
array('id' => $quizinfo->courseid)), format_string($quizinfo->shortname)),
html_writer::link(new moodle_url('/mod/quiz/view.php',
- array('id' => $quizinfo->name)), format_string($quizinfo->name)),
+ array('q' => $quizinfo->id)), format_string($quizinfo->name)),
$quizinfo->attemptcount,
$quizinfo->questionattempts ? $quizinfo->questionattempts : 0,
);
$params['descpat'.$i] = "%$keyword%";
$keywordfull2[] = $DB->sql_like('p.summary', ':sumpat'.$i, false);
$params['sumpat'.$i] = "%$keyword%";
+ $keywordfull3[] = $DB->sql_like('p.subject', ':subpat'.$i, false);
+ $params['subpat'.$i] = "%$keyword%";
+ $keywordfull4[] = $DB->sql_like('c.content', ':contpat'.$i, false);
+ $params['contpat'.$i] = "%$keyword%";
+ $keywordfull5[] = $DB->sql_like('m.fullmessage', ':msgpat'.$i, false);
+ $params['msgpat'.$i] = "%$keyword%";
+ $keywordfull6[] = $DB->sql_like('fp.message', ':forumpostpat'.$i, false);
+ $params['forumpostpat'.$i] = "%$keyword%";
+ $keywordfull7[] = $DB->sql_like('fp.subject', ':forumpostsubpat'.$i, false);
+ $params['forumpostsubpat'.$i] = "%$keyword%";
$i++;
}
$conditions = '( '.implode(' OR ', $keywordfull).' )';
$conditions2 = '( '.implode(' OR ', $keywordfull2).' )';
+ $conditions3 = '( '.implode(' OR ', $keywordfull3).' )';
+ $conditions4 = '( '.implode(' OR ', $keywordfull4).' )';
+ $conditions5 = '( '.implode(' OR ', $keywordfull5).' )';
+ $conditions6 = '( '.implode(' OR ', $keywordfull6).' )';
+ $conditions7 = '( '.implode(' OR ', $keywordfull7).' )';
$sql = "SELECT * FROM {user} WHERE deleted = 0 AND id <> :userid AND $conditions"; // Exclude oneself
$sql2 = "SELECT u.*, p.summary FROM {user} AS u, {post} AS p WHERE $conditions2 AND u.deleted = 0 AND u.id=p.userid AND u.id <> :userid";
+ $sql3 = "SELECT u.*, p.subject as postsubject FROM {user} AS u, {post} AS p WHERE $conditions3 AND u.deleted = 0 AND u.id=p.userid AND u.id <> :userid";
+ $sql4 = "SELECT u.*, c.content FROM {user} AS u, {comments} AS c WHERE $conditions4 AND u.deleted = 0 AND u.id=c.userid AND u.id <> :userid";
+ $sql5 = "SELECT u.*, m.fullmessage FROM {user} AS u, {message} AS m WHERE $conditions5 AND u.deleted = 0 AND u.id=m.useridfrom AND u.id <> :userid";
+ $sql6 = "SELECT u.*, fp.message FROM {user} AS u, {forum_posts} AS fp WHERE $conditions6 AND u.deleted = 0 AND u.id=fp.userid AND u.id <> :userid";
+ $sql7 = "SELECT u.*, fp.subject FROM {user} AS u, {forum_posts} AS fp WHERE $conditions7 AND u.deleted = 0 AND u.id=fp.userid AND u.id <> :userid";
+
$spamusers_desc = $DB->get_recordset_sql($sql, $params);
$spamusers_blog = $DB->get_recordset_sql($sql2, $params);
+ $spamusers_blogsub = $DB->get_recordset_sql($sql3, $params);
+ $spamusers_comment = $DB->get_recordset_sql($sql4, $params);
+ $spamusers_message = $DB->get_recordset_sql($sql5, $params);
+ $spamusers_forumpost = $DB->get_recordset_sql($sql6, $params);
+ $spamusers_forumpostsub = $DB->get_recordset_sql($sql7, $params);
$keywordlist = implode(', ', $keywords);
echo $OUTPUT->box(get_string('spamresult', 'tool_spamcleaner').s($keywordlist)).' ...';
- print_user_list(array($spamusers_desc, $spamusers_blog), $keywords);
-
+ print_user_list(array($spamusers_desc,
+ $spamusers_blog,
+ $spamusers_blogsub,
+ $spamusers_comment,
+ $spamusers_message,
+ $spamusers_forumpost,
+ $spamusers_forumpostsub
+ ),
+ $keywords);
}
if (isset($user->summary)) {
$user->description = '<h3>'.get_string('spamfromblog', 'tool_spamcleaner').'</h3>'.$user->summary;
unset($user->summary);
+ } else if (isset($user->postsubject)) {
+ $user->description = '<h3>'.get_string('spamfromblog', 'tool_spamcleaner').'</h3>'.$user->postsubject;
+ unset($user->postsubject);
+ } else if (isset($user->content)) {
+ $user->description = '<h3>'.get_string('spamfromcomments', 'tool_spamcleaner').'</h3>'.$user->content;
+ unset($user->content);
+ } else if (isset($user->fullmessage)) {
+ $user->description = '<h3>'.get_string('spamfrommessages', 'tool_spamcleaner').'</h3>'.$user->fullmessage;
+ unset($user->fullmessage);
+ } else if (isset($user->message)) {
+ $user->description = '<h3>'.get_string('spamfromforumpost', 'tool_spamcleaner').'</h3>'.$user->message;
+ unset($user->message);
+ } else if (isset($user->subject)) {
+ $user->description = '<h3>'.get_string('spamfromforumpost', 'tool_spamcleaner').'</h3>'.$user->subject;
+ unset($user->subject);
}
+
if (preg_match('#<img.*src=[\"\']('.$CFG->wwwroot.')#', $user->description, $matches)
&& $image_search) {
$result = false;
$string['spamdesc'] = 'Description';
$string['spameg'] = 'eg: casino, porn, xxx';
$string['spamfromblog'] = 'From blog post:';
+$string['spamfromcomments'] = 'From comments:';
+$string['spamfrommessages'] = 'From messages:';
+$string['spamfromforumpost'] = 'From forum post:';
$string['spaminvalidresult'] = 'Unknown but invalid result';
$string['spamoperation'] = 'Operation';
$string['spamresult'] = 'Results of searching user profiles containing:';
class web_service_token_form extends moodleform {
function definition() {
- global $USER, $DB;
+ global $USER, $DB, $CFG;
$mform = $this->_form;
$data = $this->_customdata;
$mform->addElement('header', 'token', get_string('token', 'webservice'));
if (empty($data->nouserselection)) {
- //user searchable selector - get all users (admin and guest included)
- $sql = "SELECT u.id, u.firstname, u.lastname
- FROM {user} u
- ORDER BY u.lastname";
- $users = $DB->get_records_sql($sql, array());
- $options = array();
- foreach ($users as $userid => $user) {
- $options[$userid] = $user->firstname . " " . $user->lastname;
+
+ //check if the number of user is reasonable to be displayed in a select box
+ $usertotal = $DB->count_records('user',
+ array('deleted' => 0, 'suspended' => 0, 'confirmed' => 1));
+
+ if ($usertotal < 500) {
+ //user searchable selector - get all users (admin and guest included)
+ //user must be confirmed, not deleted, not suspended, not guest
+ $sql = "SELECT u.id, u.firstname, u.lastname
+ FROM {user} u
+ WHERE u.deleted = 0 AND u.confirmed = 1 AND u.suspended = 0 AND u.id != ?
+ ORDER BY u.lastname";
+ $users = $DB->get_records_sql($sql, array($CFG->siteguest));
+ $options = array();
+ foreach ($users as $userid => $user) {
+ $options[$userid] = $user->firstname . " " . $user->lastname;
+ }
+ $mform->addElement('searchableselector', 'user', get_string('user'), $options);
+ } else {
+ //simple text box for username or user id (if two username exists, a form error is displayed)
+ $mform->addElement('text', 'user', get_string('usernameorid', 'webservice'));
}
- $mform->addElement('searchableselector', 'user', get_string('user'), $options);
$mform->addRule('user', get_string('required'), 'required', null, 'client');
}
$this->set_data($data);
}
- function validation($data, $files) {
+ function get_data() {
+ global $DB;
+ $data = parent::get_data();
+
+ if (!empty($data) && !is_numeric($data->user)) {
+ //retrieve username
+ $user = $DB->get_record('user', array('username' => $data->user), 'id');
+ $data->user = $user->id;
+ }
+ return $data;
+ }
+
+ function validation(&$data, $files) {
+ global $DB;
+
$errors = parent::validation($data, $files);
+
+ if (is_numeric($data['user'])) {
+ $searchtype = 'id';
+ } else {
+ $searchtype = 'username';
+ //check the username is valid
+ if (clean_param($data['user'], PARAM_USERNAME) != $data['user']) {
+ $errors['user'] = get_string('invalidusername');
+ }
+ }
+
+ if (!isset($errors['user'])) {
+ $users = $DB->get_records('user', array($searchtype => $data['user']), '', 'id');
+
+ //check that the user exists in the database
+ if (count($users) == 0) {
+ $errors['user'] = get_string('usernameoridnousererror', 'webservice');
+ } else if (count($users) > 1) { //can only be a username search as id are unique
+ $errors['user'] = get_string('usernameoridoccurenceerror', 'webservice');
+ }
+ }
+
return $errors;
}
}
}
+ //check if the user is deleted. unconfirmed, suspended or guest
+ $user = $DB->get_record('user', array('id' => $data->user));
+ if ($user->id == $CFG->siteguest or $user->deleted or !$user->confirmed or $user->suspended) {
+ throw new moodle_exception('forbiddenwsuser', 'webservice');
+ }
+
//process the creation
if (empty($errormsg)) {
//TODO improvement: either move this function from externallib.php to webservice/lib.php
if (!empty($files)) {
foreach ($files as $file) {
-
- $source = cc2moodle::$path_to_manifest_folder . DIRECTORY_SEPARATOR . $file->nodeValue;
- $destination = $destination_folder . DIRECTORY_SEPARATOR . $file->nodeValue;
+ $source = cc2moodle::$path_to_manifest_folder . DIRECTORY_SEPARATOR . $file;
+ $destination = $destination_folder . DIRECTORY_SEPARATOR . $file;
$destination_directory = dirname($destination);
if (in_array($ext, array('html', 'htm', 'xhtml'))) {
continue;
}
- $all_files[] = $file;
+ $all_files[] = $file->nodeValue;
}
}
-
unset($files);
}
if (!empty($labels) && ($labels->length > 0)) {
$tname = 'course_files';
$dpath = cc2moodle::$path_to_manifest_folder . DIRECTORY_SEPARATOR . $tname;
- $fpath = $dpath . DIRECTORY_SEPARATOR . 'folder.gif';
- $rfpath = $tname.'/folder.gif';
+ $rfpath = 'folder.gif';
+ $fpath = $dpath . DIRECTORY_SEPARATOR . $rfpath;
if (!file_exists($dpath)) {
mkdir($dpath);
if (in_array($ext, array('html', 'htm', 'xhtml'))) {
continue;
}
- $all_files[] = $file;
+ $all_files[] = $file->nodeValue;
}
unset($files);
}
if (!empty($labels) && ($labels->length > 0)) {
$tname = 'course_files';
$dpath = cc2moodle::$path_to_manifest_folder . DIRECTORY_SEPARATOR . $tname;
+ $rfpath = 'folder.gif';
$fpath = $dpath . DIRECTORY_SEPARATOR . 'folder.gif';
- $rfpath = $tname.'/folder.gif';
if (!file_exists($dpath)) {
mkdir($dpath);
}
'[#question_text#]',
'[#question_type#]',
'[#question_general_feedback#]',
+ '[#question_defaultgrade#]',
'[#date_now#]',
'[#question_type_nodes#]',
'[#question_stamp#]',
self::safexml($question['title']),
$question_moodle_type,
self::safexml($question['feedback']),
+ $question['defaultgrade'], //default grade
time(),
$question_type_node,
$quiz_stamp,
$questions[$question_identifier]['moodle_type'] = $question_type['moodle'];
$questions[$question_identifier]['cc_type'] = $question_type['cc'];
$questions[$question_identifier]['feedback'] = $this->get_general_feedback($assessment, $question_identifier);
+ $questions[$question_identifier]['defaultgrade'] = $this->get_defaultgrade($assessment, $question_identifier);
$questions[$question_identifier]['answers'] = $this->get_answers($question_identifier, $assessment, $last_answer_id);
}
}
}
+ private function get_defaultgrade($assessment, $question_identifier) {
+ $result = 1;
+ $xpath = cc2moodle::newx_path($assessment, cc2moodle::getquizns());
+ $query = '//xmlns:item[@ident="' . $question_identifier . '"]';
+ $query .= '//xmlns:qtimetadatafield[xmlns:fieldlabel="cc_weighting"]/xmlns:fieldentry';
+ $defgrade = $xpath->query($query);
+ if (!empty($defgrade) && ($defgrade->length > 0)) {
+ $resp = (int)$defgrade->item(0)->nodeValue;
+ if ($resp >= 0 && $resp <= 99) {
+ $result = $resp;
+ }
+ }
+ return $result;
+ }
+
private function get_general_feedback ($assessment, $question_identifier) {
$xpath = cc2moodle::newx_path($assessment, cc2moodle::getquizns());
'[#question_text#]',
'[#question_type#]',
'[#question_general_feedback#]',
+ '[#question_defaultgrade#]',
'[#date_now#]',
'[#question_type_nodes#]',
'[#question_stamp#]',
self::safexml($question['title']),
$question_moodle_type,
self::safexml($question['feedback']),
+ $question['defaultgrade'],
time(),
$question_type_node,
$quiz_stamp,
$questions[$question_identifier]['moodle_type'] = $question_type['moodle'];
$questions[$question_identifier]['cc_type'] = $question_type['cc'];
$questions[$question_identifier]['feedback'] = $this->get_general_feedback($assessment, $question_identifier);
+ $questions[$question_identifier]['defaultgrade'] = $this->get_defaultgrade($assessment, $question_identifier);
$questions[$question_identifier]['answers'] = $this->get_answers($question_identifier, $assessment, $last_answer_id);
}
}
}
+ private function get_defaultgrade($assessment, $question_identifier) {
+ $result = 1;
+ $xpath = cc2moodle::newx_path($assessment, cc2moodle::getquizns());
+ $query = '//xmlns:item[@ident="' . $question_identifier . '"]';
+ $query .= '//xmlns:qtimetadatafield[xmlns:fieldlabel="cc_weighting"]/xmlns:fieldentry';
+ $defgrade = $xpath->query($query);
+ if (!empty($defgrade) && ($defgrade->length > 0)) {
+ $resp = (int)$defgrade->item(0)->nodeValue;
+ if ($resp >= 0 && $resp <= 99) {
+ $result = $resp;
+ }
+ }
+ return $result;
+ }
+
private function get_general_feedback ($assessment, $question_identifier) {
$xpath = cc112moodle::newx_path($assessment, cc112moodle::getquizns());
<QUESTIONTEXTFORMAT>1</QUESTIONTEXTFORMAT>
<IMAGE></IMAGE>
<GENERALFEEDBACK>[#question_general_feedback#]</GENERALFEEDBACK>
- <DEFAULTGRADE>1</DEFAULTGRADE>
+ <DEFAULTGRADE>[#question_defaultgrade#]</DEFAULTGRADE>
<PENALTY>0</PENALTY>
<QTYPE>[#question_type#]</QTYPE>
<LENGTH>1</LENGTH>
// Basic/initial prevention against time/memory limits
set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
raise_memory_limit(MEMORY_EXTRA);
+ // If this is not a course backup, inform the plan we are not
+ // including all the activities for sure. This will affect any
+ // task/step executed conditionally to stop including information
+ // for section and activity backup. MDL-28180.
+ if ($this->get_type() !== backup::TYPE_1COURSE) {
+ $this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG);
+ $this->plan->set_excluding_activities();
+ }
return $this->plan->execute();
}
// Basic/initial prevention against time/memory limits
set_time_limit(1 * 60 * 60); // 1 hour for 1 course initially granted
raise_memory_limit(MEMORY_EXTRA);
+ // If this is not a course restore, inform the plan we are not
+ // including all the activities for sure. This will affect any
+ // task/step executed conditionally to stop processing information
+ // for section and activity restore. MDL-28180.
+ if ($this->get_type() !== backup::TYPE_1COURSE) {
+ $this->log('notifying plan about excluded activities by type', backup::LOG_DEBUG);
+ $this->plan->set_excluding_activities();
+ }
return $this->plan->execute();
}
$manifestdir = dirname($manifest);
$cc2moodle = new cc2moodle($manifest);
if ($cc2moodle->is_auth()) {
- throw new imscc1_convert_exception('Protected cartridge content - Skipping import!');
+ throw new imscc1_convert_exception('protected_cc_not_supported');
}
$status = $cc2moodle->generate_moodle_xml();
//Final cleanup
$manifestdir = dirname($manifest);
$cc112moodle = new cc112moodle($manifest);
if ($cc112moodle->is_auth()) {
- throw new imscc11_convert_exception('Protected cartridge content - Skipping import!');
+ throw new imscc11_convert_exception('protected_cc_not_supported');
}
$status = $cc112moodle->generate_moodle_xml();
//Final cleanup
$data->course = $this->get_courseid();
- $params = array(
- 'course' => $data->course,
- 'criteriatype' => $data->criteriatype,
- 'method' => $data->method,
- 'value' => $data->value,
- );
- $DB->insert_record('course_completion_aggr_methd', $params);
+ // Only create the course_completion_aggr_methd records if
+ // the target course has not them defined. MDL-28180
+ if (!$DB->record_exists('course_completion_aggr_methd', array(
+ 'course' => $data->course,
+ 'criteriatype' => $data->criteriatype))) {
+ $params = array(
+ 'course' => $data->course,
+ 'criteriatype' => $data->criteriatype,
+ 'method' => $data->method,
+ 'value' => $data->value,
+ );
+ $DB->insert_record('course_completion_aggr_methd', $params);
+ }
}
-
}
}
$this->controller = $controller;
$this->basepath = $CFG->tempdir . '/backup/' . $controller->get_backupid();
+ $this->excludingdactivities = false;
parent::__construct('backup_plan');
}
this.overlays[commentid].render();
this.overlays[commentid].hide();
- // position the overlay in the middle of the web browser window
- var WidgetPositionAlign = Y.WidgetPositionAlign;
- this.overlays[commentid].set("align", {
- node:"", //empty => viewport
- points:[WidgetPositionAlign.CC, WidgetPositionAlign.CC]
- });
-
Y.one('#comments-'+commentid).on('click', this.show, this, commentid);
}
}
if ($CFG->enablerssfeeds) {
- $rsscontext = $filtertype = $thingid = null;
+ $rsscontext = null;
+ $filtertype = null;
+ $thingid = null;
list($thingid, $rsscontext, $filtertype) = blog_rss_get_params($blogheaders['filters']);
-
+ if (empty($rsscontext)) {
+ $rsscontext = get_system_context();
+ }
$rsstitle = $blogheaders['heading'];
//check we haven't started output by outputting an error message
if ($PAGE->user_is_editing() && has_capability('moodle/course:update', $coursecontext)) {
echo '<a title="'.$streditsummary.'" '.
' href="editsection.php?id='.$thissection->id.'"><img src="'.$OUTPUT->pix_url('t/edit') . '" '.
- ' class="icon edit" alt="'.$streditsummary.'" /></a>';
+ ' class="iconsmall edit" alt="'.$streditsummary.'" /></a>';
}
echo '</div>';
if ($PAGE->user_is_editing() && has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id))) {
echo ' <a title="'.$streditsummary.'" href="editsection.php?id='.$thissection->id.'">'.
- '<img src="'.$OUTPUT->pix_url('t/edit') . '" class="icon edit" alt="'.$streditsummary.'" /></a><br /><br />';
+ '<img src="'.$OUTPUT->pix_url('t/edit') . '" class="iconsmall edit" alt="'.$streditsummary.'" /></a><br /><br />';
}
echo '</div>';
if ($PAGE->user_is_editing() && has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id))) {
echo '<p><a title="'.$streditsummary.'" '.
' href="editsection.php?id='.$thissection->id.'"><img src="'.$OUTPUT->pix_url('t/edit') . '" '.
- ' class="icon edit" alt="'.$streditsummary.'" /></a></p>';
+ ' class="iconsmall edit" alt="'.$streditsummary.'" /></a></p>';
}
echo '</div>';
if ($PAGE->user_is_editing() && has_capability('moodle/course:update', get_context_instance(CONTEXT_COURSE, $course->id))) {
echo ' <a title="'.$streditsummary.'" href="editsection.php?id='.$thissection->id.'">'.
- '<img src="'.$OUTPUT->pix_url('t/edit') . '" class="icon edit" alt="'.$streditsummary.'" /></a><br /><br />';
+ '<img src="'.$OUTPUT->pix_url('t/edit') . '" class="iconsmall edit" alt="'.$streditsummary.'" /></a><br /><br />';
}
echo '</div>';
* @global core_renderer $OUTPUT
* @staticvar type $str
* @param stdClass $mod The module to produce editing buttons for
- * @param bool $absolute If true an absolute link is produced (default true)
+ * @param bool $absolute_ignored ignored - all links are absolute
* @param bool $moveselect If true a move seleciton process is used (default true)
* @param int $indent The current indenting
* @param int $section The section to link back to
* @return string XHTML for the editing buttons
*/
-function make_editing_buttons(stdClass $mod, $absolute = true, $moveselect = true, $indent=-1, $section=-1) {
+function make_editing_buttons(stdClass $mod, $absolute_ignored = true, $moveselect = true, $indent=-1, $section=-1) {
global $CFG, $OUTPUT;
static $str;
$coursecontext = get_context_instance(CONTEXT_COURSE, $mod->course);
$modcontext = get_context_instance(CONTEXT_MODULE, $mod->id);
- // no permission to edit
- if (!has_capability('moodle/course:manageactivities', $modcontext)) {
+ $editcaps = array('moodle/course:manageactivities', 'moodle/course:activityvisibility', 'moodle/role:assign');
+ $dupecaps = array('moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport');
+
+ // no permission to edit anything
+ if (!has_any_capability($editcaps, $modcontext) and !has_all_capabilities($dupecaps, $coursecontext)) {
return false;
}
+ $hasmanageactivities = has_capability('moodle/course:manageactivities', $modcontext);
+
if (!isset($str)) {
$str = new stdClass;
$str->assign = get_string("assignroles", 'role');
$str->groupsvisible = get_string("groupsvisible");
}
- if ($absolute) {
- $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
- } else {
- $baseurl = new moodle_url('mod.php', array('sesskey' => sesskey()));
- }
+ $baseurl = new moodle_url('/course/mod.php', array('sesskey' => sesskey()));
if ($section >= 0) {
$baseurl->param('sr', $section);
$actions = array();
// leftright
- if (has_capability('moodle/course:update', $coursecontext)) {
+ if ($hasmanageactivities) {
if (right_to_left()) { // Exchange arrows on RTL
$rightarrow = 't/left';
$leftarrow = 't/right';
}
// move
- if (has_capability('moodle/course:update', $coursecontext)) {
+ if ($hasmanageactivities) {
if ($moveselect) {
$actions[] = new action_link(
new moodle_url($baseurl, array('copy' => $mod->id)),
}
// Update
- $actions[] = new action_link(
- new moodle_url($baseurl, array('update' => $mod->id)),
- new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall')),
- null,
- array('class' => 'editing_update', 'title' => $str->update)
- );
+ if ($hasmanageactivities) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('update' => $mod->id)),
+ new pix_icon('t/edit', $str->update, 'moodle', array('class' => 'iconsmall')),
+ null,
+ array('class' => 'editing_update', 'title' => $str->update)
+ );
+ }
// Duplicate (require both target import caps to be able to duplicate, see modduplicate.php)
- $dupecaps = array('moodle/backup:backuptargetimport', 'moodle/restore:restoretargetimport');
if (has_all_capabilities($dupecaps, $coursecontext)) {
$actions[] = new action_link(
new moodle_url($baseurl, array('duplicate' => $mod->id)),
}
// Delete
- $actions[] = new action_link(
- new moodle_url($baseurl, array('delete' => $mod->id)),
- new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall')),
- null,
- array('class' => 'editing_delete', 'title' => $str->delete)
- );
+ if ($hasmanageactivities) {
+ $actions[] = new action_link(
+ new moodle_url($baseurl, array('delete' => $mod->id)),
+ new pix_icon('t/delete', $str->delete, 'moodle', array('class' => 'iconsmall')),
+ null,
+ array('class' => 'editing_delete', 'title' => $str->delete)
+ );
+ }
// hideshow
if (has_capability('moodle/course:activityvisibility', $modcontext)) {
}
// groupmode
- if ($mod->groupmode !== false) {
+ if ($hasmanageactivities and $mod->groupmode !== false) {
if ($mod->groupmode == SEPARATEGROUPS) {
$groupmode = 0;
$grouptitle = $str->groupsseparate;
}
// Assign
- if (has_capability('moodle/course:managegroups', $modcontext)){
+ if (has_capability('moodle/role:assign', $modcontext)){
$actions[] = new action_link(
new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid' => $modcontext->id)),
new pix_icon('i/roles', $str->assign, 'moodle', array('class' => 'iconsmall')),
require("../config.php");
require_once("lib.php");
-require_login();
-
$sectionreturn = optional_param('sr', '', PARAM_INT);
$add = optional_param('add', '', PARAM_ALPHA);
$type = optional_param('type', '', PARAM_ALPHA);
}
$PAGE->set_url($url);
+require_login();
+
//check if we are adding / editing a module that has new forms using formslib
if (!empty($add)) {
$id = required_param('id', PARAM_INT);
redirect("$CFG->wwwroot/course/modedit.php?add=$add&type=$type&course=$id§ion=$section&return=$returntomod");
} else if (!empty($update)) {
- if (!$cm = get_coursemodule_from_id('', $update, 0, true)) {
- print_error('invalidcoursemodule');
- }
+ $cm = get_coursemodule_from_id('', $update, 0, true, MUST_EXIST);
$returntomod = optional_param('return', 0, PARAM_BOOL);
redirect("$CFG->wwwroot/course/modedit.php?update=$update&return=$returntomod");
$cm = get_coursemodule_from_id('', $duplicate, 0, true, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
- require_login($course->id);
+ require_login($course, false, $cm);
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
require_capability('moodle/course:manageactivities', $coursecontext);
}
} else if (!empty($delete)) {
- if (!$cm = get_coursemodule_from_id('', $delete, 0, true)) {
- print_error('invalidcoursemodule');
- }
+ $cm = get_coursemodule_from_id('', $delete, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
- if (!$course = $DB->get_record('course', array('id'=>$cm->course))) {
- print_error('invalidcourseid');
- }
- require_login($course->id); // needed to setup proper $COURSE
- $context = get_context_instance(CONTEXT_COURSE, $course->id);
+ require_login($course, false, $cm);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
- require_capability('moodle/course:manageactivities', $context);
+ require_capability('moodle/course:manageactivities', $modcontext);
$return = "$CFG->wwwroot/course/view.php?id=$cm->course#section-$cm->sectionnum";
if ((!empty($movetosection) or !empty($moveto)) and confirm_sesskey()) {
- if (!$cm = get_coursemodule_from_id('', $USER->activitycopy, 0, true)) {
- print_error('invalidcoursemodule');
- }
+ $cm = get_coursemodule_from_id('', $USER->activitycopy, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
if (!empty($movetosection)) {
if (!$section = $DB->get_record('course_sections', array('id'=>$movetosection, 'course'=>$cm->course))) {
}
}
- require_login($section->course); // needed to setup proper $COURSE
- $context = get_context_instance(CONTEXT_COURSE, $section->course);
- require_capability('moodle/course:manageactivities', $context);
-
if (!ismoving($section->course)) {
print_error('needcopy', '', "view.php?id=$section->course");
}
} else if (!empty($indent) and confirm_sesskey()) {
$id = required_param('id', PARAM_INT);
- if (!$cm = get_coursemodule_from_id('', $id, 0, true)) {
- print_error('invalidcoursemodule');
- }
- require_login($cm->course); // needed to setup proper $COURSE
- $context = get_context_instance(CONTEXT_COURSE, $cm->course);
- require_capability('moodle/course:manageactivities', $context);
+ $cm = get_coursemodule_from_id('', $id, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
$cm->indent += $indent;
}
} else if (!empty($hide) and confirm_sesskey()) {
- if (!$cm = get_coursemodule_from_id('', $hide, 0, true)) {
- print_error('invalidcoursemodule');
- }
+ $cm = get_coursemodule_from_id('', $hide, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
- require_login($cm->course); // needed to setup proper $COURSE
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
- require_capability('moodle/course:activityvisibility', $context);
+ require_login($course, false, $cm);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ require_capability('moodle/course:activityvisibility', $modcontext);
set_coursemodule_visible($cm->id, 0);
}
} else if (!empty($show) and confirm_sesskey()) {
- if (!$cm = get_coursemodule_from_id('', $show, 0, true)) {
- print_error('invalidcoursemodule');
- }
+ $cm = get_coursemodule_from_id('', $show, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
- require_login($cm->course); // needed to setup proper $COURSE
- $context = get_context_instance(CONTEXT_COURSE, $cm->course);
- require_capability('moodle/course:activityvisibility', $context);
+ require_login($course, false, $cm);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ require_capability('moodle/course:activityvisibility', $modcontext);
- if (!$section = $DB->get_record('course_sections', array('id'=>$cm->section))) {
- print_error('sectionnotexist');
- }
+ $section = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST);
- if (!$module = $DB->get_record('modules', array('id'=>$cm->module))) {
- print_error('moduledoesnotexist');
- }
+ $module = $DB->get_record('modules', array('id'=>$cm->module), '*', MUST_EXIST);
if ($module->visible and ($section->visible or (SITEID == $cm->course))) {
set_coursemodule_visible($cm->id, 1);
} else if ($groupmode > -1 and confirm_sesskey()) {
$id = required_param('id', PARAM_INT);
- if (!$cm = get_coursemodule_from_id('', $id, 0, true)) {
- print_error('invalidcoursemodule');
- }
- require_login($cm->course); // needed to setup proper $COURSE
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
- require_capability('moodle/course:manageactivities', $context);
+ $cm = get_coursemodule_from_id('', $id, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
+
+ require_login($course, false, $cm);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
set_coursemodule_groupmode($cm->id, $groupmode);
}
} else if (!empty($copy) and confirm_sesskey()) { // value = course module
- if (!$cm = get_coursemodule_from_id('', $copy, 0, true)) {
- print_error('invalidcoursemodule');
- }
+ $cm = get_coursemodule_from_id('', $copy, 0, true, MUST_EXIST);
+ $course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
- require_login($cm->course); // needed to setup proper $COURSE
- $context = get_context_instance(CONTEXT_COURSE, $cm->course);
- require_capability('moodle/course:manageactivities', $context);
+ require_login($course, false, $cm);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
- if (!$section = $DB->get_record('course_sections', array('id'=>$cm->section))) {
- print_error('sectionnotexist');
- }
+ $section = $DB->get_record('course_sections', array('id'=>$cm->section), '*', MUST_EXIST);
$USER->activitycopy = $copy;
$USER->activitycopycourse = $cm->course;
}
// make sure visibility is set correctly (in particular in calendar)
- set_coursemodule_visible($fromform->coursemodule, $fromform->visible);
+ if (has_capability('moodle/course:activityvisibility', $modcontext)) {
+ set_coursemodule_visible($fromform->coursemodule, $fromform->visible);
+ }
if (isset($fromform->cmidnumber)) { //label
// set cm idnumber - uniqueness is already verified by form validation
$DB->set_field('course_modules', 'section', $sectionid, array('id'=>$fromform->coursemodule));
// make sure visibility is set correctly (in particular in calendar)
+ // note: allow them to set it even without moodle/course:activityvisibility
set_coursemodule_visible($fromform->coursemodule, $fromform->visible);
if (isset($fromform->cmidnumber)) { //label
}
}
- if ($mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly') and empty($COURSE->groupmodeforce)) {
- $mform->disabledIf('groupingid', 'groupmode', 'eq', NOGROUPS);
-
- } else if (!$mform->elementExists('groupmode') and $mform->elementExists('groupmembersonly')) {
- $mform->disabledIf('groupingid', 'groupmembersonly', 'notchecked');
-
- } else if (!$mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly')) {
- // groupings have no use without groupmode or groupmembersonly
- if ($mform->elementExists('groupingid')) {
- $mform->removeElement('groupingid');
+ // Don't disable/remove groupingid if it is currently set to something,
+ // otherwise you cannot turn it off at same time as turning off other
+ // option (MDL-30764)
+ if (empty($this->_cm) || !$this->_cm->groupingid) {
+ if ($mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly') and empty($COURSE->groupmodeforce)) {
+ $mform->disabledIf('groupingid', 'groupmode', 'eq', NOGROUPS);
+
+ } else if (!$mform->elementExists('groupmode') and $mform->elementExists('groupmembersonly')) {
+ $mform->disabledIf('groupingid', 'groupmembersonly', 'notchecked');
+
+ } else if (!$mform->elementExists('groupmode') and !$mform->elementExists('groupmembersonly')) {
+ // groupings have no use without groupmode or groupmembersonly
+ if ($mform->elementExists('groupingid')) {
+ $mform->removeElement('groupingid');
+ }
}
}
$errors['availablefrom'] = get_string('badavailabledates', 'condition');
}
+ // Conditions: Verify that the grade conditions are numbers, and make sense.
+ if (array_key_exists('conditiongradegroup', $data)) {
+ foreach ($data['conditiongradegroup'] as $i => $gradedata) {
+ if ($gradedata['conditiongrademin'] !== '' && !is_numeric($gradedata['conditiongrademin'])) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradesmustbenumeric', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademax'] !== '' && !is_numeric($gradedata['conditiongrademax'])) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradesmustbenumeric', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademin'] !== '' && $gradedata['conditiongrademax'] !== '' &&
+ $gradedata['conditiongrademax'] < $gradedata['conditiongrademin']) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('badgradelimits', 'condition');
+ continue;
+ }
+ if ($gradedata['conditiongrademin'] === '' && $gradedata['conditiongrademax'] === '' &&
+ $gradedata['conditiongradeitemid']) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradeitembutnolimits', 'condition');
+ continue;
+ }
+ if (($gradedata['conditiongrademin'] !== '' || $gradedata['conditiongrademax'] !== '') &&
+ !$gradedata['conditiongradeitemid']) {
+ $errors["conditiongradegroup[{$i}]"] = get_string('gradelimitsbutnoitem', 'condition');
+ continue;
+ }
+ }
+ }
+
return $errors;
}
}
$mform->addElement('modvisible', 'visible', get_string('visible'));
+ if (!empty($this->_cm)) {
+ $context = get_context_instance(CONTEXT_MODULE, $this->_cm->id);
+ if (!has_capability('moodle/course:activityvisibility', $context)) {
+ $mform->hardFreeze('visible');
+ }
+ }
if ($this->_features->idnumber) {
$mform->addElement('text', 'cmidnumber', get_string('idnumbermod'));
$grouparray[] =& $mform->createElement('static', '', '','% '.get_string('grade_upto','condition').' ');
$grouparray[] =& $mform->createElement('text', 'conditiongrademax','',array('size'=>3));
$grouparray[] =& $mform->createElement('static', '', '','%');
- $mform->setType('conditiongrademin',PARAM_FLOAT);
- $mform->setType('conditiongrademax',PARAM_FLOAT);
$group = $mform->createElement('group','conditiongradegroup',
get_string('gradecondition', 'condition'),$grouparray);
$PAGE->set_url('/course/rest.php', array('courseId'=>$courseid,'class'=>$class));
+//NOTE: when making any changes here please make sure it is using the same access control as course/mod.php !!
+
+require_login();
+
// Authorise the user and verify some incoming data
if (!$course = $DB->get_record('course', array('id'=>$courseid))) {
error_log('AJAX commands.php: Course does not exist');
die;
}
-$context = get_context_instance(CONTEXT_COURSE, $course->id);
-require_login($course);
-require_capability('moodle/course:update', $context);
-
if (empty($CFG->enablecourseajax)) {
error_log('Course AJAX not allowed');
die;
switch ($class) {
case 'block':
-
- switch ($field) {
- case 'visible':
- blocks_execute_action($PAGE, $pageblocks, 'toggle', $blockinstance);
- break;
-
- case 'position': // Misleading case. Should probably call it 'move'.
- // We want to move the block around. This means changing
- // the column (position field) and/or block sort order
- // (weight field).
- blocks_move_block($PAGE, $blockinstance, $column, $value);
- break;
- }
+ // not used any more
break;
case 'section':
+ require_login($course);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ require_capability('moodle/course:update', $coursecontext);
if (!$DB->record_exists('course_sections', array('course'=>$course->id, 'section'=>$id))) {
error_log('AJAX commands.php: Bad Section ID '.$id);
error_log('AJAX commands.php: Bad course module ID '.$id);
die;
}
+ require_login($course, false, $cm);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
switch ($field) {
case 'visible':
+ require_capability('moodle/course:activityvisibility', $modcontext);
set_coursemodule_visible($cm->id, $value);
break;
case 'groupmode':
+ require_capability('moodle/course:manageactivities', $modcontext);
set_coursemodule_groupmode($cm->id, $value);
break;
case 'indentleft':
+ require_capability('moodle/course:manageactivities', $modcontext);
if ($cm->indent > 0) {
$cm->indent--;
$DB->update_record('course_modules', $cm);
break;
case 'indentright':
+ require_capability('moodle/course:manageactivities', $modcontext);
$cm->indent++;
$DB->update_record('course_modules', $cm);
break;
case 'move':
+ require_capability('moodle/course:manageactivities', $modcontext);
if (!$section = $DB->get_record('course_sections', array('course'=>$course->id, 'section'=>$sectionid))) {
error_log('AJAX commands.php: Bad section ID '.$sectionid);
die;
case 'course':
switch($field) {
case 'marker':
+ require_login($course);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+ require_capability('moodle/course:update', $coursecontext);
$newcourse = new stdClass();
$newcourse->id = $course->id;
$newcourse->marker = $value;
case 'DELETE':
switch ($class) {
case 'block':
- blocks_execute_action($PAGE, $pageblocks, 'delete', $blockinstance);
+ // not used any more
break;
case 'resource':
error_log('AJAX rest.php: Bad course module ID '.$id);
die;
}
+ require_login($course, false, $cm);
+ $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ require_capability('moodle/course:manageactivities', $modcontext);
$modlib = "$CFG->dirroot/mod/$cm->modname/lib.php";
if (file_exists($modlib)) {
die;
}
- $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
-
// remove all module files in case modules forget to do that
$fs = get_file_storage();
$fs->delete_area_files($modcontext->id);
$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
$personalcontext = get_context_instance(CONTEXT_USER, $user->id);
+$PAGE->set_url('/course/user.php', array('id'=>$id, 'user'=>$user->id, 'mode'=>$mode));
+
require_login();
$PAGE->set_pagelayout('admin');
if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) and !is_enrolled($coursecontext)) {
if ($PAGE->user_allowed_editing()) {
if (($edit == 1) and confirm_sesskey()) {
$USER->editing = 1;
- redirect($PAGE->url);
+ // Redirect to site root if Editing is toggled on frontpage
+ if ($course->id == SITEID) {
+ redirect($CFG->wwwroot .'/?redirect=0');
+ } else {
+ redirect($PAGE->url);
+ }
} else if (($edit == 0) and confirm_sesskey()) {
$USER->editing = 0;
if(!empty($USER->activitycopy) && $USER->activitycopycourse == $course->id) {
$USER->activitycopy = false;
$USER->activitycopycourse = NULL;
}
- redirect($PAGE->url);
+ // Redirect to site root if Editing is toggled on frontpage
+ if ($course->id == SITEID) {
+ redirect($CFG->wwwroot .'/?redirect=0');
+ } else {
+ redirect($PAGE->url);
+ }
}
- if ($hide && confirm_sesskey()) {
- set_section_visible($course->id, $hide, '0');
- }
+ if (has_capability('moodle/course:update', $context)) {
+ if ($hide && confirm_sesskey()) {
+ set_section_visible($course->id, $hide, '0');
+ }
- if ($show && confirm_sesskey()) {
- set_section_visible($course->id, $show, '1');
- }
+ if ($show && confirm_sesskey()) {
+ set_section_visible($course->id, $show, '1');
+ }
- if (!empty($section)) {
- if (!empty($move) and confirm_sesskey()) {
- if (!move_section($course, $section, $move)) {
- echo $OUTPUT->notification('An error occurred while moving a section');
+ if (!empty($section)) {
+ if (!empty($move) and confirm_sesskey()) {
+ if (!move_section($course, $section, $move)) {
+ echo $OUTPUT->notification('An error occurred while moving a section');
+ }
+ // Clear the navigation cache at this point so that the affects
+ // are seen immediately on the navigation.
+ $PAGE->navigation->clear_cache();
}
- // Clear the navigation cache at this point so that the affects
- // are seen immediately on the navigation.
- $PAGE->navigation->clear_cache();
}
}
} else {
if ($createcourses) {
require_once("$CFG->dirroot/course/lib.php");
- $template = $this->get_config('templatecourse');
+ $templatecourse = $this->get_config('templatecourse');
$defaultcategory = $this->get_config('defaultcategory');
- if ($template) {
- if ($template = $DB->get_record('course', array('shortname'=>$template))) {
+ $template = false;
+ if ($templatecourse) {
+ if ($template = $DB->get_record('course', array('shortname'=>$templatecourse))) {
unset($template->id);
unset($template->fullname);
unset($template->shortname);
unset($template->idnumber);
} else {
- $template = new stdClass();
+ if ($verbose) {
+ mtrace(" can not find template for new course!");
+ }
}
- } else {
+ }
+ if (!$template) {
+ $courseconfig = get_config('moodlecourse');
$template = new stdClass();
+ $template->summary = '';
+ $template->summaryformat = FORMAT_HTML;
+ $template->format = $courseconfig->format;
+ $template->numsections = $courseconfig->numsections;
+ $template->hiddensections = $courseconfig->hiddensections;
+ $template->newsitems = $courseconfig->newsitems;
+ $template->showgrades = $courseconfig->showgrades;
+ $template->showreports = $courseconfig->showreports;
+ $template->maxbytes = $courseconfig->maxbytes;
+ $template->groupmode = $courseconfig->groupmode;
+ $template->groupmodeforce = $courseconfig->groupmodeforce;
+ $template->visible = $courseconfig->visible;
+ $template->lang = $courseconfig->lang;
+ $template->groupmodeforce = $courseconfig->groupmodeforce;
}
if (!$DB->record_exists('course_categories', array('id'=>$defaultcategory))) {
+ if ($verbose) {
+ mtrace(" default course category does not exist!");
+ }
$categories = $DB->get_records('course_categories', array(), 'sortorder', 'id', 0, 1);
$first = reset($categories);
$defaultcategory = $first->id;
if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
$group->coursecode = trim($matches[1]);
}
- if (preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)) {
+ if (preg_match('{<description>.*?<long>(.*?)</long>.*?</description>}is', $tagcontents, $matches)){
$group->description = trim($matches[1]);
}
+ if (preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)) {
+ $group->shortName = trim($matches[1]);
+ }
if (preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)) {
$group->category = trim($matches[1]);
}
if (!$createnewcourses) {
$this->log_line("Course $coursecode not found in Moodle's course idnumbers.");
} else {
+ // Set shortname to description or description to shortname if one is set but not the other.
+ $nodescription = !isset($group->description);
+ $noshortname = !isset($group->shortName);
+ if ( $nodescription && $noshortname) {
+ // If neither short nor long description are set let if fail
+ $this->log_line("Neither long nor short name are set for $coursecode");
+ } else if ($nodescription) {
+ // If short and ID exist, then give the long short's value, then give short the ID's value
+ $group->description = $group->shortName;
+ $group->shortName = $coursecode;
+ } else if ($noshortname) {
+ // If long and ID exist, then map long to long, then give short the ID's value.
+ $group->shortName = $coursecode;
+ }
// Create the (hidden) course(s) if not found
$courseconfig = get_config('moodlecourse'); // Load Moodle Course shell defaults
$course = new stdClass();
$course->fullname = $group->description;
- $course->shortname = $coursecode;
+ $course->shortname = $group->shortName;;
$course->idnumber = $coursecode;
$course->format = $courseconfig->format;
$course->visible = $courseconfig->visible;
if ($confirm) {
$plugin->delete_instance($instance);
- $context->mark_dirty(); // invalidate all enrol caches
redirect($PAGE->url);
}
} else if ($action === 'disable') {
$instance = $instances[$instanceid];
- if ($instance->status == ENROL_INSTANCE_ENABLED) {
- $instance->status = ENROL_INSTANCE_DISABLED;
- $DB->update_record('enrol', $instance);
- $context->mark_dirty(); // invalidate all enrol caches
+ $plugin = $plugins[$instance->enrol];
+ if ($instance->status != ENROL_INSTANCE_DISABLED) {
+ $plugin->update_status($instance, ENROL_INSTANCE_DISABLED);
redirect($PAGE->url);
}
} else if ($action === 'enable') {
$instance = $instances[$instanceid];
- if ($instance->status == ENROL_INSTANCE_DISABLED) {
- $instance->status = ENROL_INSTANCE_ENABLED;
- $DB->update_record('enrol', $instance);
- $context->mark_dirty(); // invalidate all enrol caches
+ $plugin = $plugins[$instance->enrol];
+ if ($instance->status != ENROL_INSTANCE_ENABLED) {
+ $plugin->update_status($instance, ENROL_INSTANCE_ENABLED);
redirect($PAGE->url);
}
}
require_once("$CFG->dirroot/course/lib.php");
// Override defaults with template course
- $course = new stdClass();
+ $template = false;
if ($this->get_config('template')) {
- if($template = $DB->get_record('course', array('shortname'=>$this->get_config('template')))) {
+ if ($template = $DB->get_record('course', array('shortname'=>$this->get_config('template')))) {
unset($template->id); // So we are clear to reinsert the record
unset($template->fullname);
unset($template->shortname);
unset($template->idnumber);
- $course = $template;
}
}
+ if (!$template) {
+ $courseconfig = get_config('moodlecourse');
+ $template = new stdClass();
+ $template->summary = '';
+ $template->summaryformat = FORMAT_HTML;
+ $template->format = $courseconfig->format;
+ $template->numsections = $courseconfig->numsections;
+ $template->hiddensections = $courseconfig->hiddensections;
+ $template->newsitems = $courseconfig->newsitems;
+ $template->showgrades = $courseconfig->showgrades;
+ $template->showreports = $courseconfig->showreports;
+ $template->maxbytes = $courseconfig->maxbytes;
+ $template->groupmode = $courseconfig->groupmode;
+ $template->groupmodeforce = $courseconfig->groupmodeforce;
+ $template->visible = $courseconfig->visible;
+ $template->lang = $courseconfig->lang;
+ $template->groupmodeforce = $courseconfig->groupmodeforce;
+ }
+ $course = $template;
$course->category = $this->get_config('category');
if (!$DB->record_exists('course_categories', array('id'=>$this->get_config('category')))) {
//--- role mapping settings ---
$settings->add(new admin_setting_heading('enrol_ldap_roles', get_string('roles', 'enrol_ldap'), ''));
if (!during_initial_install()) {
- $settings->add(new admin_setting_ldap_rolemapping('enrol_ldap/role_mapping', get_string ('role_mapping_key', 'enrol_ldap', $role->name), get_string ('role_mapping', 'enrol_ldap'), ''));
+ $settings->add(new admin_setting_ldap_rolemapping('enrol_ldap/role_mapping', get_string ('role_mapping_key', 'enrol_ldap'), get_string ('role_mapping', 'enrol_ldap'), ''));
}
$options = $yesno;
$settings->add(new admin_setting_configselect('enrol_ldap/course_search_sub', get_string('course_search_sub_key', 'enrol_ldap'), get_string('course_search_sub', 'enrol_ldap'), 0, $options));
if (hidden) {
bb.setStyle('top', '-1000px').removeClass(DIALOGUE_PREFIX+'-hidden');
}
- var x = Math.round((bb.get('winWidth') - bb.get('offsetWidth'))/2);
- var y = Math.round((bb.get('winHeight') - bb.get('offsetHeight'))/2)+Y.one(window).get('scrollTop');
+ var x = Math.max(Math.round((bb.get('winWidth') - bb.get('offsetWidth'))/2), 15);
+ var y = Math.max(Math.round((bb.get('winHeight') - bb.get('offsetHeight'))/2), 15) + Y.one(window).get('scrollTop');
+
if (hidden) {
bb.addClass(DIALOGUE_PREFIX+'-hidden');
}
* @param boolean $return Whether to return (true) or echo (false) the HTML generated by this function
* @param string $bodytags Additional attributes that will be added to the <body> tag
* @param string $buttons Additional buttons to display on the page
+ * @param boolean $shownavigation should the gradebook navigation drop down (or tabs) be shown?
*
* @return string HTML code or nothing if $return == false
*/
if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_DROPDOWN) {
$returnval .= print_grade_plugin_selector($plugin_info, $active_type, $active_plugin, $return);
}
- $returnval .= $OUTPUT->heading($heading);
+
+ if ($return) {
+ $returnval .= $OUTPUT->heading($heading);
+ } else {
+ echo $OUTPUT->heading($heading);
+ }
+
if ($CFG->grade_navmethod == GRADE_NAVMETHOD_COMBO || $CFG->grade_navmethod == GRADE_NAVMETHOD_TABS) {
$returnval .= grade_print_tabs($active_type, $active_plugin, $plugin_info, $return);
}
$usercell->scope = 'row';
if ($showuserimage) {
- $usercell->text = $OUTPUT->container($OUTPUT->user_picture($user), 'userpic');
+ $usercell->text = $OUTPUT->user_picture($user);
}
$usercell->text .= html_writer::link(new moodle_url('/user/view.php', array('id' => $user->id, 'course' => $this->course->id)), fullname($user));
background-color:#fff;
}
-.path-grade-report-grader div.userpic {
-margin-right:10px;
-float:left;
-}
-
-.path-grade-report-grader div.userpic img {
+.path-grade-report-grader th.user img {
border:3px double #cecece;
-vertical-align:middle;
+vertical-align:top;
width:2.7em;
height:2.7em;
+margin-right:10px;
}
.path-grade-report-grader a.quickedit {
while ($userdata = $gui->next_user()) {
$user = $userdata->user;
$report = new grade_report_user($courseid, $gpr, $context, $user->id);
- echo $OUTPUT->heading(get_string('pluginname', 'gradereport_user'). ' - '.fullname($report->user));
+
+ $studentnamelink = html_writer::link(new moodle_url('/user/view.php', array('id' => $report->user->id, 'course' => $courseid)), fullname($report->user));
+ echo $OUTPUT->heading(get_string('pluginname', 'gradereport_user') . ' - ' . $studentnamelink);
if ($report->fill_table()) {
echo '<br />'.$report->print_table(true);
$gui->close();
} else { // Only show one user's report
$report = new grade_report_user($courseid, $gpr, $context, $userid);
- print_grade_page_head($courseid, 'report', 'user', get_string('pluginname', 'gradereport_user'). ' - '.fullname($report->user));
+
+ $studentnamelink = html_writer::link(new moodle_url('/user/view.php', array('id' => $report->user->id, 'course' => $courseid)), fullname($report->user));
+ print_grade_page_head($courseid, 'report', 'user', get_string('pluginname', 'gradereport_user') . ' - ' . $studentnamelink);
groups_print_course_menu($course, $gpr->get_return_url('index.php?id='.$courseid, array('userid'=>0)));
if ($user_selector) {
The difference between access from/to dates and availability settings for the activity is that outside the set dates the latter allows students to view the activity description, whereas access from/to dates prevent access completely.';
$string['availableuntil'] = 'Allow access until';
$string['badavailabledates'] = 'Invalid dates. If you set both dates, the \'Allow access from\' date should be before the \'until\' date.';
+$string['badgradelimits'] = 'If you set both an upper and lower grade limit, the upper limit must be higher than the lower limit.';
$string['completion_complete'] = 'must be marked complete';
$string['completioncondition'] = 'Activity completion condition';
$string['completioncondition_help'] = 'This setting determines any activity completion conditions which must be met in order to access the activity. Note that completion tracking must first be set before an activity completion condition can be set.
Multiple grade conditions may be set if desired. If so, the activity will only allow access when ALL grade conditions are met.';
$string['grade_upto'] = 'and less than';
+$string['gradeitembutnolimits'] = 'You must enter an upper or lower limit, or both.';
+$string['gradelimitsbutnoitem'] = 'You must choose a grade item.';
+$string['gradesmustbenumeric'] = 'The minimum and maximum grades must be numeric (or blank).';
$string['none'] = '(none)';
$string['notavailableyet'] = 'Not available yet';
$string['requires_completion_0'] = 'Not available unless the activity <strong>{$a}</strong> is incomplete.';
$string['xmldberror'] = 'XMLDB error!';
$string['alreadyloggedin'] = 'You are already logged in as {$a}, you need to log out before logging in as different user.';
$string['youcannotdeletecategory'] = 'You cannot delete category \'{$a}\' because you can neither delete the contents, nor move them elsewhere.';
+$string['protected_cc_not_supported'] = 'Protected cartridges not supported.';
$string['dbcreationerror'] = 'Database creation error. Could not create the given database name with the settings provided';
$string['dbhost'] = 'Host server';
$string['dbpass'] = 'Password';
+$string['dbport'] = 'Port';
$string['dbprefix'] = 'Tables prefix';
$string['dbtype'] = 'Type';
$string['dbwrongencoding'] = 'The selected database is running under one non-recommended encoding ({$a}). It would be better to use one Unicode (UTF-8) encoded database instead. Anyway, you can bypass this test by selecting the "Skip DB Encoding Test" check below, but you could experience problems in the future.';
$string['addhost'] = 'Add host';
$string['addnewhost'] = 'Add a new host';
$string['addtoacl'] = 'Add to access control';
+$string['allhosts'] = 'All hosts';
$string['allhosts_no_options'] = 'No options are available when viewing multiple hosts';
$string['allow'] = 'Allow';
$string['applicationtype'] = 'Application type';
$string['databaseerror'] = 'Could not write details to the database.';
$string['deleteaserver'] = 'Deleting a server';
$string['deletehost'] = 'Delete host';
+$string['deletedhostinfo'] = 'This host has been deleted. If you want to undelete it, switch the deleted status back to \'No\'.';
+$string['deletedhosts'] = 'Deleted hosts: {$a}';
$string['deletekeycheck'] = 'Are you absolutely sure you want to delete this key?';
$string['deleteoutoftime'] = 'Your 60-second window for deleting this key has expired. Please start again.';
$string['deleteuserrecord'] = 'SSO ACL: delete record for user \'{$a->user}\' from {$a->host}.';
$string['hideremote'] = 'Hide remote users';
$string['host'] = 'host';
$string['hostcoursenotfound'] = 'Host or course not found';
-$string['hostdeleted'] = 'Ok - host deleted';
+$string['hostdeleted'] = 'Host deleted';
$string['hostexists'] = 'A record already exists for a host with that hostname (it may be deleted). <a href="{$a}">click here</a> to edit that record.';
$string['hostlist'] = 'List of networked hosts';
$string['hostname'] = 'Hostname';
$string['externalserviceusers'] = 'External service users';
$string['failedtolog'] = 'Failed to log';
$string['filenameexist'] = 'File name already exists: {$a}';
+$string['forbiddenwsuser'] = 'Can not create token for an unconfirmed, deleted, suspended or guest user.';
$string['function'] = 'Function';
$string['functions'] = 'Functions';
$string['generalstructure'] = 'General structure';
$string['userasclients'] = 'Users as clients with token';
$string['userasclientsdescription'] = 'The following steps help you to set up the Moodle web service for users as clients. These steps also help to set up the recommended token (security keys) authentication method. In this use case, the user will generate his token from the security keys page via My profile settings.';
$string['usermissingcaps'] = 'Missing capabilities: {$a}';
+$string['usernameorid'] = 'Username / User id';
+$string['usernameorid_help'] = 'Enter a username or a user id.';
+$string['usernameoridnousererror'] = 'No users were found with this username/user id.';
+$string['usernameoridoccurenceerror'] = 'More than one user was found with this username. Please enter the user id.';
$string['usernotallowed'] = 'The user is not allowed for this service. First you need to allow this user on the {$a}\'s allowed users administration page.';
$string['usersettingssaved'] = 'User settings saved';
$string['validuntil'] = 'Valid until';
if (!empty($CFG->defaultuserroleid)) {
$syscontext = context_system::instance();
$accessdata['ra'][$syscontext->path][(int)$CFG->defaultuserroleid] = (int)$CFG->defaultuserroleid;
- $raparents[$CFG->defaultuserroleid][$syscontext->path] = $syscontext->path;
+ $raparents[$CFG->defaultuserroleid][$syscontext->id] = $syscontext->id;
}
// load the "default frontpage role"
$frontpagecontext = context_course::instance(get_site()->id);
if ($frontpagecontext->path) {
$accessdata['ra'][$frontpagecontext->path][(int)$CFG->defaultfrontpageroleid] = (int)$CFG->defaultfrontpageroleid;
- $raparents[$CFG->defaultfrontpageroleid][$frontpagecontext->path] = $frontpagecontext->path;
+ $raparents[$CFG->defaultfrontpageroleid][$frontpagecontext->id] = $frontpagecontext->id;
}
}
// preload every assigned role at and above course context
- $sql = "SELECT ctx.path, ra.roleid
+ $sql = "SELECT ctx.path, ra.roleid, ra.contextid
FROM {role_assignments} ra
- JOIN {context} ctx ON ctx.id = ra.contextid
- LEFT JOIN {context} cctx
- ON (cctx.contextlevel = ".CONTEXT_COURSE." AND ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'").")
- WHERE ra.userid = :userid AND cctx.id IS NULL";
-
-
+ JOIN {context} ctx
+ ON ctx.id = ra.contextid
+ LEFT JOIN {block_instances} bi
+ ON (ctx.contextlevel = ".CONTEXT_BLOCK." AND bi.id = ctx.instanceid)
+ LEFT JOIN {context} bpctx
+ ON (bpctx.id = bi.parentcontextid)
+ WHERE ra.userid = :userid
+ AND (ctx.contextlevel <= ".CONTEXT_COURSE." OR bpctx.contextlevel < ".CONTEXT_COURSE.")";
$params = array('userid'=>$userid);
$rs = $DB->get_recordset_sql($sql, $params);
foreach ($rs as $ra) {
// RAs leafs are arrays to support multi-role assignments...
$accessdata['ra'][$ra->path][(int)$ra->roleid] = (int)$ra->roleid;
- $raparents[$ra->roleid][$ra->path] = $ra->path;
+ $raparents[$ra->roleid][$ra->contextid] = $ra->contextid;
}
$rs->close();
// now get overrides of interesting roles in all interesting child contexts
// hopefully we will not run out of SQL limits here,
- // users would have to have very many roles above course context...
+ // users would have to have very many roles at/above course context...
$sqls = array();
$params = array();
static $cp = 0;
- foreach ($raparents as $roleid=>$paths) {
+ foreach ($raparents as $roleid=>$ras) {
$cp++;
- list($paths, $rparams) = $DB->get_in_or_equal($paths, SQL_PARAMS_NAMED, 'p'.$cp.'_');
- $params = array_merge($params, $rparams);
+ list($sqlcids, $cids) = $DB->get_in_or_equal($ras, SQL_PARAMS_NAMED, 'c'.$cp.'_');
+ $params = array_merge($params, $cids);
$params['r'.$cp] = $roleid;
$sqls[] = "(SELECT ctx.path, rc.roleid, rc.capability, rc.permission
FROM {role_capabilities} rc
JOIN {context} ctx
ON (ctx.id = rc.contextid)
- LEFT JOIN {context} cctx
- ON (cctx.contextlevel = ".CONTEXT_COURSE."
- AND ctx.path LIKE ".$DB->sql_concat('cctx.path',"'/%'").")
JOIN {context} pctx
- ON (pctx.path $paths
+ ON (pctx.id $sqlcids
AND (ctx.id = pctx.id
OR ctx.path LIKE ".$DB->sql_concat('pctx.path',"'/%'")."
OR pctx.path LIKE ".$DB->sql_concat('ctx.path',"'/%'")."))
+ LEFT JOIN {block_instances} bi
+ ON (ctx.contextlevel = ".CONTEXT_BLOCK." AND bi.id = ctx.instanceid)
+ LEFT JOIN {context} bpctx
+ ON (bpctx.id = bi.parentcontextid)
WHERE rc.roleid = :r{$cp}
- AND cctx.id IS NULL)";
+ AND (ctx.contextlevel <= ".CONTEXT_COURSE." OR bpctx.contextlevel < ".CONTEXT_COURSE.")
+ )";
}
// fixed capability order is necessary for rdef dedupe
$timemodified = time();
}
-/// Check for existing entry
+ // Check for existing entry
+ // TODO: Revisit this sql_empty() use once Oracle bindings are improved. MDL-29765
+ $component = ($component === '') ? $DB->sql_empty() : $component;
$ras = $DB->get_records('role_assignments', array('roleid'=>$roleid, 'contextid'=>$context->id, 'userid'=>$userid, 'component'=>$component, 'itemid'=>$itemid), 'id');
if ($ras) {
}
}
+ // TODO: Revisit this sql_empty() use once Oracle bindings are improved. MDL-29765
+ if (isset($params['component'])) {
+ $params['component'] = ($params['component'] === '') ? $DB->sql_empty() : $params['component'];
+ }
$ras = $DB->get_records('role_assignments', $params);
foreach($ras as $ra) {
$DB->delete_records('role_assignments', array('id'=>$ra->id));
$extracaps = array_merge($subcaps, $extracaps);
- // All modules allow viewhiddenactivities. This is so you can hide
- // the module then override to allow specific roles to see it.
- // The actual check is in course page so not module-specific
- $extracaps[] = "moodle/course:viewhiddenactivities";
list($extra, $params) = $DB->get_in_or_equal(
$extracaps, SQL_PARAMS_NAMED, 'cap0');
$extra = "OR name $extra";
$sql = "SELECT *
FROM {capabilities}
WHERE (contextlevel = ".CONTEXT_MODULE."
- AND component = :component)
+ AND (component = :component OR component = 'moodle'))
$extra";
$params['component'] = "mod_$module->name";
var buttons = commandContainer.getElementsByTagName('a');
// Buttons that we might need to add back in.
+ var deletePresent = false;
+ var hideshow = false;
+ var movehandle = false;
var moveLeft = false;
var moveRight = false;
var updateButton = null;
this.groupmode = this.SEPARATEGROUPS;
} else if (buttons[x].className == 'editing_groupsvisible') {
this.groupmode = this.VISIBLEGROUPS;
+ } else if (buttons[x].className == 'editing_delete') {
+ deletePresent = true;
+ } else if (buttons[x].className == 'editing_hide') {
+ hideshow = true;
+ } else if (buttons[x].className == 'editing_show') {
+ hideshow = true;
+ } else if (buttons[x].className == 'editing_moveup') {
+ movehandle = true;
+ } else if (buttons[x].className == 'editing_movedown') {
+ movehandle = true;
+ } else if (buttons[x].className == 'editing_move') {
+ movehandle = true;
}
}
- if (updateButton == null) {
- // Update button must always be present.
- YAHOO.log('Cannot find updateButton for '+this.getEl().id, 'error');
- }
-
// Clear all the buttons.
commandContainer.innerHTML = '';
// Add move-handle for drag and drop.
- var handleRef = main.mk_button('a', main.portal.icons['move_2d'], main.portal.strings['move'],
- [['style', 'cursor:move']], [['class', 'iconsmall']]);
+ if (movehandle) {
+ var handleRef = main.mk_button('a', main.portal.icons['move_2d'], main.portal.strings['move'],
+ [['style', 'cursor:move']], [['class', 'iconsmall']]);
- YAHOO.util.Dom.generateId(handleRef, 'sectionHandle');
- this.handle = handleRef;
- commandContainer.appendChild(handleRef);
- this.setHandleElId(this.handle.id);
+ YAHOO.util.Dom.generateId(handleRef, 'sectionHandle');
+ this.handle = handleRef;
+ commandContainer.appendChild(handleRef);
+ this.setHandleElId(this.handle.id);
+ }
// Add indentation buttons if needed (move left, move right).
if (moveLeft) {
this.indentRightButton = button;
}
- // Add edit button back in.
- commandContainer.appendChild(updateButton);
+ if (updateButton) {
+ // Add edit button back in.
+ commandContainer.appendChild(updateButton);
+ }
if (duplicateButton) {
commandContainer.appendChild(duplicateButton);
}
// Add the delete button.
- var button = main.mk_button('a', main.portal.icons['delete'], main.portal.strings['delete'], null, [['class', 'iconsmall']]);
- YAHOO.util.Event.addListener(button, 'click', this.delete_button, this, true);
- commandContainer.appendChild(button);
+ if (deletePresent) {
+ var button = main.mk_button('a', main.portal.icons['delete'], main.portal.strings['delete'], null, [['class', 'iconsmall']]);
+ YAHOO.util.Event.addListener(button, 'click', this.delete_button, this, true);
+ commandContainer.appendChild(button);
+ }
// Add the hide or show button.
- if (this.hidden) {
- var button = main.mk_button('a', main.portal.icons['show'], main.portal.strings['show'], null, [['class', 'iconsmall']]);
- } else {
- var button = main.mk_button('a', main.portal.icons['hide'], main.portal.strings['hide'], null, [['class', 'iconsmall']]);
+ if (hideshow) {
+ if (this.hidden) {
+ var button = main.mk_button('a', main.portal.icons['show'], main.portal.strings['show'], null, [['class', 'iconsmall']]);
+ } else {
+ var button = main.mk_button('a', main.portal.icons['hide'], main.portal.strings['hide'], null, [['class', 'iconsmall']]);
+ }
+ YAHOO.util.Event.addListener(button, 'click', this.toggle_hide, this, true);
+ commandContainer.appendChild(button);
+ this.viewButton = button;
}
- YAHOO.util.Event.addListener(button, 'click', this.toggle_hide, this, true);
- commandContainer.appendChild(button);
- this.viewButton = button;
// Add the groupmode button if needed.
if (this.groupmode != null) {
} else {
$newweight = ceil($newweight);
for ($weight = $bestgap - 1; $weight >= $newweight; $weight--) {
- foreach ($usedweights[$weight] as $biid) {
- $this->reposition_block($biid, $newregion, $weight + 1);
+ if (array_key_exists($weight, $usedweights)) {
+ foreach ($usedweights[$weight] as $biid) {
+ $this->reposition_block($biid, $newregion, $weight + 1);
+ }
}
}
$this->reposition_block($block->instance->id, $newregion, $newweight);
'riskbitmask' => RISK_XSS,
'captype' => 'write',
- 'contextlevel' => CONTEXT_COURSE,
+ 'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
'moodle/course:activityvisibility' => array(
'captype' => 'write',
- 'contextlevel' => CONTEXT_COURSE,
+ 'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
'moodle/course:viewhiddenactivities' => array(
'captype' => 'write',
- 'contextlevel' => CONTEXT_COURSE,
+ 'contextlevel' => CONTEXT_MODULE,
'archetypes' => array(
'teacher' => CAP_ALLOW,
'editingteacher' => CAP_ALLOW,
defined('MOODLE_INTERNAL') || die();
function xmldb_main_install() {
- global $CFG, $DB, $SITE;
+ global $CFG, $DB, $SITE, $OUTPUT;
/// Make sure system context exists
$syscontext = context_system::instance(0, MUST_EXIST, false);
$guest->timemodified= time();
$guest->id = $DB->insert_record('user', $guest);
if ($guest->id != 1) {
- throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new guest user id!');
+ echo $OUTPUT->notification('Unexpected id generated for the Guest account. Your database configuration or clustering setup may not be fully supported', 'notifyproblem');
}
// Store guest id
set_config('siteguest', $guest->id);
$admin->timemodified = time();
$admin->lastip = CLI_SCRIPT ? '0.0.0.0' : getremoteaddr(); // installation hijacking prevention
$admin->id = $DB->insert_record('user', $admin);
+
if ($admin->id != 2) {
- throw new moodle_exception('generalexceptionmessage', 'error', '', 'Unexpected new admin user id!');
+ echo $OUTPUT->notification('Unexpected id generated for the Admin account. Your database configuration or clustering setup may not be fully supported', 'notifyproblem');
+ }
+ if ($admin->id != ($guest->id + 1)) {
+ echo $OUTPUT->notification('Nonconsecutive id generated for the Admin account. Your database configuration or clustering setup may not be fully supported.', 'notifyproblem');
}
- // Store list of admins
+
+ /// Store list of admins
set_config('siteadmins', $admin->id);
// Make sure user context exists
context_user::instance($admin->id);
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20111118" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20111214" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="userid"/>
<FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="name"/>
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="userid" NEXT="value"/>
- <FIELD NAME="value" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="name"/>
+ <FIELD NAME="value" TYPE="char" LENGTH="1333" NOTNULL="true" SEQUENCE="false" PREVIOUS="name"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id"/>
// Moodle v2.2.0 release upgrade line
// Put any upgrade step following this
+ if ($oldversion < 2011120500.02) {
+
+ upgrade_set_timeout(60*20); // This may take a while
+ // MDL-28180. Some missing restrictions in certain backup & restore operations
+ // were causing incorrect duplicates in the course_completion_aggr_methd table.
+ // This upgrade step takes rid of them.
+ $sql = 'SELECT course, criteriatype, MIN(id) AS minid
+ FROM {course_completion_aggr_methd}
+ GROUP BY course, criteriatype
+ HAVING COUNT(*) > 1';
+ $duprs = $DB->get_recordset_sql($sql);
+ foreach ($duprs as $duprec) {
+ // We need to handle NULLs in criteriatype diferently
+ if (is_null($duprec->criteriatype)) {
+ $where = 'course = ? AND criteriatype IS NULL AND id > ?';
+ $params = array($duprec->course, $duprec->minid);
+ } else {
+ $where = 'course = ? AND criteriatype = ? AND id > ?';
+ $params = array($duprec->course, $duprec->criteriatype, $duprec->minid);
+ }
+ $DB->delete_records_select('course_completion_aggr_methd', $where, $params);
+ }
+ $duprs->close();
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2011120500.02);
+ }
+
+ if ($oldversion < 2011120500.03) {
+
+ // Changing precision of field value on table user_preferences to (1333)
+ $table = new xmldb_table('user_preferences');
+ $field = new xmldb_field('value', XMLDB_TYPE_CHAR, '1333', null, XMLDB_NOTNULL, null, null, 'name');
+
+ // Launch change of precision for field value
+ $dbman->change_field_precision($table, $field);
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2011120500.03);
+ }
+
return true;
}
$this->assertTrue($DB->execute($sql, array('3')));
$this->assertEqual($DB->count_records($tablename1, array('course' => 6)), 2);
+ // update records with subquery condition
+ // confirm that the option not using table aliases is cross-db
+ $sql = "UPDATE {{$tablename1}}
+ SET course = 0
+ WHERE NOT EXISTS (
+ SELECT course
+ FROM {{$tablename2}} tbl2
+ WHERE tbl2.course = {{$tablename1}}.course
+ AND 1 = 0)"; // Really we don't update anything, but verify the syntax is allowed
+ $this->assertTrue($DB->execute($sql));
+
// insert from one into second table
$sql = "INSERT INTO {{$tablename2}} (course)
return null;
}
+ /**
+ * Update instance status
+ *
+ * Override when plugin needs to do some action when enabled or disabled.
+ *
+ * @param stdClass $instance
+ * @param int $newstatus ENROL_INSTANCE_ENABLED, ENROL_INSTANCE_DISABLED
+ * @return void
+ */
+ public function update_status($instance, $newstatus) {
+ global $DB;
+
+ $instance->status = $newstatus;
+ $DB->update_record('enrol', $instance);
+
+ // invalidate all enrol caches
+ $context = context_course::instance($instance->courseid);
+ $context->mark_dirty();
+ }
+
/**
* Delete course enrol plugin instance, unenrol all users.
* @param object $instance
// finally drop the enrol row
$DB->delete_records('enrol', array('id'=>$instance->id));
+
+ // invalidate all enrol caches
+ $context = context_course::instance($instance->courseid);
+ $context->mark_dirty();
}
/**
}
/// Now search the version we are using
- $current_version = normalize_version(get_config('', 'release'));
+ $release = get_config('', 'release');
+ $current_version = normalize_version($release);
+ if (strpos($release, 'dev') !== false) {
+ // when final version is required, dev is NOT enough!
+ $current_version = $current_version - 0.1;
+ }
/// And finally compare them, saving results
if (version_compare($current_version, $needed_version, '>=')) {
$result->setStatus(false);
}
$result->setLevel('required');
- $result->setCurrentVersion($current_version);
+ $result->setCurrentVersion($release);
$result->setNeededVersion($needed_version);
return $result;
}
function getValue() {
- return $this->getAttribute('value');
+ return $this->_values;
}
function getMaxbytes() {
return $str;
}
+ /**
+ * What to display when element is frozen.
+ *
+ * @return empty string
+ */
+ function getFrozenHtml() {
+
+ return '';
+ }
}
*/
function _convert_pre(&$text)
{
- while(preg_match('/<pre[^>]*>(.*)<\/pre>/ismU', $text, $matches)) {
- $result = preg_replace($this->pre_search, $this->pre_replace, $matches[1]);
- $text = preg_replace('/<pre[^>]*>.*<\/pre>/ismU', '<div><br>' . $result . '<br></div>', $text, 1);
+ while (preg_match('/<pre[^>]*>(.*)<\/pre>/ismU', $text, $matches)) {
+ // convert the content
+ $this->pre_content = sprintf('<div><br>%s<br></div>',
+ preg_replace($this->pre_search, $this->pre_replace, $matches[1]));
+ // replace the content (use callback because content can contain $0 variable)
+ $text = preg_replace_callback('/<pre[^>]*>.*<\/pre>/ismU',
+ array('html2text', '_preg_pre_callback'), $text, 1);
+ // free memory
+ $this->pre_content = '';
}
}
}
}
+ /**
+ * Callback function for preg_replace_callback use in PRE content handler.
+ *
+ * @param array PREG matches
+ * @return string
+ */
+ private function _preg_pre_callback($matches)
+ {
+ return $this->pre_content;
+ }
+
/**
* Strtoupper multibyte wrapper function
*
throw new coding_exception('Invalid value in set_user_preference() call, arrays are not allowed');
}
$value = (string)$value;
+ if (textlib::strlen($value) > 1333) { //value column maximum length is 1333 characters
+ throw new coding_exception('Invalid value in set_user_preference() call, value is is too long for the value column');
+ }
if (is_null($user)) {
$user = $USER;
$access = false;
- if (is_viewing($coursecontext, $USER)) {
+ if (is_role_switched($course->id)) {
+ // ok, user had to be inside this course before the switch
+ $access = true;
+
+ } else if (is_viewing($coursecontext, $USER)) {
// ok, no need to mess with enrol
$access = true;
* @return string The now encrypted data
*/
function rc4encrypt($data) {
- $password = 'nfgjeingjk';
+ $password = get_site_identifier();
return endecrypt($password, $data, '');
}
* @return string The now decrypted data
*/
function rc4decrypt($data) {
- $password = 'nfgjeingjk';
+ $password = get_site_identifier();
return endecrypt($password, $data, 'de');
}
return $plugins;
}
+/**
+* Invoke plugin's callback functions
+*
+* @param string $type plugin type e.g. 'mod'
+* @param string $name plugin name
+* @param string $feature feature name
+* @param string $action feature's action
+* @param array $params parameters of callback function, should be an array
+* @param mixed $default default value if callback function hasn't been defined, or if it retursn null.
+* @return mixed
+*
+* @todo Decide about to deprecate and drop plugin_callback() - MDL-30743
+*/
+function plugin_callback($type, $name, $feature, $action, $params = null, $default = null) {
+ return component_callback($type . '_' . $name, $feature . '_' . $action, (array) $params, $default);
+}
/**
- * invoke plugin's callback functions
+ * Invoke component's callback functions
*
- * @param string $type Plugin type e.g. 'mod'
- * @param string $name Plugin name
- * @param string $feature Feature name
- * @param string $action Feature's action
- * @param string $options parameters of callback function, should be an array
- * @param mixed $default default value if callback function hasn't been defined
+ * @param string $component frankenstyle component name, e.g. 'mod_quiz'
+ * @param string $function the rest of the function name, e.g. 'cron' will end up calling 'mod_quiz_cron'
+ * @param array $params parameters of callback function
+ * @param mixed $default default value if callback function hasn't been defined, or if it retursn null.
* @return mixed
*/
-function plugin_callback($type, $name, $feature, $action, $options = null, $default=null) {
+function component_callback($component, $function, array $params = array(), $default = null) {
global $CFG; // this is needed for require_once() bellow
- $component = clean_param($type . '_' . $name, PARAM_COMPONENT);
- if (empty($component)) {
- throw new coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
+ $cleancomponent = clean_param($component, PARAM_COMPONENT);
+ if (empty($cleancomponent)) {
+ throw new coding_exception('Invalid component used in plugin/component_callback():' . $component);
}
+ $component = $cleancomponent;
list($type, $name) = normalize_component($component);
$component = $type . '_' . $name;
- $function = $component.'_'.$feature.'_'.$action;
- $oldfunction = $name.'_'.$feature.'_'.$action;
+ $oldfunction = $name.'_'.$function;
+ $function = $component.'_'.$function;
$dir = get_component_directory($component);
if (empty($dir)) {
- throw new coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
+ throw new coding_exception('Invalid component used in plugin/component_callback():' . $component);
}
// Load library and look for function
if (function_exists($function)) {
// Function exists, so just return function result
- $ret = call_user_func_array($function, (array)$options);
+ $ret = call_user_func_array($function, $params);
if (is_null($ret)) {
return $default;
} else {
}
}
if ($gradeaccess) {
- $reporttab->add(get_string('grade'), new moodle_url('/course/user.php', array('mode'=>'grade', 'id'=>$course->id)));
+ $reporttab->add(get_string('grade'), new moodle_url('/course/user.php', array('mode'=>'grade', 'id'=>$course->id, 'user'=>$usercontext->instanceid)));
}
}
// Check the number of nodes in the report node... if there are none remove the node
* @return moodle_url
*/
public function get_url(moodle_page $page, renderer_base $renderer = null) {
- global $CFG, $FULLME;
+ global $CFG;
if (is_null($renderer)) {
$renderer = $page->get_renderer('core');
// Build a gravatar URL with what we know.
// If the currently requested page is https then we'll return an
// https gravatar page.
- if (strpos($FULLME, 'https://') === 0) {
+ if (strpos($CFG->httpswwwroot, 'https:') === 0) {
$imageurl = new moodle_url("https://secure.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $imageurl->out(false)));
} else {
$imageurl = new moodle_url("http://www.gravatar.com/avatar/{$md5}", array('s' => $size, 'd' => $imageurl->out(false)));
if (debugging('', DEBUG_DEVELOPER)) {
$this->yui3loader->filter = YUI_RAW; // for more detailed logging info use YUI_DEBUG here
$this->yui2loader->filter = YUI_RAW; // for more detailed logging info use YUI_DEBUG here
+ $this->yui2loader->allowRollups = false;
} else {
$this->yui3loader->filter = null;
$this->yui2loader->filter = null;
$this->M_yui_loader->base = $this->yui3loader->base;
$this->M_yui_loader->comboBase = $this->yui3loader->comboBase;
$this->M_yui_loader->combine = $this->yui3loader->combine;
- $this->M_yui_loader->filter = ($this->yui3loader->filter == YUI_DEBUG) ? 'debug' : '';
+ $this->M_yui_loader->filter = (string)$this->yui3loader->filter;
$this->M_yui_loader->insertBefore = 'firstthemesheet';
$this->M_yui_loader->modules = array();
$this->M_yui_loader->groups = array(
* Initialise with the bits of JavaScript that every Moodle page should have.
*
* @param moodle_page $page
- * @param core_renderer $output
+ * @param core_renderer $renderer
*/
protected function init_requirements_data(moodle_page $page, core_renderer $renderer) {
global $CFG;
/**
* Returns true if the module has already been loaded.
*
- * @param string|array $modulename
+ * @param string|array $module
* @return bool True if the module has already been loaded
*/
protected function js_module_loaded($module) {
* (e.g. and array) that you pass to JavaScript with {@link data_for_js()}.
*
* @param string $identifier the desired string.
- * @param string $module the language file to look in.
+ * @param string $component the language file to look in.
* @param mixed $a any extra data to add into the string (optional).
*/
public function string_for_js($identifier, $component, $a = NULL) {
/**
* Get the inline JavaScript code that need to appear in a particular place.
- * @return bool $ondomready
+ * @param bool $ondomready
+ * @return string
*/
protected function get_javascript_code($ondomready) {
$where = $ondomready ? 'ondomready' : 'normal';
$code .= '<link rel="stylesheet" type="text/css" href="'.$this->yui3loader->base.'cssbase/base-min.css" />';
}
- if (debugging('', DEBUG_DEVELOPER)) {
- $code .= '<script type="text/javascript" src="'.$this->yui3loader->base.'yui/yui-debug.js"></script>';
- } else {
- $code .= '<script type="text/javascript" src="'.$this->yui3loader->base.'yui/yui-min.js"></script>';
+ $code .= '<script type="text/javascript" src="'.$this->yui3loader->base.'yui/yui-min.js"></script>';
+
+ if ($this->yui3loader->filter === YUI_RAW) {
+ $code = str_replace('-min.css', '.css', $code);
+ $code = str_replace('-min.js', '.js', $code);
+ } else if ($this->yui3loader->filter === YUI_DEBUG) {
+ $code = str_replace('-min.css', '.css', $code);
+ $code = str_replace('-min.js', '-debug.js', $code);
}
return $code;
/**
* Adds extra modules specified after printing of page header
+ * @return string
*/
protected function get_extra_modules_code() {
if (empty($this->extramodules)) {
* Normally, this method is called automatically by the code that prints the
* <head> tag. You should not normally need to call it in your own code.
*
+ * @param moodle_page $page
+ * @param core_renderer $renderer
* @return string the HTML code to to inside the <head> tag.
*/
public function get_head_code(moodle_page $page, core_renderer $renderer) {
}
// now the real test and redirect!
+ // NOTE: do NOT use this test for detection of https on current page because this code is not compatible with SSL proxies,
+ // instead use strpos($CFG->httpswwwroot, 'https:') === 0
if (strpos($FULLME, 'https:') !== 0) {
// this may lead to infinite redirect on misconfigured sites, in that case use $CFG->loginhttps=0; in /config.php
redirect($this->_url);
$result .= $this->HeaderLine('Date', self::RFCDate());
if($this->Sender == '') {
- $result .= $this->HeaderLine('Return-Path', trim($this->From));
+ $result .= $this->HeaderLine('Return-Path', trim($this->SecureHeader($this->From))); // Moodle modification
} else {
- $result .= $this->HeaderLine('Return-Path', trim($this->Sender));
+ $result .= $this->HeaderLine('Return-Path', trim($this->SecureHeader($this->Sender))); // Moodle modification
}
// To be created automatically by mail()
/**
* Enter description here...
*
- * @param string $questionids list of questionids
+ * @param array $questionids of question ids
* @param object $newcontext the context to create the saved category in.
* @param string $oldplace a textual description of the think being deleted,
* e.g. from get_context_name
* function also have to do other work, which is why you should not call this method
* directly from outside the questionbank.
*
- * @param string $questionids a comma-separated list of question ids.
+ * @param array $questionids of question ids.
* @param integer $newcategoryid the id of the category to move to.
*/
function question_move_questions_to_category($questionids, $newcategoryid) {
return $caps;
}
+
+/**
+ * Tracks all the contexts related to the one where we are currently editing
+ * questions, and provides helper methods to check permissions.
+ *
+ * @copyright 2007 Jamie Pratt me@jamiep.org
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
class question_edit_contexts {
public static $caps = array(
protected $allcontexts;
/**
- * @param current context
+ * Constructor
+ * @param context the current context.
*/
- public function __construct($thiscontext) {
- $pcontextids = get_parent_contexts($thiscontext);
- $contexts = array($thiscontext);
- foreach ($pcontextids as $pcontextid) {
- $contexts[] = get_context_instance_by_id($pcontextid);
- }
- $this->allcontexts = $contexts;
+ public function __construct(context $thiscontext) {
+ $this->allcontexts = array_values($thiscontext->get_parent_contexts(true));
}
+
/**
* @return array all parent contexts
*/
public function all() {
return $this->allcontexts;
}
+
/**
* @return object lowest context which must be either the module or course context
*/
public function lowest() {
return $this->allcontexts[0];
}
+
/**
* @param string $cap capability
* @return array parent contexts having capability, zero based index
}
return $contextswithcap;
}
+
/**
* @param array $caps capabilities
* @return array parent contexts having at least one of $caps, zero based index
}
return $contextswithacap;
}
+
/**
* @param string $tabname edit tab name
* @return array parent contexts having at least one of $caps, zero based index
public function having_one_edit_tab_cap($tabname) {
return $this->having_one_cap(self::$caps[$tabname]);
}
+
/**
* Has at least one parent context got the cap $cap?
*
}
}
+
/**
- * Rewrite question url, file_rewrite_pluginfile_urls always build url by
- * $file/$contextid/$component/$filearea/$itemid/$pathname_in_text, so we cannot add
- * extra questionid and attempted in url by it, so we create quiz_rewrite_question_urls
- * to build url here
+ * Helps call file_rewrite_pluginfile_urls with the right parameters.
*
* @param string $text text being processed
* @param string $file the php script used to serve files
*/
function question_rewrite_question_urls($text, $file, $contextid, $component,
$filearea, array $ids, $itemid, array $options=null) {
- global $CFG;
- $options = (array)$options;
- if (!isset($options['forcehttps'])) {
- $options['forcehttps'] = false;
+ $idsstr = '';
+ if (!empty($ids)) {
+ $idsstr .= implode('/', $ids);
}
-
- if (!$CFG->slasharguments) {
- $file = $file . '?file=';
+ if ($itemid !== null) {
+ $idsstr .= '/' . $itemid;
}
+ return file_rewrite_pluginfile_urls($text, $file, $contextid, $component,
+ $filearea, $idsstr, $options);
+}
- $baseurl = "$CFG->wwwroot/$file/$contextid/$component/$filearea/";
+/**
+ * Rewrite the PLUGINFILE urls in the questiontext, when viewing the question
+ * text outside and attempt (for example, in the question bank listing or in the
+ * quiz statistics report).
+ *
+ * @param string $questiontext the question text.
+ * @param int $contextid the context the text is being displayed in.
+ * @param string $component component
+ * @param array $ids other IDs will be used to check file permission
+ * @param array $options
+ * @return string $questiontext with URLs rewritten.
+ */
+function question_rewrite_questiontext_preview_urls($questiontext, $contextid,
+ $component, $questionid, $options=null) {
- if (!empty($ids)) {
- $baseurl .= (implode('/', $ids) . '/');
- }
+ return file_rewrite_pluginfile_urls($questiontext, 'pluginfile.php', $contextid,
+ 'question', 'questiontext_preview', "$component/$questionid", $options);
+}
- if ($itemid !== null) {
- $baseurl .= "$itemid/";
- }
+/**
+ * Send a file from the question text of a question.
+ * @param int $questionid the question id
+ * @param array $args the remaining file arguments (file path).
+ * @param bool $forcedownload whether the user must be forced to download the file.
+ */
+function question_send_questiontext_file($questionid, $args, $forcedownload) {
+ global $DB;
+
+ $question = $DB->get_record_sql('
+ SELECT q.id, qc.contextid
+ FROM {question} q
+ JOIN {question_categories} qc ON qc.id = q.category
+ WHERE q.id = :id', array('id' => $questionid), MUST_EXIST);
- if ($options['forcehttps']) {
- $baseurl = str_replace('http://', 'https://', $baseurl);
+ $fs = get_file_storage();
+ $fullpath = "/$question->contextid/question/questiontext/$question->id/" . implode('/', $args);
+ if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
+ send_file_not_found();
}
- return str_replace('@@PLUGINFILE@@/', $baseurl, $text);
+ send_stored_file($file, 0, 0, $forcedownload);
}
/**
function question_pluginfile($course, $context, $component, $filearea, $args, $forcedownload) {
global $DB, $CFG;
+ if ($filearea === 'questiontext_preview') {
+ $component = array_shift($args);
+ $questionid = array_shift($args);
+
+ component_callback($component, 'questiontext_preview_pluginfile', array(
+ $context, $questionid, $args, $forcedownload));
+
+ send_file_not_found();
+ }
+
list($context, $course, $cm) = get_context_info_array($context->id);
require_login($course, false, $cm);
}
}
+/**
+ * Serve questiontext files in the question text when they are displayed in this report.
+ * @param context $context the context
+ * @param int $questionid the question id
+ * @param array $args remaining file args
+ * @param bool $forcedownload
+ */
+function core_question_questiontext_preview_pluginfile($context, $questionid, $args, $forcedownload) {
+ global $DB;
+
+ // Verify that contextid matches the question.
+ $question = $DB->get_record_sql('
+ SELECT q.*, qc.contextid
+ FROM {question} q
+ JOIN {question_categories} qc ON qc.id = q.category
+ WHERE q.id = :id AND qc.contextid = :contextid',
+ array('id' => $questionid, 'contextid' => $context->id), MUST_EXIST);
+
+ // Check the capability.
+ list($context, $course, $cm) = get_context_info_array($context->id);
+ require_login($course, false, $cm);
+
+ question_require_capability_on($question, 'use');
+
+ question_send_questiontext_file($questionid, $args, $forcedownload);
+}
+
/**
* Create url for question export
*
/**
* Full script path including all params, slash arguments, scheme and host.
+ *
+ * Note: Do NOT use for getting of current page URL or detection of https,
+ * instead use $PAGE->url or strpos($CFG->httpswwwroot, 'https:') === 0
+ *
* @global string $FULLME
* @name $FULLME
*/
*/
function make_temp_directory($directory, $exceptiononerror = true) {
global $CFG;
- protect_directory($CFG->tempdir);
+ if ($CFG->tempdir !== "$CFG->dataroot/temp") {
+ check_dir_exists($CFG->tempdir, true, true);
+ protect_directory($CFG->tempdir);
+ } else {
+ protect_directory($CFG->dataroot);
+ }
return make_writable_directory("$CFG->tempdir/$directory", $exceptiononerror);
}
*/
function make_cache_directory($directory, $exceptiononerror = true) {
global $CFG;
- protect_directory($CFG->cachedir);
+ if ($CFG->cachedir !== "$CFG->dataroot/cache") {
+ check_dir_exists($CFG->cachedir, true, true);
+ protect_directory($CFG->cachedir);
+ } else {
+ protect_directory($CFG->dataroot);
+ }
return make_writable_directory("$CFG->cachedir/$directory", $exceptiononerror);
}
$tablenames = array('config', 'config_plugins', 'modules', 'course', 'course_modules', 'course_sections', 'course_categories', 'mnet_host', 'mnet_application',
'capabilities', 'context', 'context_temp', 'role', 'role_capabilities', 'role_allow_switch', 'license', 'my_pages', 'block', 'block_instances', 'block_positions',
'role_allow_assign', 'role_allow_override', 'role_assignments', 'role_context_levels' ,'enrol', 'user_enrolments', 'filter_active', 'filter_config', 'comments',
- 'user', 'groups_members', 'cache_flags', 'events_handlers', 'user_lastaccess', 'rating', 'files', 'role_names', 'user_preferences');
+ 'user', 'groups_members', 'cache_flags', 'events_handlers', 'user_lastaccess', 'rating', 'files', 'role_names', 'user_preferences', 'grading_areas');
$this->create_test_tables($tablenames, 'lib');
// Create all core default records and default settings
unset_user_preference('_test_user_preferences_pref');
$this->assertTrue(!isset($USER->preference['_test_user_preferences_pref']));
+ // Test 1333 char values (no need for unicode, there are already tests for that in DB tests)
+ $longvalue = str_repeat('a', 1333);
+ set_user_preference('_test_long_user_preference', $longvalue);
+ $this->assertEqual($longvalue, get_user_preferences('_test_long_user_preference'));
+ $this->assertEqual($longvalue,
+ $DB->get_field('user_preferences', 'value', array('userid' => $USER->id, 'name' => '_test_long_user_preference')));
+
+ // Test > 1333 char values, coding_exception expected
+ $longvalue = str_repeat('a', 1334);
+ try {
+ set_user_preference('_test_long_user_preference', $longvalue);
+ $this->assertFail('Exception expected - longer than 1333 chars not allowed as preference value');
+ } catch (Exception $e) {
+ $this->assertTrue($e instanceof coding_exception);
+ }
+
//test invalid params
try {
set_user_preference('_test_user_preferences_pref', array());
$this->assertIdentical('0', html_to_text('0'));
}
+ public function test_html_to_text_pre_parsing_problem() {
+ $strorig = 'Consider the following function:<br /><pre><span style="color: rgb(153, 51, 102);">void FillMeUp(char* in_string) {'.
+ '<br /> int i = 0;<br /> while (in_string[i] != \'\0\') {<br /> in_string[i] = \'X\';<br /> i++;<br /> }<br />'.
+ '}</span></pre>What would happen if a non-terminated string were input to this function?<br /><br />';
+
+ $strconv = 'Consider the following function:
+
+void FillMeUp(char* in_string) {
+ int i = 0;
+ while (in_string[i] != \'\0\') {
+ in_string[i] = \'X\';
+ i++;
+ }
+}
+What would happen if a non-terminated string were input to this function?
+
+';
+
+ $this->assertIdentical($strconv, html_to_text($strorig));
+ }
+
public function test_clean_text() {
$text = "lala <applet>xx</applet>";
$this->assertEqual($text, clean_text($text, FORMAT_PLAIN));
* @package Zend_Validate
* @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
- * @version $Id$
+ * @version $Id: Upca.php 20096 2010-01-06 02:05:09Z bkarwin $
*/
/**
* @var string
*/
protected $_checksum = '_gtin';
-}
+}
\ No newline at end of file
* lots of files removed
* small fix to error reporting in reflection (MDL-21460, ZF-8980)
* SOAP and XMLRPC servers overwrite the fault() functions
+* synced and renamed file to version in ZF 1.10.6 (MDL-30603, ZF-11080)
/**
- * Return information about all the current hosts
- * This is basically just a resultset.
+ * Returns information about MNet peers
*
+ * @param bool $withdeleted should the deleted peers be returned too
* @return array
*/
-function mnet_get_hosts() {
+function mnet_get_hosts($withdeleted = false) {
global $CFG, $DB;
- return $DB->get_records_sql(' SELECT
- h.id,
- h.wwwroot,
- h.ip_address,
- h.name,
- h.public_key,
- h.public_key_expires,
- h.transport,
- h.portno,
- h.last_connect_time,
- h.last_log_id,
- h.applicationid,
- a.name as app_name,
- a.display_name as app_display_name,
- a.xmlrpc_server_url
- FROM
- {mnet_host} h,
- {mnet_application} a
- WHERE
- h.id <> ? AND
- h.deleted = 0 AND
- h.applicationid=a.id',
- array($CFG->mnet_localhost_id));
+
+ $sql = "SELECT h.id, h.deleted, h.wwwroot, h.ip_address, h.name, h.public_key, h.public_key_expires,
+ h.transport, h.portno, h.last_connect_time, h.last_log_id, h.applicationid,
+ a.name as app_name, a.display_name as app_display_name, a.xmlrpc_server_url
+ FROM {mnet_host} h
+ JOIN {mnet_application} a ON h.applicationid = a.id
+ WHERE h.id <> ?";
+
+ if (!$withdeleted) {
+ $sql .= " AND h.deleted = 0";
+ }
+
+ $sql .= " ORDER BY h.deleted, h.name, h.id";
+
+ return $DB->get_records_sql($sql, array($CFG->mnet_localhost_id));
}
function view_feedback($submission=NULL) {
- global $USER, $CFG, $DB, $OUTPUT;
+ global $USER, $CFG, $DB, $OUTPUT, $PAGE;
require_once($CFG->libdir.'/gradelib.php');
+ require_once("$CFG->dirroot/grade/grading/lib.php");
if (!$submission) { /// Get submission for this assignment
- $submission = $this->get_submission($USER->id);
+ $userid = $USER->id;
+ $submission = $this->get_submission($userid);
+ } else {
+ $userid = $submission->userid;
}
if (empty($submission->timemarked)) { /// Nothing to show, so print nothing
return;
}
+ // Check the user can submit
+ $canviewfeedback = ($userid == $USER->id && has_capability('mod/assignment:submit', $this->context, $USER->id, false));
+ // If not then check if the user still has the view cap and has a previous submission
+ $canviewfeedback = $canviewfeedback || (!empty($submission) && $submission->userid == $USER->id && has_capability('mod/assignment:view', $this->context));
+ // Or if user can grade (is a teacher or admin)
+ $canviewfeedback = $canviewfeedback || has_capability('mod/assignment:grade', $this->context);
- $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, $USER->id);
+ if (!$canviewfeedback) {
+ // can not view or submit assignments -> no feedback
+ return;
+ }
+
+ $grading_info = grade_get_grades($this->course->id, 'mod', 'assignment', $this->assignment->id, $userid);
$item = $grading_info->items[0];
- $grade = $item->grades[$USER->id];
+ $grade = $item->grades[$userid];
if ($grade->hidden or $grade->grade === false) { // hidden or error
return;
}
if ($grade->grade === null and empty($grade->str_feedback)) { // No grade to show yet
- if ($this->count_responsefiles($USER->id)) { // but possibly response files are present
+ if ($this->count_responsefiles($userid)) { // but possibly response files are present
echo $OUTPUT->heading(get_string('responsefiles', 'assignment'), 3);
- $responsefiles = $this->print_responsefiles($USER->id, true);
+ $responsefiles = $this->print_responsefiles($userid, true);
echo $OUTPUT->box($responsefiles, 'generalbox boxaligncenter');
}
return;
echo '<tr>';
echo '<td class="left side"> </td>';
echo '<td class="content">';
- if ($this->assignment->grade) {
- echo '<div class="grade">';
- echo get_string("grade").': '.$grade->str_long_grade;
- echo '</div>';
- echo '<div class="clearer"></div>';
+ $gradestr = '<div class="grade">'. get_string("grade").': '.$grade->str_long_grade. '</div>';
+ if (!empty($submission) && $controller = get_grading_manager($this->context, 'mod_assignment', 'submission')->get_active_controller()) {
+ $controller->set_grade_range(make_grades_menu($this->assignment->grade));
+ echo $controller->render_grade($PAGE, $submission->id, $item, $gradestr, has_capability('mod/assignment:grade', $this->context));
+ } else {
+ echo $gradestr;
}
+ echo '<div class="clearer"></div>';
echo '<div class="comment">';
echo $grade->str_feedback;
echo '<tr>';
echo '<td class="left side"> </td>';
echo '<td class="content">';
- echo $this->print_responsefiles($USER->id, true);
+ echo $this->print_responsefiles($userid, true);
echo '</tr>';
echo '</table>';
// WARNING: the $fromform->message array has been overwritten, do not use it anymore!
$fromform->messagetrust = trusttext_trusted($modcontext);
+ $contextcheck = isset($fromform->groupinfo) && has_capability('mod/forum:movediscussions', $modcontext);
+
if ($fromform->edit) { // Updating a post
unset($fromform->groupid);
$fromform->id = $fromform->edit;
print_error('cannotupdatepost', 'forum');
}
+ // If the user has access to all groups and they are changing the group, then update the post.
+ if ($contextcheck) {
+ $DB->set_field('forum_discussions' ,'groupid' , $fromform->groupinfo, array('firstpost' => $fromform->id));
+ }
+
$updatepost = $fromform; //realpost
$updatepost->forum = $forum->id;
if (!forum_update_post($updatepost, $mform_post, $message)) {
if (!forum_user_can_post_discussion($forum, $fromform->groupid, -1, $cm, $modcontext)) {
print_error('cannotcreatediscussion', 'forum');
}
+ // If the user has access all groups capability let them choose the group.
+ if ($contextcheck) {
+ $fromform->groupid = $fromform->groupinfo;
+ }
if (empty($fromform->groupid)) {
$fromform->groupid = -1;
}
}
if (groups_get_activity_groupmode($cm, $course)) { // hack alert
- if (empty($post->groupid)) {
- $groupname = get_string('allparticipants');
+ $groupdata = groups_get_activity_allowed_groups($cm);
+ $groupcount = count($groupdata);
+ $modulecontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+ $contextcheck = has_capability('mod/forum:movediscussions', $modulecontext) && empty($post->parent) && $groupcount > 1;
+ if ($contextcheck) {
+ $groupinfo = array('0' => get_string('allparticipants'));
+ foreach ($groupdata as $grouptemp) {
+ $groupinfo[$grouptemp->id] = $grouptemp->name;
+ }
+ $mform->addElement('select','groupinfo', get_string('group'), $groupinfo);
+ $mform->setDefault('groupinfo', $post->groupid);
} else {
- $group = groups_get_group($post->groupid);
- $groupname = format_string($group->name);
+ if (empty($post->groupid)) {
+ $groupname = get_string('allparticipants');
+ } else {
+ $groupname = format_string($groupdata[$post->groupid]->name);
+ }
+ $mform->addElement('static', 'groupinfo', get_string('group'), $groupname);
}
- $mform->addElement('static', 'groupinfo', get_string('group'), $groupname);
}
-
//-------------------------------------------------------------------------------
// buttons
if (isset($post->edit)) { // hack alert
if (!in_array($data->displayformat, $formats)) {
$data->displayformat = 'dictionary';
}
+ if (!empty($data->mainglossary) and $data->mainglossary == 1 and
+ $DB->record_exists('glossary', array('mainglossary' => 1, 'course' => $this->get_courseid()))) {
+ // Only allow one main glossary in the course
+ $data->mainglossary = 0;
+ }
// insert the glossary record
$newitemid = $DB->insert_record('glossary', $data);
$options = array();
$options['current'] = get_string('currentglossary', 'glossary');
$options['newglossary'] = get_string('newglossary', 'glossary');
- $mform->addElement('select', 'dest', get_string('currentglossary', 'glossary'), $options);
+ $mform->addElement('select', 'dest', get_string('destination', 'glossary'), $options);
$mform->addHelpButton('dest', 'destination', 'glossary');
$mform->addElement('checkbox', 'catsincl', get_string('importcategories', 'glossary'));
$submit_string = get_string('submit');
$xmlbase = '';
}
if (!$href = $res->attributes->getNamedItem('href')) {
- continue;
+ // If href not found look for <file href="help.htm"/>
+ $fileresources = $res->getElementsByTagName('file');
+ foreach ($fileresources as $file) {
+ $href = $file->getAttribute('href');
+ }
+ if (empty($href)) {
+ continue;
+ }
+ } else {
+ $href = $href->nodeValue;
}
- $href = $href->nodeValue;
if (strpos($href, 'http://') !== 0) {
$href = $xmlbase.$href;
}
$answers = $this->get_answers();
$formattextdefoptions = new stdClass;
$formattextdefoptions->para = false; //I'll use it widely in this page
+ $formattextdefoptions->context = $answerpage->context;
+
foreach ($answers as $answer) {
if ($useranswer != NULL) {
$essayinfo = unserialize($useranswer->useranswer);
// dont think this should ever be reached....
$avescore = get_string("nooneansweredthisquestion", "lesson");
}
- $answerdata->answers[] = array(s($essayinfo->answer), $avescore);
+ $answerdata->answers[] = array(format_text($essayinfo->answer, FORMAT_MOODLE, $formattextdefoptions), $avescore);
$answerpage->answerdata = $answerdata;
}
return $answerpage;
$yeslink = html_writer::link(new moodle_url('/mod/lesson/view.php', array('id'=>$this->page->cm->id, 'pageid'=>$lastpageseenid, 'startlastseen'=>'yes')), get_string('yes'));
$output .= html_writer::tag('span', $yeslink, array('class'=>'lessonbutton standardbutton'));
+ $output .= ' ';
$nolink = html_writer::link(new moodle_url('/mod/lesson/view.php', array('id'=>$this->page->cm->id, 'pageid'=>$lesson->firstpageid, 'startlastseen'=>'no')), get_string('no'));
$output .= html_writer::tag('span', $nolink, array('class'=>'lessonbutton standardbutton'));
$action = optional_param('action', 'reportoverview', PARAM_ALPHA); // action to take
$nothingtodisplay = false;
-$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);;
+$cm = get_coursemodule_from_id('lesson', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', array('id' => $cm->course), '*', MUST_EXIST);
$lesson = new lesson($DB->get_record('lesson', array('id' => $cm->instance), '*', MUST_EXIST));
$answerpage->qtype = $qtypes[$page->qtype].$page->option_description_string();
$answerpage->grayout = $page->grayout;
+ $answerpage->context = $context;
if (empty($userid)) {
// there is no userid, so set these vars and display stats.
}
function lti_request_is_using_ssl() {
- global $FULLME;
- return (stripos($FULLME, 'https://') === 0);
+ global $CFG;
+ return (stripos($CFG->httpswwwroot, 'https://') === 0);
}
function lti_ensure_url_is_https($url) {
//move to the end of the selected page
$pagebreakpositions = array_keys($questions, 0);
$numpages = count($pagebreakpositions);
+
// Ensure the target page number is in range.
- $moveselectedonpage = max(1, min($moveselectedonpage, $pagebreakpositions));
+ for ($i = $moveselectedonpage; $i > $numpages; $i--) {
+ $questions[] = 0;
+ $pagebreakpositions[] = count($questions) - 1;
+ }
$moveselectedpos = $pagebreakpositions[$moveselectedonpage - 1];
+
+ // Do the move.
array_splice($questions, $moveselectedpos, 0, $selectedquestionids);
$quiz->questions = implode(',', $questions);
+
+ // Update the database.
$DB->set_field('quiz', 'questions', $quiz->questions, array('id' => $quiz->id));
$deletepreviews = true;
}
'editaction', 'previewaction');
}
+ protected function default_sort() {
+ return array('qtype' => 1, 'questionnametext' => 1);
+ }
+
/**
* Let the question bank display know whether the quiz has been attempted,
* hence whether some bits of UI, like the add this question to the quiz icon,
if (groups_get_activity_groupmode($cm)) {
$a->total = $numattempts;
if ($currentgroup) {
- $a->group = $DB->count_records_sql('SELECT count(1) FROM ' .
+ $a->group = $DB->count_records_sql('SELECT COUNT(DISTINCT qa.id) FROM ' .
'{quiz_attempts} qa JOIN ' .
'{groups_members} gm ON qa.userid = gm.userid ' .
'WHERE quiz = ? AND preview = 0 AND groupid = ?',
return get_string('attemptsnumthisgroup', 'quiz', $a);
} else if ($groups = groups_get_all_groups($cm->course, $USER->id, $cm->groupingid)) {
list($usql, $params) = $DB->get_in_or_equal(array_keys($groups));
- $a->group = $DB->count_records_sql('SELECT count(1) FROM ' .
+ $a->group = $DB->count_records_sql('SELECT COUNT(DISTINCT qa.id) FROM ' .
'{quiz_attempts} qa JOIN ' .
'{groups_members} gm ON qa.userid = gm.userid ' .
'WHERE quiz = ? AND preview = 0 AND ' .
/**
* Get information about which students to show in the report.
* @param object $cm the coures module.
+ * @param object $course the course settings.
* @return an array with four elements:
* 0 => integer the current group id (0 for none).
* 1 => array ids of all the students in this course.
* 3 => array ids of all the students to show in the report. Will be the
* same as either element 1 or 2.
*/
- protected function load_relevant_students($cm) {
- $currentgroup = groups_get_activity_group($cm, true);
+ protected function load_relevant_students($cm, $course = null) {
+ $currentgroup = $this->get_current_group($cm, $course, $this->context);
+
+ if ($currentgroup == self::NO_GROUPS_ALLOWED) {
+ return array($currentgroup, array(), array(), array());
+ }
if (!$students = get_users_by_capability($this->context,
array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
protected function validate_common_options(&$attemptsmode, &$pagesize, $course, $currentgroup) {
if ($currentgroup) {
//default for when a group is selected
- if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) {
+ if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) {
$attemptsmode = QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH;
}
} else if (!$currentgroup && $course->id == SITEID) {
}
}
- /**
- * Contruct all the parts of the main database query.
- * @param object $quiz the quiz settings.
- * @param string $qmsubselect SQL fragment from {@link quiz_report_qm_filter_select()}.
- * @param bool $qmfilter whether to show all, or only the final grade attempt.
- * @param int $attemptsmode which attempts to show.
- * One of the QUIZ_REPORT_ATTEMPTS_... constants.
- * @param array $reportstudents list if userids of users to include in the report.
- * @return array with 4 elements ($fields, $from, $where, $params) that can be used to
- * build the actual database query.
- */
- protected function base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $reportstudents) {
- global $DB;
-
- $fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid,';
-
- if ($qmsubselect) {
- $fields .= "\n(CASE WHEN $qmsubselect THEN 1 ELSE 0 END) AS gradedattempt,";
- }
-
- $extrafields = get_extra_user_fields_sql($this->context, 'u', '',
- array('id', 'idnumber', 'firstname', 'lastname', 'picture',
- 'imagealt', 'institution', 'department', 'email'));
- $fields .= '
- quiza.uniqueid AS usageid,
- quiza.id AS attempt,
- u.id AS userid,
- u.idnumber,
- u.firstname,
- u.lastname,
- u.picture,
- u.imagealt,
- u.institution,
- u.department,
- u.email' . $extrafields . ',
- quiza.sumgrades,
- quiza.timefinish,
- quiza.timestart,
- CASE WHEN quiza.timefinish = 0 THEN null
- WHEN quiza.timefinish > quiza.timestart THEN quiza.timefinish - quiza.timestart
- ELSE 0 END AS duration';
- // To explain that last bit, in MySQL, qa.timestart and qa.timefinish
- // are unsigned. Since MySQL 5.5.5, when they introduced strict mode,
- // subtracting a larger unsigned int from a smaller one gave an error.
- // Therefore, we avoid doing that. timefinish can be non-zero and less
- // than timestart when you have two load-balanced servers with very
- // badly synchronised clocks, and a student does a really quick attempt.';
-
- // This part is the same for all cases - join users and quiz_attempts tables
- $from = "\n{user} u";
- $from .= "\nLEFT JOIN {quiz_attempts} quiza ON
- quiza.userid = u.id AND quiza.quiz = :quizid";
- $params = array('quizid' => $quiz->id);
-
- if ($qmsubselect && $qmfilter) {
- $from .= " AND $qmsubselect";
- }
- switch ($attemptsmode) {
- case QUIZ_REPORT_ATTEMPTS_ALL:
- // Show all attempts, including students who are no longer in the course
- $where = 'quiza.id IS NOT NULL AND quiza.preview = 0';
- break;
- case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH:
- // Show only students with attempts
- list($usql, $uparams) = $DB->get_in_or_equal(
- $reportstudents, SQL_PARAMS_NAMED, 'u');
- $params += $uparams;
- $where = "u.id $usql AND quiza.preview = 0 AND quiza.id IS NOT NULL";
- break;
- case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
- // Show only students without attempts
- list($usql, $uparams) = $DB->get_in_or_equal(
- $reportstudents, SQL_PARAMS_NAMED, 'u');
- $params += $uparams;
- $where = "u.id $usql AND quiza.id IS NULL";
- break;
- case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS:
- // Show all students with or without attempts
- list($usql, $uparams) = $DB->get_in_or_equal(
- $reportstudents, SQL_PARAMS_NAMED, 'u');
- $params += $uparams;
- $where = "u.id $usql AND (quiza.preview = 0 OR quiza.preview IS NULL)";
- break;
- }
-
- return array($fields, $from, $where, $params);
- }
-
/**
* Add all the user-related columns to the $columns and $headers arrays.
* @param table_sql $table the table being constructed.
* @param object $quiz the quiz settings.
* @param array $columns the list of columns. Added to.
* @param array $headers the columns headings. Added to.
+ * @param bool $includefeedback whether to include the feedbacktext columns
*/
- protected function add_grade_columns($quiz, &$columns, &$headers) {
+ protected function add_grade_columns($quiz, &$columns, &$headers, $includefeedback = true) {
if ($this->should_show_grades($quiz)) {
$columns[] = 'sumgrades';
$headers[] = get_string('grade', 'quiz') . '/' .
quiz_format_grade($quiz, $quiz->grade);
}
- if (quiz_has_feedback($quiz)) {
+ if ($includefeedback && quiz_has_feedback($quiz)) {
$columns[] = 'feedbacktext';
$headers[] = get_string('feedback', 'quiz');
}
protected $quiz;
protected $context;
protected $qmsubselect;
+ protected $qmfilter;
+ protected $attemptsmode;
protected $groupstudents;
protected $students;
protected $questions;
protected $includecheckboxes;
- public function __construct($uniqueid, $quiz, $context, $qmsubselect, $groupstudents,
- $students, $questions, $includecheckboxes, $reporturl, $displayoptions) {
+ public function __construct($uniqueid, $quiz, $context, $qmsubselect, $qmfilter,
+ $attemptsmode, $groupstudents, $students, $questions, $includecheckboxes,
+ $reporturl, $displayoptions) {
parent::__construct($uniqueid);
$this->quiz = $quiz;
$this->context = $context;
$this->qmsubselect = $qmsubselect;
+ $this->qmfilter = $qmfilter;
+ $this->attemptsmode = $attemptsmode;
$this->groupstudents = $groupstudents;
$this->students = $students;
$this->questions = $questions;
return '';
}
+ /**
+ * Contruct all the parts of the main database query.
+ * @param array $reportstudents list if userids of users to include in the report.
+ * @return array with 4 elements ($fields, $from, $where, $params) that can be used to
+ * build the actual database query.
+ */
+ public function base_sql($reportstudents) {
+ global $DB;
+
+ $fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid,';
+
+ if ($this->qmsubselect) {
+ $fields .= "\n(CASE WHEN $this->qmsubselect THEN 1 ELSE 0 END) AS gradedattempt,";
+ }
+
+ $extrafields = get_extra_user_fields_sql($this->context, 'u', '',
+ array('id', 'idnumber', 'firstname', 'lastname', 'picture',
+ 'imagealt', 'institution', 'department', 'email'));
+ $fields .= '
+ quiza.uniqueid AS usageid,
+ quiza.id AS attempt,
+ u.id AS userid,
+ u.idnumber,
+ u.firstname,
+ u.lastname,
+ u.picture,
+ u.imagealt,
+ u.institution,
+ u.department,
+ u.email' . $extrafields . ',
+ quiza.sumgrades,
+ quiza.timefinish,
+ quiza.timestart,
+ CASE WHEN quiza.timefinish = 0 THEN null
+ WHEN quiza.timefinish > quiza.timestart THEN quiza.timefinish - quiza.timestart
+ ELSE 0 END AS duration';
+ // To explain that last bit, in MySQL, qa.timestart and qa.timefinish
+ // are unsigned. Since MySQL 5.5.5, when they introduced strict mode,
+ // subtracting a larger unsigned int from a smaller one gave an error.
+ // Therefore, we avoid doing that. timefinish can be non-zero and less
+ // than timestart when you have two load-balanced servers with very
+ // badly synchronised clocks, and a student does a really quick attempt.';
+
+ // This part is the same for all cases - join users and quiz_attempts tables
+ $from = "\n{user} u";
+ $from .= "\nLEFT JOIN {quiz_attempts} quiza ON
+ quiza.userid = u.id AND quiza.quiz = :quizid";
+ $params = array('quizid' => $this->quiz->id);
+
+ if ($this->qmsubselect && $this->qmfilter) {
+ $from .= " AND $this->qmsubselect";
+ }
+ switch ($this->attemptsmode) {
+ case QUIZ_REPORT_ATTEMPTS_ALL:
+ // Show all attempts, including students who are no longer in the course
+ $where = 'quiza.id IS NOT NULL AND quiza.preview = 0';
+ break;
+ case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH:
+ // Show only students with attempts
+ list($usql, $uparams) = $DB->get_in_or_equal(
+ $reportstudents, SQL_PARAMS_NAMED, 'u');
+ $params += $uparams;
+ $where = "u.id $usql AND quiza.preview = 0 AND quiza.id IS NOT NULL";
+ break;
+ case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
+ // Show only students without attempts
+ list($usql, $uparams) = $DB->get_in_or_equal(
+ $reportstudents, SQL_PARAMS_NAMED, 'u');
+ $params += $uparams;
+ $where = "u.id $usql AND quiza.id IS NULL";
+ break;
+ case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS:
+ // Show all students with or without attempts
+ list($usql, $uparams) = $DB->get_in_or_equal(
+ $reportstudents, SQL_PARAMS_NAMED, 'u');
+ $params += $uparams;
+ $where = "u.id $usql AND (quiza.preview = 0 OR quiza.preview IS NULL)";
+ break;
+ }
+
+ return array($fields, $from, $where, $params);
+ }
+
/**
* Add the information about the latest state of the question with slot
* $slot to the query.
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class quiz_default_report {
+ const NO_GROUPS_ALLOWED = -2;
+
/**
* Override this function to displays the report.
* @param $cm the course-module for this quiz.
*/
public abstract function display($cm, $course, $quiz);
+ /**
+ * Initialise some parts of $PAGE and start output.
+ *
+ * @param object $cm the course_module information.
+ * @param object $coures the course settings.
+ * @param object $quiz the quiz settings.
+ * @param string $reportmode the report name.
+ */
public function print_header_and_tabs($cm, $course, $quiz, $reportmode = 'overview') {
global $PAGE, $OUTPUT;
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
}
+
+ /**
+ * Get the current group for the user user looking at the report.
+ *
+ * @param object $cm the course_module information.
+ * @param object $coures the course settings.
+ * @param context $context the quiz context.
+ * @return int the current group id, if applicable. 0 for all users,
+ * NO_GROUPS_ALLOWED if the user cannot see any group.
+ */
+ public function get_current_group($cm, $course, $context) {
+ $groupmode = groups_get_activity_groupmode($cm, $course);
+ $currentgroup = groups_get_activity_group($cm, true);
+
+ if ($groupmode == SEPARATEGROUPS && !$currentgroup && !has_capability('moodle/site:accessallgroups', $context)) {
+ $currentgroup = self::NO_GROUPS_ALLOWED;
+ }
+
+ return $currentgroup;
+ }
}
}
// Get the group, and the list of significant users.
- $this->currentgroup = groups_get_activity_group($this->cm, true);
- $this->users = get_users_by_capability($this->context,
- array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '',
- $this->currentgroup, '', false);
+ $this->currentgroup = $this->get_current_group($cm, $course, $this->context);
+ if ($this->currentgroup == self::NO_GROUPS_ALLOWED) {
+ $this->users = array();
+ } else {
+ $this->users = get_users_by_capability($this->context,
+ array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '',
+ $this->currentgroup, '', false);
+ }
// Start output.
$this->print_header_and_tabs($cm, $course, $quiz, 'grading');
protected $regradedqs = array();
- public function __construct($quiz, $context, $qmsubselect, $groupstudents,
- $students, $detailedmarks, $questions, $includecheckboxes, $reporturl, $displayoptions) {
+ public function __construct($quiz, $context, $qmsubselect, $qmfilter,
+ $attemptsmode, $groupstudents, $students, $detailedmarks,
+ $questions, $includecheckboxes, $reporturl, $displayoptions) {
parent::__construct('mod-quiz-report-overview-report', $quiz , $context,
- $qmsubselect, $groupstudents, $students, $questions, $includecheckboxes,
- $reporturl, $displayoptions);
+ $qmsubselect, $qmfilter, $attemptsmode, $groupstudents, $students,
+ $questions, $includecheckboxes, $reporturl, $displayoptions);
$this->detailedmarks = $detailedmarks;
}
public function build_table() {
global $DB;
- if ($this->rawdata) {
- $this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
- parent::build_table();
-
- //end of adding data from attempts data to table / download
- //now add averages at bottom of table :
- $params = array($this->quiz->id);
- $averagesql = '
- SELECT AVG(qg.grade) AS grade, COUNT(qg.grade) AS numaveraged
- FROM {quiz_grades} qg
- WHERE quiz = ?';
-
- $this->add_separator();
- if ($this->is_downloading()) {
- $namekey = 'lastname';
- } else {
- $namekey = 'fullname';
- }
- if ($this->groupstudents) {
- list($usql, $uparams) = $DB->get_in_or_equal($this->groupstudents);
- $record = $DB->get_record_sql($averagesql . ' AND qg.userid ' . $usql,
- array_merge($params, $uparams));
- $groupaveragerow = array(
- $namekey => get_string('groupavg', 'grades'),
- 'sumgrades' => $this->format_average($record),
- 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
- $record->grade, $this->quiz->id, $this->context)));
- if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
- $avggradebyq = $this->load_average_question_grades($this->groupstudents);
- $groupaveragerow += $this->format_average_grade_for_questions($avggradebyq);
- }
- $this->add_data_keyed($groupaveragerow);
- }
+ if (!$this->rawdata) {
+ return;
+ }
- if ($this->students) {
- list($usql, $uparams) = $DB->get_in_or_equal($this->students);
- $record = $DB->get_record_sql($averagesql . ' AND qg.userid ' . $usql,
- array_merge($params, $uparams));
- $overallaveragerow = array(
- $namekey => get_string('overallaverage', 'grades'),
- 'sumgrades' => $this->format_average($record),
- 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
- $record->grade, $this->quiz->id, $this->context)));
- if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
- $avggradebyq = $this->load_average_question_grades($this->students);
- $overallaveragerow += $this->format_average_grade_for_questions($avggradebyq);
- }
- $this->add_data_keyed($overallaveragerow);
- }
+ $this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
+ parent::build_table();
+
+ // End of adding the data from attempts. Now add averages at bottom.
+ $this->add_separator();
+
+ if ($this->groupstudents) {
+ $this->add_average_row(get_string('groupavg', 'grades'), $this->groupstudents);
+ }
+
+ if ($this->students) {
+ $this->add_average_row(get_string('overallaverage', 'grades'), $this->students);
}
}
+ /**
+ * Add an average grade over the attempts of a set of users.
+ * @param string $label the title ot use for this row.
+ * @param array $users the users to average over.
+ */
+ protected function add_average_row($label, $users) {
+ global $DB;
+
+ list($fields, $from, $where, $params) = $this->base_sql($users);
+ $record = $DB->get_record_sql("
+ SELECT AVG(quiza.sumgrades) AS grade, COUNT(quiza.sumgrades) AS numaveraged
+ FROM $from
+ WHERE $where", $params);
+ $record->grade = quiz_rescale_grade($record->grade, $this->quiz);
+
+ if ($this->is_downloading()) {
+ $namekey = 'lastname';
+ } else {
+ $namekey = 'fullname';
+ }
+ $averagerow = array(
+ $namekey => $label,
+ 'sumgrades' => $this->format_average($record),
+ 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
+ $record->grade, $this->quiz->id, $this->context))
+ );
+
+ if ($this->detailedmarks) {
+ $dm = new question_engine_data_mapper();
+ $qubaids = new qubaid_join($from, 'quiza.uniqueid', $where, $params);
+ $avggradebyq = $dm->load_average_marks($qubaids, array_keys($this->questions));
+
+ $averagerow += $this->format_average_grade_for_questions($avggradebyq);
+ }
+
+ $this->add_data_keyed($averagerow);
+ }
+
+ /**
+ * Helper userd by {@link add_average_row()}.
+ * @param array $gradeaverages the raw grades.
+ * @return array the (partial) row of data.
+ */
protected function format_average_grade_for_questions($gradeaverages) {
$row = array();
+
if (!$gradeaverages) {
$gradeaverages = array();
}
+
foreach ($this->questions as $question) {
if (isset($gradeaverages[$question->slot]) && $question->maxmark > 0) {
$record = $gradeaverages[$question->slot];
$record->grade = quiz_rescale_grade(
$record->averagefraction * $question->maxmark, $this->quiz, false);
+
} else {
$record = new stdClass();
$record->grade = null;
$record->numaveraged = null;
}
+
$row['qsgrade' . $question->slot] = $this->format_average($record, true);
}
+
return $row;
}
}
}
- /**
- * Load the average grade for each question, averaged over particular users.
- * @param array $userids the user ids to average over.
- */
- protected function load_average_question_grades($userids) {
- global $DB;
-
- $qmfilter = '';
- if ($this->quiz->attempts != 1) {
- $qmfilter = '(' . quiz_report_qm_filter_select($this->quiz, 'quiza') . ') AND ';
- }
-
- list($usql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'u');
- $params['quizid'] = $this->quiz->id;
- $qubaids = new qubaid_join(
- '{quiz_attempts} quiza',
- 'quiza.uniqueid',
- "quiza.userid $usql AND quiza.quiz = :quizid",
- $params);
-
- $dm = new question_engine_data_mapper();
- return $dm->load_average_marks($qubaids, array_keys($this->questions));
- }
-
/**
* Get all the questions in all the attempts being displayed that need regrading.
* @return array A two dimensional array $questionusageid => $slot => $regradeinfo.
$download = optional_param('download', '', PARAM_ALPHA);
list($currentgroup, $students, $groupstudents, $allowed) =
- $this->load_relevant_students($cm);
+ $this->load_relevant_students($cm, $course);
$pageoptions = array();
$pageoptions['id'] = $cm->id;
$questions = quiz_report_get_significant_questions($quiz);
$table = new quiz_report_overview_table($quiz, $this->context, $qmsubselect,
- $groupstudents, $students, $detailedmarks, $questions, $includecheckboxes,
- $reporturl, $displayoptions);
+ $qmfilter, $attemptsmode, $groupstudents, $students, $detailedmarks,
+ $questions, $includecheckboxes, $reporturl, $displayoptions);
$filename = quiz_report_download_filename(get_string('overviewfilename', 'quiz_overview'),
$courseshortname, $quiz->name);
$table->is_downloading($download, $filename,
"END) AS gradedattempt, ";
}
- list($fields, $from, $where, $params) =
- $this->base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $allowed);
+ list($fields, $from, $where, $params) = $table->base_sql($allowed);
$table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params);
$headers[] = get_string('regrade', 'quiz_overview');
}
- $this->add_grade_columns($quiz, $columns, $headers);
+ $this->add_grade_columns($quiz, $columns, $headers, false);
$this->set_up_table_columns(
$table, $columns, $headers, $reporturl, $displayoptions, false);
$download = optional_param('download', '', PARAM_ALPHA);
list($currentgroup, $students, $groupstudents, $allowed) =
- $this->load_relevant_students($cm);
+ $this->load_relevant_students($cm, $course);
$pageoptions = array();
$pageoptions['id'] = $cm->id;
array('context' => $displaycoursecontext));
$table = new quiz_report_responses_table($quiz, $this->context, $qmsubselect,
- $groupstudents, $students, $questions, $includecheckboxes, $reporturl, $displayoptions);
+ $qmfilter, $attemptsmode, $groupstudents, $students, $questions,
+ $includecheckboxes, $reporturl, $displayoptions);
$filename = quiz_report_download_filename(get_string('responsesfilename', 'quiz_responses'),
$courseshortname, $quiz->name);
$table->is_downloading($download, $filename,
}
}
- list($fields, $from, $where, $params) =
- $this->base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $allowed);
+ list($fields, $from, $where, $params) = $table->base_sql($allowed);
$table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params);
*/
class quiz_report_responses_table extends quiz_attempt_report_table {
- public function __construct($quiz, $context, $qmsubselect, $groupstudents,
- $students, $questions, $includecheckboxes, $reporturl, $displayoptions) {
+ public function __construct($quiz, $context, $qmsubselect, $qmfilter,
+ $attemptsmode, $groupstudents, $students,
+ $questions, $includecheckboxes, $reporturl, $displayoptions) {
parent::__construct('mod-quiz-report-responses-report', $quiz, $context,
- $qmsubselect, $groupstudents, $students, $questions, $includecheckboxes,
- $reporturl, $displayoptions);
+ $qmsubselect, $qmfilter, $attemptsmode, $groupstudents, $students,
+ $questions, $includecheckboxes, $reporturl, $displayoptions);
}
public function build_table() {
--- /dev/null
+<?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/>.
+
+/**
+ * Standard plugin entry points of the quiz statistics report.
+ *
+ * @package quiz
+ * @subpackage statistics
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+/**
+ * Serve questiontext files in the question text when they are displayed in this report.
+ * @param context $context the context
+ * @param int $questionid the question id
+ * @param array $args remaining file args
+ * @param bool $forcedownload
+ */
+function quiz_statistics_questiontext_preview_pluginfile($context, $questionid, $args, $forcedownload) {
+ global $CFG;
+ require_once($CFG->dirroot . '/mod/quiz/locallib.php');
+
+ list($context, $course, $cm) = get_context_info_array($context->id);
+ require_login($course, false, $cm);
+
+ // Assume only trusted people can see this report. There is no real way to
+ // validate questionid, becuase of the complexity of random quetsions.
+ require_capability('quiz/statistics:view', $context);
+
+ question_send_questiontext_file($questionid, $args, $forcedownload);
+}
public function display($quiz, $cm, $course) {
global $CFG, $DB, $OUTPUT, $PAGE;
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+ $this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
// Work out the display options.
$download = optional_param('download', '', PARAM_ALPHA);
}
// Find out current groups mode
- $groupmode = groups_get_activity_groupmode($cm);
- $currentgroup = groups_get_activity_group($cm, true);
+ $currentgroup = $this->get_current_group($cm, $course, $this->context);
$nostudentsingroup = false; // True if a group is selected and there is no one in it.
if (empty($currentgroup)) {
$currentgroup = 0;
$groupstudents = array();
+ } else if ($currentgroup == self::NO_GROUPS_ALLOWED) {
+ $groupstudents = array();
+ $nostudentsingroup = true;
+
} else {
// All users who can attempt quizzes and who are in the currently selected group
- $groupstudents = get_users_by_capability($context,
+ $groupstudents = get_users_by_capability($this->context,
array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
'', '', '', '', $currentgroup, '', false);
if (!$groupstudents) {
if (!$this->table->is_downloading()) {
$this->print_header_and_tabs($cm, $course, $quiz, 'statistics');
- if ($groupmode) {
+ if (groups_get_activity_groupmode($cm)) {
groups_print_activity_menu($cm, $reporturl->out());
if ($currentgroup && !$groupstudents) {
$OUTPUT->notification(get_string('nostudentsingroup', 'quiz_statistics'));
}
if (!quiz_questions_in_quiz($quiz->questions)) {
- echo quiz_no_questions_message($quiz, $cm, $context);
+ echo quiz_no_questions_message($quiz, $cm, $this->context);
} else if (!$this->table->is_downloading() && $s == 0) {
echo $OUTPUT->notification(get_string('noattempts', 'quiz'));
}
echo $OUTPUT->heading(get_string('questionstatistics', 'quiz_statistics'));
echo html_writer::table($questionstatstable);
}
+ public function format_text($text, $format, $qa, $component, $filearea, $itemid,
+ $clean = false) {
+ $formatoptions = new stdClass();
+ $formatoptions->noclean = !$clean;
+ $formatoptions->para = false;
+ $text = $qa->rewrite_pluginfile_urls($text, $component, $filearea, $itemid);
+ return format_text($text, $format, $formatoptions);
+ }
+
+ /** @return the result of applying {@link format_text()} to the question text. */
+ public function format_questiontext($qa) {
+ return $this->format_text($this->questiontext, $this->questiontextformat,
+ $qa, 'question', 'questiontext', $this->id);
+ }
/**
* @param object $question question data.
* @return string HTML of question text, ready for display.
*/
- protected function render_question_text($question){
+ protected function render_question_text($question) {
global $OUTPUT;
- return $OUTPUT->box(format_text($question->questiontext, $question->questiontextformat,
- array('overflowdiv' => true)),
+
+ $text = question_rewrite_questiontext_preview_urls($question->questiontext,
+ $this->context->id, 'quiz_statistics', $question->id);
+
+ return $OUTPUT->box(format_text($text, $question->questiontextformat,
+ array('noclean' => true, 'para' => false, 'overflowdiv' => true)),
'questiontext boxaligncenter generalbox boxwidthnormal mdl-align');
}
$firstattempts = new stdClass();
$firstattempts->countrecs = 0;
$firstattempts->total = 0;
- $firstattempts->average = '-';
+ $firstattempts->average = null;
}
$allattempts = new stdClass();
$userdata->student_id = addslashes_js($USER->username);
$userdata->student_name = addslashes_js($USER->lastname .', '. $USER->firstname);
$userdata->mode = 'normal';
-if (isset($mode)) {
+if (!empty($mode)) {
$userdata->mode = $mode;
}
if ($userdata->mode == 'normal') {
'cmi.learner_id':{'defaultvalue':'<?php echo $userdata->student_id ?>', 'mod':'r'},
'cmi.learner_name':{'defaultvalue':'<?php echo $userdata->student_name ?>', 'mod':'r'},
'cmi.learner_preference._children':{'defaultvalue':student_preference_children, 'mod':'r'},
- 'cmi.learner_preference.audio_level':{'defaultvalue':'1', 'format':CMIDecimal, 'range':audio_range, 'mod':'rw'},
- 'cmi.learner_preference.language':{'defaultvalue':'', 'format':CMILang, 'mod':'rw'},
- 'cmi.learner_preference.delivery_speed':{'defaultvalue':'1', 'format':CMIDecimal, 'range':speed_range, 'mod':'rw'},
- 'cmi.learner_preference.audio_captioning':{'defaultvalue':'0', 'format':CMISInteger, 'range':text_range, 'mod':'rw'},
+ 'cmi.learner_preference.audio_level':{'defaultvalue':<?php echo !empty($userdata->{'cmi.learner_preference.audio_level'})?'\''.$userdata->{'cmi.learner_preference.audio_level'}.'\'':'\'1\'' ?>, 'format':CMIDecimal, 'range':audio_range, 'mod':'rw'},
+ 'cmi.learner_preference.language':{'defaultvalue':<?php echo !empty($userdata->{'cmi.learner_preference.language'})?'\''.$userdata->{'cmi.learner_preference.language'}.'\'':'\'\'' ?>, 'format':CMILang, 'mod':'rw'},
+ 'cmi.learner_preference.delivery_speed':{'defaultvalue':<?php echo !empty($userdata->{'cmi.learner_preference.delivery_speed'})?'\''.$userdata->{'cmi.learner_preference.delivery_speed'}.'\'':'\'1\'' ?>, 'format':CMIDecimal, 'range':speed_range, 'mod':'rw'},
+ 'cmi.learner_preference.audio_captioning':{'defaultvalue':<?php echo !empty($userdata->{'cmi.learner_preference.audio_captioning'})?'\''.$userdata->{'cmi.learner_preference.audio_captioning'}.'\'':'\'0\'' ?>, 'format':CMISInteger, 'range':text_range, 'mod':'rw'},
'cmi.location':{'defaultvalue':<?php echo !empty($userdata->{'cmi.location'})?'\''.$userdata->{'cmi.location'}.'\'':'null' ?>, 'format':CMIString1000, 'mod':'rw'},
'cmi.max_time_allowed':{'defaultvalue':<?php echo !empty($userdata->attemptAbsoluteDurationLimit)?'\''.$userdata->attemptAbsoluteDurationLimit.'\'':'null' ?>, 'mod':'r'},
'cmi.mode':{'defaultvalue':'<?php echo $userdata->mode ?>', 'mod':'r'},
'cmi.objectives.n.completion_status':{'defaultvalue':'unknown', 'pattern':CMIIndex, 'format':CMICStatus, 'mod':'rw'},
'cmi.objectives.n.progress_measure':{'defaultvalue':null, 'format':CMIDecimal, 'range':progress_range, 'mod':'rw'},
'cmi.objectives.n.description':{'pattern':CMIIndex, 'format':CMILangString250, 'mod':'rw'},
- 'cmi.progress_measure':{'defaultvalue':<?php echo !empty($userdata->{'cmi.progess_measure'})?'\''.$userdata->{'cmi.progress_measure'}.'\'':'null' ?>, 'format':CMIDecimal, 'range':progress_range, 'mod':'rw'},
+ 'cmi.progress_measure':{'defaultvalue':<?php echo !empty($userdata->{'cmi.progress_measure'})?'\''.$userdata->{'cmi.progress_measure'}.'\'':'null' ?>, 'format':CMIDecimal, 'range':progress_range, 'mod':'rw'},
'cmi.scaled_passing_score':{'defaultvalue':<?php echo !empty($userdata->{'cmi.scaled_passing_score'})?'\''.$userdata->{'cmi.scaled_passing_score'}.'\'':'null' ?>, 'format':CMIDecimal, 'range':scaled_range, 'mod':'r'},
'cmi.score._children':{'defaultvalue':score_children, 'mod':'r'},
'cmi.score.scaled':{'defaultvalue':<?php echo !empty($userdata->{'cmi.score.scaled'})?'\''.$userdata->{'cmi.score.scaled'}.'\'':'null' ?>, 'format':CMIDecimal, 'range':scaled_range, 'mod':'rw'},
case 'ADLCP:COMPLETIONTHRESHOLD':
$parent = array_pop($parents);
array_push($parents, $parent);
- if (!isset($block['tagData'])) {
- $block['tagData'] = '';
+ if (!isset($block['attrs']['MINPROGRESSMEASURE'])) {
+ $block['attrs']['MINPROGRESSMEASURE'] = '1.0';
}
- $scoes->elements[$manifest][$parent->organization][$parent->identifier]->threshold = $block['tagData'];
+ $scoes->elements[$manifest][$parent->organization][$parent->identifier]->threshold = $block['attrs']['MINPROGRESSMEASURE'];
break;
case 'ADLNAV:PRESENTATION':
$parent = array_pop($parents);
'editingteacher' => CAP_ALLOW,
'manager' => CAP_ALLOW
)
+ ),
+ 'mod/scorm:deleteownresponses' => array(
+
+ 'captype' => 'write',
+ 'contextlevel' => CONTEXT_MODULE,
+ 'archetypes' => array()
)
);
$string['defaultothersettings'] = 'Other default settings';
$string['deleteattemptcheck'] = 'Are you absolutely sure you want to completely delete these attempts?';
$string['deleteallattempts'] = 'Delete all SCORM attempts';
+$string['deleteuserattemptcheck'] = 'Are you absolutely sure you want to completely delete all your attempts?';
$string['details'] = 'Track details';
$string['directories'] = 'Show the directory links';
$string['disabled'] = 'Disabled';
$string['scormloggingon'] = 'API logging is on';
$string['scormopen'] = 'Open';
$string['scormresponsedeleted'] = 'Deleted user attempts';
+$string['scorm:deleteownresponses'] = 'Delete own attempts';
$string['scorm:savetrack'] = 'Save tracks';
$string['scorm:skipview'] = 'Skip overview';
$string['scormtype'] = 'Type';
* @param object $scorm a moodle scrom object - mdl_scorm
* @return string - Attempt status string
*/
-function scorm_get_attempt_status($user, $scorm) {
- global $DB;
+function scorm_get_attempt_status($user, $scorm, $cm='') {
+ global $DB, $PAGE, $OUTPUT;
$attempts = scorm_get_attempt_count($user->id, $scorm, true);
if (empty($attempts)) {
if ($attemptcount >= $scorm->maxattempt and $scorm->maxattempt > 0) {
$result .= '<p><font color="#cc0000">'.get_string('exceededmaxattempts', 'scorm').'</font></p>';
}
+ if (!empty($cm)) {
+ $context = context_module::instance($cm->id);
+ if (has_capability('mod/scorm:deleteownresponses', $context) &&
+ $DB->record_exists('scorm_scoes_track', array('userid' => $user->id, 'scormid' => $scorm->id))) {
+ //check to see if any data is stored for this user:
+ $deleteurl = new moodle_url($PAGE->url, array('action'=>'delete', 'sesskey' => sesskey()));
+ $result .= $OUTPUT->single_button($deleteurl, get_string('deleteallattempts', 'scorm'));
+ }
+ }
+
+
return $result;
}
defined('MOODLE_INTERNAL') || die();
-$module->version = 2011112900; // The current module version (Date: YYYYMMDDXX)
+$module->version = 2011112901; // The current module version (Date: YYYYMMDDXX)
$module->requires = 2011112900; // Requires this Moodle version
$module->component = 'mod_scorm'; // Full name of the plugin (used for diagnostics)
$module->cron = 300;
$id = optional_param('id', '', PARAM_INT); // Course Module ID, or
$a = optional_param('a', '', PARAM_INT); // scorm ID
$organization = optional_param('organization', '', PARAM_INT); // organization ID
+$action = optional_param('action', '', PARAM_ALPHA);
if (!empty($id)) {
if (! $cm = get_coursemodule_from_id('scorm', $id)) {
$PAGE->set_heading($course->fullname);
echo $OUTPUT->header();
+if (!empty($action) && confirm_sesskey() && has_capability('mod/scorm:deleteownresponses', $contextmodule)) {
+ if ($action == 'delete') {
+ $confirmurl = new moodle_url($PAGE->url, array('action'=>'deleteconfirm'));
+ echo $OUTPUT->confirm(get_string('deleteuserattemptcheck', 'scorm'), $confirmurl, $PAGE->url);
+ echo $OUTPUT->footer();
+ exit;
+ } else if ($action == 'deleteconfirm') {
+ //delete this users attempts.
+ $DB->delete_records('scorm_scoes_track', array('userid' => $USER->id, 'scormid' => $scorm->id));
+ scorm_update_grades($scorm, $USER->id, true);
+ echo $OUTPUT->notification(get_string('scormresponsedeleted', 'scorm'), 'notifysuccess');
+ }
+}
+
$currenttab = 'info';
require($CFG->dirroot . '/mod/scorm/tabs.php');
echo $OUTPUT->heading(format_string($scorm->name));
$attemptstatus = '';
if ($scorm->displayattemptstatus == 1) {
- $attemptstatus = scorm_get_attempt_status($USER, $scorm);
+ $attemptstatus = scorm_get_attempt_status($USER, $scorm, $cm);
}
echo $OUTPUT->box(format_module_intro('scorm', $scorm, $cm->id).$attemptstatus, 'generalbox boxaligncenter boxwidthwide', 'intro');
if (has_capability('moodle/course:viewparticipants', $coursecontext) || has_capability('moodle/site:viewparticipants', $systemcontext)) {
$link = new moodle_url('/user/index.php',array('id'=>$course->id));
}
- $PAGE->navbar->add(get_string('participants'), $link);
- $PAGE->navbar->add($strnotes);
}
$PAGE->set_pagelayout('course');
*/
$string['gradingdetails'] = 'Marks for this submission: {$a->raw}/{$a->max}.';
-$string['gradingdetailsadjustment'] = 'With previous penalties this gives <strong>{$a->cur}/{$a->max}</strong>.';
+$string['gradingdetailsadjustment'] = 'Accounting for previous tries, this gives <strong>{$a->cur}/{$a->max}</strong>.';
$string['gradingdetailspenalty'] = 'This submission attracted a penalty of {$a}.';
+$string['gradingdetailspenaltytotal'] = 'Total penalties so far: {$a}.';
$string['notcomplete'] = 'Not complete';
$string['pluginname'] = 'Adaptive mode';
*/
protected function penalty_info(question_attempt $qa, $mark,
question_display_options $options) {
- if (!$qa->get_question()->penalty) {
+
+ $currentpenalty = $qa->get_question()->penalty * $qa->get_max_mark();
+ $totalpenalty = $currentpenalty * $qa->get_last_behaviour_var('_try', 0);
+
+ if ($currentpenalty == 0) {
return '';
}
$output = '';
// Print information about any new penalty, only relevant if the answer can be improved.
if ($qa->get_behaviour()->is_state_improvable($qa->get_state())) {
$output .= ' ' . get_string('gradingdetailspenalty', 'qbehaviour_adaptive',
- format_float($qa->get_question()->penalty, $options->markdp));
+ format_float($currentpenalty, $options->markdp));
+
+ // Print information about total penalties so far, if larger than current penalty.
+ if ($totalpenalty > $currentpenalty) {
+ $output .= ' ' . get_string('gradingdetailspenaltytotal', 'qbehaviour_adaptive',
+ format_float($totalpenalty, $options->markdp));
+ }
}
return $output;
return new NoPatternExpectation($penaltypattern);
}
+ protected function get_contains_total_penalty_expectation($penalty) {
+ $penaltyinfo = get_string('gradingdetailspenaltytotal', 'qbehaviour_adaptive',
+ format_float($penalty, $this->displayoptions->markdp));
+ return new PatternExpectation('/'.preg_quote($penaltyinfo).'/');
+ }
+
+ protected function get_does_not_contain_total_penalty_expectation() {
+ $penaltyinfo = get_string('gradingdetailspenaltytotal', 'qbehaviour_adaptive', 'XXXXX');
+ $penaltypattern = '/'.str_replace('XXXXX', '\\w*', preg_quote($penaltyinfo)).'/';
+ return new NoPatternExpectation($penaltypattern);
+ }
+
public function test_adaptive_multichoice() {
// Create a multiple choice, single response question.
$this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
$this->get_contains_mc_radio_expectation(($wrongindex + 2) % 3, true, false),
$this->get_contains_incorrect_expectation(),
- $this->get_contains_penalty_info_expectation(0.33));
+ $this->get_contains_penalty_info_expectation(1.00),
+ $this->get_does_not_contain_total_penalty_expectation());
$this->assertPattern('/B|C/',
$this->quba->get_response_summary($this->slot));
$this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, true, false),
$this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, true, false),
$this->get_contains_correct_expectation(),
- $this->get_does_not_contain_penalty_info_expectation());
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation());
$this->assertEqual('A',
$this->quba->get_response_summary($this->slot));
$this->get_contains_mark_summary(2),
$this->get_contains_submit_button_expectation(true),
$this->get_contains_correct_expectation(),
- $this->get_does_not_contain_penalty_info_expectation());
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation());
// Save the same correct answer again. Should not do anything.
$numsteps = $this->get_step_count();
$this->get_contains_submit_button_expectation(true),
$this->get_contains_partcorrect_expectation(),
$this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Submit an incorrect answer.
$this->get_contains_submit_button_expectation(true),
$this->get_contains_incorrect_expectation(),
$this->get_contains_penalty_info_expectation(0.33),
+ $this->get_contains_total_penalty_expectation(0.67),
$this->get_does_not_contain_validation_error_expectation());
// Submit a correct answer.
$this->get_contains_submit_button_expectation(true),
$this->get_contains_correct_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Finish the attempt.
// Create a short answer question
$sa = test_question_maker::make_a_shortanswer_question();
- $this->start_attempt_at_question($sa, 'adaptive');
+ $this->start_attempt_at_question($sa, 'adaptive', 6);
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->get_contains_mark_summary(0),
$this->get_contains_submit_button_expectation(true),
$this->get_contains_incorrect_expectation(),
- $this->get_contains_penalty_info_expectation(0.33),
+ $this->get_contains_penalty_info_expectation(2.00),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Submit the same wrong answer again. Nothing should change.
$this->get_contains_mark_summary(0),
$this->get_contains_submit_button_expectation(true),
$this->get_contains_incorrect_expectation(),
- $this->get_contains_penalty_info_expectation(0.33),
+ $this->get_contains_penalty_info_expectation(2.00),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Submit a correct answer.
// Verify.
$this->check_current_state(question_state::$complete);
- $this->check_current_mark(0.66666667);
+ $this->check_current_mark(4.00);
$this->check_current_output(
- $this->get_contains_mark_summary(0.67),
+ $this->get_contains_mark_summary(4.00),
$this->get_contains_submit_button_expectation(true),
$this->get_contains_correct_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Submit another incorrect answer.
// Verify.
$this->check_current_state(question_state::$complete);
- $this->check_current_mark(0.66666667);
+ $this->check_current_mark(4.00);
$this->check_current_output(
- $this->get_contains_mark_summary(0.67),
+ $this->get_contains_mark_summary(4.00),
$this->get_contains_submit_button_expectation(true),
$this->get_contains_incorrect_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Finish the attempt.
// Verify.
$this->check_current_state(question_state::$gradedwrong);
- $this->check_current_mark(0.66666667);
+ $this->check_current_mark(4.00);
$this->check_current_output(
- $this->get_contains_mark_summary(0.67),
+ $this->get_contains_mark_summary(4.00),
$this->get_contains_submit_button_expectation(false),
$this->get_contains_incorrect_expectation(),
$this->get_does_not_contain_validation_error_expectation());
$this->get_contains_submit_button_expectation(true),
$this->get_contains_incorrect_expectation(),
$this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Submit a correct answer.
$this->get_contains_submit_button_expectation(true),
$this->get_contains_correct_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Submit an empty answer.
$this->get_contains_mark_summary(0.67),
$this->get_contains_submit_button_expectation(true),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_contains_validation_error_expectation());
// Submit another wrong answer.
$this->get_contains_submit_button_expectation(true),
$this->get_contains_incorrect_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Finish the attempt.
$this->get_does_not_contain_validation_error_expectation());
}
+ public function test_adaptive_shortanswer_zero_penalty() {
+
+ // Create a short answer question
+ $sa = test_question_maker::make_a_shortanswer_question();
+ // Disable penalties for this question
+ $sa->penalty = 0;
+ $this->start_attempt_at_question($sa, 'adaptive');
+
+ // Check the initial state.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation());
+
+ // Submit a wrong answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
+
+ // Verify.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(0);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit a correct answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(1.0);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(1.0),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Finish the attempt.
+ $this->quba->finish_all_questions();
+
+ // Verify.
+ $this->check_current_state(question_state::$gradedright);
+ $this->check_current_mark(1.0);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(1.0),
+ $this->get_contains_submit_button_expectation(false),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+ }
+
public function test_adaptive_shortanswer_try_to_submit_blank() {
// Create a short answer question with correct answer true.
$this->get_contains_submit_button_expectation(true),
$this->get_does_not_contain_correctness_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_contains_validation_error_expectation());
$this->assertNull($this->quba->get_response_summary($this->slot));
$this->get_contains_submit_button_expectation(true),
$this->get_contains_partcorrect_expectation(),
$this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Now submit blank again.
$this->get_contains_submit_button_expectation(true),
$this->get_contains_partcorrect_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_contains_validation_error_expectation());
}
$this->get_contains_submit_button_expectation(true),
$this->get_contains_correct_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Submit an incorrect answer.
$this->get_contains_submit_button_expectation(true),
$this->get_contains_incorrect_expectation(),
$this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_total_penalty_expectation(),
$this->get_does_not_contain_validation_error_expectation());
// Finish the attempt.
global $DB;
$questionids = $DB->get_records_select_menu('question',
'category = ? AND (parent = 0 OR parent = id)', array($oldcat), '', 'id,1');
- question_move_questions_to_category($questionids, $newcat);
+ question_move_questions_to_category(array_keys($questionids), $newcat);
}
/**
}
protected function display_content($question, $rowclasses) {
- $text = format_text($question->questiontext, $question->questiontextformat,
- $this->formatoptions, $this->qbank->get_courseid());
+ $text = question_rewrite_questiontext_preview_urls($question->questiontext,
+ $question->contextid, 'question', $question->id);
+ $text = format_text($text, $question->questiontextformat,
+ $this->formatoptions);
if ($text == '') {
$text = ' ';
}
echo $text;
}
+ public function get_extra_joins() {
+ return array('qc' => 'JOIN {question_categories} qc ON qc.id = q.category');
+ }
+
public function get_required_fields() {
- return array('q.questiontext', 'q.questiontextformat');
+ return array('q.id', 'q.questiontext', 'q.questiontextformat', 'qc.contextid');
}
}
}
/**
- * Deal with a sort name of the forum columnname, or colname_subsort by
+ * Deal with a sort name of the form columnname, or colname_subsort by
* breaking it up, validating the bits that are presend, and returning them.
* If there is no subsort, then $subsort is returned as ''.
* @return array array($colname, $subsort).
$sorts = array();
foreach ($this->sort as $sort => $order) {
list($colname, $subsort) = $this->parse_subsort($sort);
- $sorts[] = $this->knowncolumntypes[$colname]->sort_expression($order < 0, $subsort);
+ $sorts[] = $this->requiredcolumns[$colname]->sort_expression($order < 0, $subsort);
}
/// Build the where clause.
- $tests = array('parent = 0');
+ $tests = array('q.parent = 0');
if (!$showhidden) {
- $tests[] = 'hidden = 0';
+ $tests[] = 'q.hidden = 0';
}
if ($recurse) {
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
abstract class question_bank {
+ const MAX_SUMMARY_LENGTH = 65000;
+
/** @var array question type name => question_type subclass. */
private static $questiontypes = array();
$record->minfraction = $qa->get_min_fraction();
$record->flagged = $qa->is_flagged();
$record->questionsummary = $qa->get_question_summary();
+ if (textlib::strlen($record->questionsummary) > question_bank::MAX_SUMMARY_LENGTH) {
+ // It seems some people write very long quesions! MDL-30760
+ $record->questionsummary = textlib::substr($record->questionsummary,
+ 0, question_bank::MAX_SUMMARY_LENGTH - 3) . '...';
+ }
$record->rightanswer = $qa->get_right_answer_summary();
$record->responsesummary = $qa->get_response_summary();
$record->timemodified = time();
* @return string SQL code for the subquery.
*/
public function sum_usage_marks_subquery($qubaid) {
- return "SELECT SUM(qa.maxmark * qas.fraction)
+ // To explain the COALESCE in the following SQL: SUM(lots of NULLs) gives
+ // NULL, while SUM(one 0.0 and lots of NULLS) gives 0.0. We don't want that.
+ // We always want to return a number, so the COALESCE is there to turn the
+ // NULL total into a 0.
+ return "SELECT COALESCE(SUM(qa.maxmark * qas.fraction), 0)
FROM {question_attempts} qa
JOIN {question_attempt_steps} qas ON qas.id = (
SELECT MAX(summarks_qas.id)
$qa = $qas[$questionid];
$qa->questionusageid = $attempt->uniqueid;
$qa->slot = $i;
+ if (textlib::strlen($qa->questionsummary) > question_bank::MAX_SUMMARY_LENGTH) {
+ // It seems some people write very long quesions! MDL-30760
+ $qa->questionsummary = textlib::substr($qa->questionsummary,
+ 0, question_bank::MAX_SUMMARY_LENGTH - 3) . '...';
+ }
$this->insert_record('question_attempts', $qa);
$layout[$questionkeys[$questionid]] = $qa->slot;
&& preg_match('~'.NUMERICAL_ALTERNATIVE_REGEX.'~',
$altregs[ANSWER_ALTERNATIVE_REGEX_ANSWER], $numregs)) {
$wrapped->answer[] = $numregs[NUMERICAL_CORRECT_ANSWER];
- if ($numregs[NUMERICAL_ABS_ERROR_MARGIN]) {
+ if (array_key_exists(NUMERICAL_ABS_ERROR_MARGIN, $numregs)) {
$wrapped->tolerance["$answerindex"] =
$numregs[NUMERICAL_ABS_ERROR_MARGIN];
} else {
}
// convert and write the multichoice
+ if (!isset($data['multichoice'])) {
+ // This should never happen, but it can do if the 1.9 site contained
+ // corrupt data/
+ $data['multichoice'] = array(array(
+ 'single' => 1,
+ 'shuffleanswers' => 1,
+ 'correctfeedback' => '',
+ 'correctfeedbackformat' => FORMAT_HTML,
+ 'partiallycorrectfeedback' => '',
+ 'partiallycorrectfeedbackformat' => FORMAT_HTML,
+ 'incorrectfeedback' => '',
+ 'incorrectfeedbackformat' => FORMAT_HTML,
+ 'answernumbering' => 'abc',
+ ));
+ }
$this->write_multichoice($data['multichoice'], $data['oldquestiontextformat']);
}
// Adjust some columns
$data->question = $newquestionid;
// Map sequence of question_answer ids
- $answersarr = explode(',', $data->answers);
+ if ($data->answers) {
+ $answersarr = explode(',', $data->answers);
+ } else {
+ $answersarr = array();
+ }
foreach ($answersarr as $key => $answer) {
$answersarr[$key] = $this->get_mappingid('question_answer', $answer);
}