//$this->set_mapping('grade_setting', $oldid, $newitemid);
}
- //put all activity grade items in the correct grade category and mark all for recalculation
+ /**
+ * put all activity grade items in the correct grade category and mark all for recalculation
+ */
protected function after_execute() {
global $DB;
);
$rs = $DB->get_recordset('backup_ids_temp', $conditions);
+ // We need this for calculation magic later on.
+ $mappings = array();
+
if (!empty($rs)) {
foreach($rs as $grade_item_backup) {
+
+ // Store the oldid with the new id.
+ $mappings[$grade_item_backup->itemid] = $grade_item_backup->newitemid;
+
$updateobj = new stdclass();
$updateobj->id = $grade_item_backup->newitemid;
}
$rs->close();
+ // We need to update the calculations for calculated grade items that may reference old
+ // grade item ids using ##gi\d+##.
+ list($sql, $params) = $DB->get_in_or_equal(array_values($mappings), SQL_PARAMS_NAMED);
+ $sql = "SELECT gi.id, gi.calculation
+ FROM {grade_items} gi
+ WHERE gi.id {$sql} AND
+ calculation IS NOT NULL";
+ $rs = $DB->get_recordset_sql($sql, $params);
+ foreach ($rs as $gradeitem) {
+ // Collect all of the used grade item id references
+ if (preg_match_all('/##gi(\d+)##/', $gradeitem->calculation, $matches) < 1) {
+ // This calculation doesn't reference any other grade items... EASY!
+ continue;
+ }
+ // For this next bit we are going to do the replacement of id's in two steps:
+ // 1. We will replace all old id references with a special mapping reference.
+ // 2. We will replace all mapping references with id's
+ // Why do we do this?
+ // Because there potentially there will be an overlap of ids within the query and we
+ // we substitute the wrong id.. safest way around this is the two step system
+ $calculationmap = array();
+ $mapcount = 0;
+ foreach ($matches[1] as $match) {
+ // Check that the old id is known to us, if not it was broken to begin with and will
+ // continue to be broken.
+ if (!array_key_exists($match, $mappings)) {
+ continue;
+ }
+ // Our special mapping key
+ $mapping = '##MAPPING'.$mapcount.'##';
+ // The old id that exists within the calculation now
+ $oldid = '##gi'.$match.'##';
+ // The new id that we want to replace the old one with.
+ $newid = '##gi'.$mappings[$match].'##';
+ // Replace in the special mapping key
+ $gradeitem->calculation = str_replace($oldid, $mapping, $gradeitem->calculation);
+ // And record the mapping
+ $calculationmap[$mapping] = $newid;
+ $mapcount++;
+ }
+ // Iterate all special mappings for this calculation and replace in the new id's
+ foreach ($calculationmap as $mapping => $newid) {
+ $gradeitem->calculation = str_replace($mapping, $newid, $gradeitem->calculation);
+ }
+ // Update the calculation now that its being remapped
+ $DB->update_record('grade_items', $gradeitem);
+ }
+ $rs->close();
+
//need to correct the grade category path and parent
$conditions = array(
'courseid' => $this->get_courseid()
}
protected function process_grade_item($data) {
+ global $DB;
$data = (object)($data);
$oldid = $data->id; // We'll need these later
$oldparentid = $data->categoryid;
+ $courseid = $this->get_courseid();
// make sure top course category exists, all grade items will be associated
// to it. Later, if restoring the whole gradebook, categories will be introduced
- $coursecat = grade_category::fetch_course_category($this->get_courseid());
+ $coursecat = grade_category::fetch_course_category($courseid);
$coursecatid = $coursecat->id; // Get the categoryid to be used
+ $idnumber = null;
+ if (!empty($data->idnumber)) {
+ // Don't get any idnumber from course module. Keep them as they are in grade_item->idnumber
+ // Reason: it's not clear what happens with outcomes->idnumber or activities with multiple items (workshop)
+ // so the best is to keep the ones already in the gradebook
+ // Potential problem: duplicates if same items are restored more than once. :-(
+ // This needs to be fixed in some way (outcomes & activities with multiple items)
+ // $data->idnumber = get_coursemodule_from_instance($data->itemmodule, $data->iteminstance)->idnumber;
+ // In any case, verify always for uniqueness
+ $sql = "SELECT cm.id
+ FROM {course_modules} cm
+ WHERE cm.course = :courseid AND
+ cm.idnumber = :idnumber AND
+ cm.id <> :cmid";
+ $params = array(
+ 'courseid' => $courseid,
+ 'idnumber' => $data->idnumber,
+ 'cmid' => $this->task->get_moduleid()
+ );
+ if (!$DB->record_exists_sql($sql, $params) && !$DB->record_exists('grade_items', array('courseid' => $courseid, 'idnumber' => $data->idnumber))) {
+ $idnumber = $data->idnumber;
+ }
+ }
+
unset($data->id);
$data->categoryid = $coursecatid;
$data->courseid = $this->get_courseid();
$data->iteminstance = $this->task->get_activityid();
- // Don't get any idnumber from course module. Keep them as they are in grade_item->idnumber
- // Reason: it's not clear what happens with outcomes->idnumber or activities with multiple items (workshop)
- // so the best is to keep the ones already in the gradebook
- // Potential problem: duplicates if same items are restored more than once. :-(
- // This needs to be fixed in some way (outcomes & activities with multiple items)
- // $data->idnumber = get_coursemodule_from_instance($data->itemmodule, $data->iteminstance)->idnumber;
- // In any case, verify always for uniqueness
- $data->idnumber = grade_verify_idnumber($data->idnumber, $this->get_courseid()) ? $data->idnumber : null;
+ $data->idnumber = $idnumber;
$data->scaleid = $this->get_mappingid('scale', $data->scaleid);
$data->outcomeid = $this->get_mappingid('outcome', $data->outcomeid);
$data->timecreated = $this->apply_date_offset($data->timecreated);
$usage->preferredbehaviour = $quiz->preferredbehaviour;
$usage->id = $DB->insert_record('question_usages', $usage);
- $DB->set_field('quiz_attempts', 'uniqueid', $usage->id,
- array('id' => $this->get_mappingid('quiz_attempt', $data->id)));
+ $this->inform_new_usage_id($usage->id);
$data->uniqueid = $usage->id;
$upgrader->save_usage($quiz->preferredbehaviour, $data, $qas, $quiz->questions);
$qstates = array();
foreach ($data->states['state'] as $state) {
if ($state['question'] == $questionid) {
- $qstates[$state['seq_number']] = (object) $state;
+ // It would be natural to use $state['seq_number'] as the array-key
+ // here, but it seems that buggy behaviour in 2.0 and early can
+ // mean that that is not unique, so we use id, which is guaranteed
+ // to be unique.
+ $qstates[$state['id']] = (object) $state;
}
}
ksort($qstates);
+ $qstates = array_values($qstates);
return array($qsession, $qstates);
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function blog_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function blog_page_type_list($pagetype, $parentcontext, $currentcontext) {
return array(
'*'=>get_string('page-x', 'pagetype'),
'blog-*'=>get_string('page-blog-x', 'blog'),
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function course_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function course_page_type_list($pagetype, $parentcontext, $currentcontext) {
// if above course context ,display all course fomats
list($currentcontext, $course, $cm) = get_context_info_array($currentcontext->id);
if ($course->id == SITEID) {
'mod-*'=>get_string('page-mod-x', 'pagetype')
);
}
-}
+}
\ No newline at end of file
$string['completion:view'] = 'View course completion report';
$string['completiondate']='Completion date';
+$string['pluginpagetype'] = 'Completion course report';
$string['pluginname']='Course completion';
}
}
}
+
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function completion_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype'),
+ 'course-report-completion-*' => get_string('pluginpagetype', 'coursereport_completion')
+ );
+ return $array;
+}
--- /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/>.
+
+/**
+ * This file contains functions used by course reports
+ *
+ * @since 2.1
+ * @package course-report
+ * @copyright 2011 Andrew Davis
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function coursereport_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype')
+ );
+ return $array;
+}
\ No newline at end of file
$string['log:view'] = 'View course logs';
$string['log:viewlive'] = 'View live logs';
$string['log:viewtoday'] = 'View today\'s logs';
+$string['pluginpagetype'] = 'Log course report';
$string['pluginname'] = 'Logs';
$navigation->add(get_string('pluginname', 'coursereport_log'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
}
}
+
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function log_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype'),
+ 'course-report-log-*' => get_string('pluginpagetype', 'coursereport_log')
+ );
+ return $array;
+}
\ No newline at end of file
$reportrow->cells[] = $numviewscell;
if ($CFG->useblogassociations) {
+ require_once($CFG->dirroot.'/blog/lib.php');
$blogcell = new html_table_cell();
$blogcell->attributes['class'] = 'blog';
if ($blogcount = blog_get_associated_count($course->id, $cm->id)) {
*/
$string['outline:view'] = 'View course activity report';
+$string['pluginpagetype'] = 'Course activity report';
$string['pluginname'] = 'Course activity';
$url = new moodle_url('/course/report/outline/index.php', array('id'=>$course->id));
$navigation->add(get_string( 'activityreport' ), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
}
+}
+
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function outline_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype'),
+ 'course-report-outline-*' => get_string('pluginpagetype', 'coursereport_outline')
+ );
+ return $array;
}
\ No newline at end of file
*/
$string['participation:view'] = 'View course participation report';
+$string['pluginpagetype'] = 'Participation course report';
$string['pluginname'] = 'Course participation';
$url = new moodle_url('/course/report/participation/index.php', array('id'=>$course->id));
$navigation->add(get_string('participationreport'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
}
+}
+
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function participation_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype'),
+ 'course-report-participation-*' => get_string('pluginpagetype', 'coursereport_participation')
+ );
+ return $array;
}
\ No newline at end of file
*/
$string['pluginname'] = 'Activity completion';
+$string['pluginpagetype'] = 'Progress course report';
$string['progress:view'] = 'View activity completion reports';
$navigation->add(get_string('pluginname','coursereport_progress'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
}
}
+
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function progress_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype'),
+ 'course-report-progress-*' => get_string('pluginpagetype', 'coursereport_progress')
+ );
+ return $array;
+}
\ No newline at end of file
*/
$string['pluginname'] = 'Course statistics';
+$string['pluginpagetype'] = 'Statistics course report';
$string['stats:view'] = 'View course statistics report';
}
}
}
+
+/**
+ * Return a list of page types
+ * @param string $pagetype current page type
+ * @param stdClass $parentcontext Block's parent context
+ * @param stdClass $currentcontext Current context of block
+ */
+function stats_page_type_list($pagetype, $parentcontext, $currentcontext) {
+ $array = array(
+ '*' => get_string('page-x', 'pagetype'),
+ 'course-report-*' => get_string('page-course-report-x', 'pagetype'),
+ 'course-report-stats-*' => get_string('pluginpagetype', 'coursereport_stats')
+ );
+ return $array;
+}
\ No newline at end of file
$string['page-course-view-x'] = 'Any type of course main page';
$string['page-course-x'] = 'Any course page';
+$string['page-course-report-x'] = 'Any course report';
$string['page-mod-x'] = 'Any activity module page';
$string['page-mod-x-view'] = 'Any main activity module page';
$string['page-my-index'] = 'My home page';
// If the block wants to be system-wide, then explicitly set that
if ($data->bui_contexts == BUI_CONTEXTS_ENTIRE_SITE) { // Only possible on a frontpage or system page
$bi->parentcontextid = $systemcontext->id;
- $bi->showinsubcontexts = 1;
+ $bi->showinsubcontexts = BUI_CONTEXTS_CURRENT_SUBS; //show in current and sub contexts
+ $bi->pagetypepattern = '*';
} else { // The block doesn't want to be system-wide, so let's ensure that
if ($parentcontext->id == $systemcontext->id) { // We need to move it to the front page
$frontpagecontext = get_context_instance(CONTEXT_COURSE, SITEID);
$bi->parentcontextid = $frontpagecontext->id;
- $bi->pagetypepattern = '*'; // Just in case
+ $bi->pagetypepattern = 'site-index';
}
}
}
$bits = explode('-', $pagetype);
- $component = clean_param(reset($bits), PARAM_ALPHANUMEXT);
- $function = 'default_pagetypelist';
-
$core = get_core_subsystems();
$plugins = get_plugin_types();
- // First check to see if the initial component is a core component
- // if its not check to see if it is a plugin component.
- if (array_key_exists($component, $core) && !empty($core[$component])) {
- $libfile = $CFG->dirroot.'/'.$core[$component].'/lib.php';
- if (file_exists($libfile)) {
- require_once($libfile);
- if (function_exists($component.'_pagetypelist')) {
- $function = $component.'_pagetypelist';
+ //progressively strip pieces off the page type looking for a match
+ $componentarray = null;
+ for ($i = count($bits); $i > 0; $i--) {
+ $possiblecomponentarray = array_slice($bits, 0, $i);
+ $possiblecomponent = implode('', $possiblecomponentarray);
+
+ // Check to see if the component is a core component
+ if (array_key_exists($possiblecomponent, $core) && !empty($core[$possiblecomponent])) {
+ $libfile = $CFG->dirroot.'/'.$core[$possiblecomponent].'/lib.php';
+ if (file_exists($libfile)) {
+ require_once($libfile);
+ $function = $possiblecomponent.'_page_type_list';
+ if (function_exists($function)) {
+ if ($patterns = $function($pagetype, $parentcontext, $currentcontext)) {
+ break;
+ }
+ }
}
}
- } else if (array_key_exists($component, $plugins) && !empty($plugins[$component])) {
- $function = 'plugin_pagetypelist';
- if (function_exists($component.'_pagetypelist')) {
- $function = $component.'_pagetypelist';
+
+ //check the plugin directory and look for a callback
+ if (array_key_exists($possiblecomponent, $plugins) && !empty($plugins[$possiblecomponent])) {
+
+ //We've found a plugin type. Look for a plugin name by getting the next section of page type
+ if (count($bits) > $i) {
+ $pluginname = $bits[$i];
+ $directory = get_plugin_directory($possiblecomponent, $pluginname);
+ if (!empty($directory)){
+ $libfile = $directory.'/lib.php';
+ if (file_exists($libfile)) {
+ require_once($libfile);
+ $function = $pluginname.'_page_type_list';
+ if (function_exists($function)) {
+ if ($patterns = $function($pagetype, $parentcontext, $currentcontext)) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ //we'll only get to here if we still don't have any patterns
+ //the plugin type may have a callback
+ $directory = get_plugin_directory($possiblecomponent, null);
+ if (!empty($directory)){
+ $libfile = $directory.'/lib.php';
+ if (file_exists($libfile)) {
+ require_once($libfile);
+ $function = $possiblecomponent.'_page_type_list';
+ if (function_exists($function)) {
+ if ($patterns = $function($pagetype, $parentcontext, $currentcontext)) {
+ break;
+ }
+ }
+ }
+ }
}
}
- // Call the most appropriate function we could determine
- $patterns = $function($pagetype, $parentcontext, $currentcontext);
+
if (empty($patterns)) {
- // If there are no patterns default to just the current pattern.
- $patterns = array($pagetype => $pagetype);
+ $patterns = default_page_type_list($pagetype, $parentcontext, $currentcontext);
}
+
return $patterns;
}
* @param stdClass $currentcontext
* @return array
*/
-function default_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
+function default_page_type_list($pagetype, $parentcontext = null, $currentcontext = null) {
// Generate page type patterns based on current page type if
// callbacks haven't been defined
$patterns = array($pagetype => $pagetype);
return $patterns;
}
-/**
- * Generates a page type list for plugins
- *
- * @param string $pagetype
- * @param stdClass $parentcontext
- * @param stdClass $currentcontext
- * @return array
- */
-function plugin_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
- global $CFG;
-
- // for modules
- $bits = explode('-', $pagetype);
- $plugintype = $bits[0];
- $pluginname = $bits[1];
- $directory = get_plugin_directory($plugintype, $pluginname);
- if (empty($directory)) {
- return array();
- }
- $libfile = $directory.'/lib.php';
- require_once($libfile);
- $function = $pluginname.'_pagetypelist';
- if (!function_exists($function)) {
- return array();
- }
- $patterns = $function($pagetype, $parentcontext, $currentcontext);
- if ($parentcontext->contextlevel == CONTEXT_COURSE) {
- // including course page type
- require_once("$CFG->dirroot/course/lib.php");
- $patterns = array_merge(course_pagetypelist($pagetype, $parentcontext, $currentcontext), $patterns);
- }
- return $patterns;
-}
-
/**
* Generates the page type list for the my moodle page
*
* @param stdClass $currentcontext
* @return array
*/
-function my_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
+function my_page_type_list($pagetype, $parentcontext = null, $currentcontext = null) {
return array('my-index' => 'my-index');
}
* @param stdClass $currentcontext
* @return array
*/
-function mod_pagetypelist($pagetype, $parentcontext = null, $currentcontext = null) {
- $patterns = plugin_pagetypelist($pagetype, $parentcontext, $currentcontext);
+function mod_page_type_list($pagetype, $parentcontext = null, $currentcontext = null) {
+ $patterns = plugin_page_type_list($pagetype, $parentcontext, $currentcontext);
if (empty($patterns)) {
// if modules don't have callbacks
// generate two default page type patterns for modules only
'capabilities'=> 'moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update',
),
+ 'moodle_user_get_users_by_courseid' => array(
+ 'classname' => 'moodle_user_external',
+ 'methodname' => 'get_users_by_courseid',
+ 'classpath' => 'user/externallib.php',
+ 'description' => 'Get enrolled users by course id.',
+ 'type' => 'read',
+ 'capabilities'=> 'moodle/user:viewdetails, moodle/user:viewhiddendetails, moodle/course:useremail, moodle/user:update, moodle/site:accessallgroups',
+ ),
+
'moodle_user_get_course_participants_by_id' => array(
'classname' => 'moodle_user_external',
'methodname' => 'get_course_participants_by_id',
// === message related functions ===
- 'moodle_message_send_messages' => array(
+ 'moodle_message_send_instantmessages' => array(
'classname' => 'moodle_message_external',
- 'methodname' => 'send_messages',
+ 'methodname' => 'send_instantmessages',
'classpath' => 'message/externallib.php',
- 'description' => 'Send messages',
+ 'description' => 'Send instant messages',
'type' => 'write',
'capabilities'=> 'moodle/site:sendmessage',
),
'moodle_webservice_get_siteinfo',
'moodle_notes_create_notes',
'moodle_user_get_course_participants_by_id',
- 'moodle_message_send_messages'),
+ 'moodle_user_get_users_by_courseid',
+ 'moodle_message_send_instantmessages'),
'enabled' => 0,
'restrictedusers' => 0,
'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE
)
");
+ // It seems that it is possible, in old versions of Moodle, for a
+ // quiz_attempt to be deleted while the question_attempt remains.
+ // In that situation we still get NULLs left in the table, which
+ // causes the upgrade to break at the next step. To avoid breakage,
+ // without risking dataloss, we just replace all NULLs with 0 here.
+ $DB->set_field_select('question_usages', 'contextid', 0, 'contextid IS NULL');
+
// Then make it NOT NULL.
$field = new xmldb_field('contextid');
$field->set_attributes(XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED,
// Find out if user has configured this output
$userisconfigured = $processor->object->is_user_configured($eventdata->userto);
- // DEBUG: noify if we are forcing unconfigured output
+ // DEBUG: notify if we are forcing unconfigured output
if ($permitted == 'forced' && !$userisconfigured) {
debugging('Attempt to force message delivery to user who has "'.$processor->name.'" output unconfigured', DEBUG_NORMAL);
}
// cli scripts work in system context, do not annoy devs with debug info
// very few scripts do not use cookies, we can safely use system as default context there
} else {
- debugging('Coding problem: this page does not set $PAGE->context properly.');
+ debugging('Coding problem: $PAGE->context was not set. You may have forgotten '
+ .'to call require_login() or $PAGE->set_context(). The page may not display '
+ .'correctly as a result');
}
$this->_context = get_context_instance(CONTEXT_SYSTEM);
}
* @param question_display_options $displayoptions the display options to use.
* @param int $variant the variant of the question to preview. If null, one will
* be picked randomly.
+ * @param object $context context to run the preview in (affects things like
+ * filter settings, theme, lang, etc.) Defaults to $PAGE->context.
* @return string the URL.
*/
function question_preview_url($questionid, $preferredbehaviour = null,
- $maxmark = null, $displayoptions = null, $variant = null) {
+ $maxmark = null, $displayoptions = null, $variant = null, $context = null) {
$params = array('id' => $questionid);
+ if (is_null($context)) {
+ global $PAGE;
+ $context = $PAGE->context;
+ }
+ if ($context->contextlevel == CONTEXT_MODULE) {
+ $params['cmid'] = $context->instanceid;
+ } else if ($context->contextlevel == CONTEXT_COURSE) {
+ $params['courseid'] = $context->instanceid;
+ }
+
if (!is_null($preferredbehaviour)) {
$params['behaviour'] = $preferredbehaviour;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function question_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function question_page_type_list($pagetype, $parentcontext, $currentcontext) {
global $CFG;
$types = array(
'question-*'=>get_string('page-question-x', 'question'),
);
if ($currentcontext->contextlevel == CONTEXT_COURSE) {
require_once($CFG->dirroot . '/course/lib.php');
- return array_merge(course_pagetypelist($pagetype, $parentcontext, $currentcontext), $types);
+ return array_merge(course_page_type_list($pagetype, $parentcontext, $currentcontext), $types);
} else {
return $types;
}
if (!local_qeupgradehelper_is_upgraded()) {
if (!$quiz->optionflags) {
$quiz->preferredbehaviour = 'deferredfeedback';
- } else if (!$quiz->penaltyscheme) {
+ } else if ($quiz->penaltyscheme) {
$quiz->preferredbehaviour = 'adaptive';
} else {
$quiz->preferredbehaviour = 'adaptivenopenalty';
* Returns description of method parameters
* @return external_function_parameters
*/
- public static function send_messages_parameters() {
+ public static function send_instantmessages_parameters() {
return new external_function_parameters(
array(
'messages' => new external_multiple_structure(
* @param $messages An array of message to send.
* @return boolean
*/
- public static function send_messages($messages = array()) {
+ public static function send_instantmessages($messages = array()) {
global $CFG, $USER, $DB;
require_once($CFG->dirroot . "/message/lib.php");
self::validate_context($context);
require_capability('moodle/site:sendmessage', $context);
- $params = self::validate_parameters(self::send_messages_parameters(), array('messages' => $messages));
+ $params = self::validate_parameters(self::send_instantmessages_parameters(), array('messages' => $messages));
//retrieve all tousers of the messages
- $touserids = array();
+ $receivers = array();
foreach($params['messages'] as $message) {
- $touserids[] = $message['touserid'];
+ $receivers[] = $message['touserid'];
}
- list($sqluserids, $sqlparams) = $DB->get_in_or_equal($touserids, SQL_PARAMS_NAMED, 'userid_');
+ list($sqluserids, $sqlparams) = $DB->get_in_or_equal($receivers, SQL_PARAMS_NAMED, 'userid_');
$tousers = $DB->get_records_select("user", "id " . $sqluserids . " AND deleted = 0", $sqlparams);
-
- //retrieve the tousers who are blocking the $USER
+ $blocklist = array();
+ $contactlist = array();
$sqlparams['contactid'] = $USER->id;
- $sqlparams['blocked'] = 1;
- //Note: return userid field should be unique for the below request,
- //so we'll use this field as key of $blockingcontacts
- $blockingcontacts = $DB->get_records_select("message_contacts",
- "userid " . $sqluserids . " AND contactid = :contactid AND blocked = :blocked",
- $sqlparams, '', "userid");
+ $rs = $DB->get_recordset_sql("SELECT *
+ FROM {message_contacts}
+ WHERE userid $sqluserids
+ AND contactid = :contactid", $sqlparams);
+ foreach ($rs as $record) {
+ if ($record->blocked) {
+ // $record->userid is blocking current user
+ $blocklist[$record->userid] = true;
+ } else {
+ // $record->userid have current user as contact
+ $contactlist[$record->userid] = true;
+ }
+ }
+ $rs->close();
$canreadallmessages = has_capability('moodle/site:readallmessages', $context);
}
//check that the touser is not blocking the current user
- if ($success and isset($blockingcontacts[$message['touserid']]) and !$canreadallmessages) {
+ if ($success and !empty($blocklist[$message['touserid']]) and !$canreadallmessages) {
$success = false;
$errormessage = get_string('userisblockingyou', 'message');
}
// Check if the user is a contact
//TODO: performance improvement - edit the function so we can pass an array instead userid
- if ($success && empty($contact) && get_user_preferences('message_blocknoncontacts', NULL, $message['touserid']) == null) {
+ $blocknoncontacts = get_user_preferences('message_blocknoncontacts', NULL, $message['touserid']);
+ // message_blocknoncontacts option is on and current user is not in contact list
+ if ($success && empty($contactlist[$message['touserid']]) && !empty($blocknoncontacts)) {
// The user isn't a contact and they have selected to block non contacts so this message won't be sent.
$success = false;
$errormessage = get_string('userisblockingyounoncontact', 'message');
* Returns description of method result value
* @return external_description
*/
- public static function send_messages_returns() {
+ public static function send_instantmessages_returns() {
return new external_multiple_structure(
new external_single_structure(
array(
- 'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the message', VALUE_OPTIONAL),
'msgid' => new external_value(PARAM_INT, 'test this to know if it succeeds: id of the created message if it succeeded, -1 when failed'),
+ 'clientmsgid' => new external_value(PARAM_ALPHANUMEXT, 'your own id for the message', VALUE_OPTIONAL),
'errormessage' => new external_value(PARAM_TEXT, 'error message - if it failed', VALUE_OPTIONAL)
)
)
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function message_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function message_page_type_list($pagetype, $parentcontext, $currentcontext) {
return array('messages-*'=>get_string('page-message-x', 'message'));
}
$string['assignmentmailhtml'] = '{$a->teacher} has posted some feedback on your
assignment submission for \'<i>{$a->assignment}</i>\'<br /><br />
You can see it appended to your <a href="{$a->url}">assignment submission</a>.';
+$string['assignmentmailsmall'] = '{$a->teacher} has posted some feedback on your
+assignment submission for \'{$a->assignment}\' You can see it appended to your submission';
$string['assignmentname'] = 'Assignment name';
$string['assignment:submit'] = 'Submit assignment';
$string['assignmentsubmission'] = 'Assignment submissions';
Only teachers who are able to grade the particular assignment are notified. So, for example, if the course uses separate groups, teachers restricted to particular groups won\'t receive notification about students in other groups.';
$string['emptysubmission'] = 'You have not submitted anything yet';
-$string['enableemailnotification'] = 'Send notification emails';
-$string['enableemailnotification_help'] = 'If enabled, students will receive email notification when their assignment submissions are graded.';
+$string['enablenotification'] = 'Send notifications';
+$string['enablenotification_help'] = 'If enabled, students will be notified when their assignment submissions are graded.';
$string['errornosubmissions'] = 'There are no submissions to download';
$string['existingfiledeleted'] = 'Existing file has been deleted: {$a}';
$string['failedupdatefeedback'] = 'Failed to update submission feedback for user {$a}';
$mformdata->submissioncommentformat= FORMAT_HTML;
$mformdata->submission_content= $this->print_user_files($user->id,true);
$mformdata->filter = $filter;
+ $mformdata->mailinfo = get_user_preferences('assignment_mailinfo', 0);
if ($assignment->assignmenttype == 'upload') {
$mformdata->fileui_options = array('subdirs'=>1, 'maxbytes'=>$assignment->maxbytes, 'maxfiles'=>$assignment->var1, 'accepted_types'=>'*', 'return_types'=>FILE_INTERNAL);
} elseif ($assignment->assignmenttype == 'uploadsingle') {
if (get_user_preferences('assignment_mailinfo', 1)) {
$mailinfopref = true;
}
- $emailnotification = html_writer::checkbox('mailinfo', 1, $mailinfopref, get_string('enableemailnotification','assignment'));
+ $emailnotification = html_writer::checkbox('mailinfo', 1, $mailinfopref, get_string('enablenotification','assignment'));
- $emailnotification .= $OUTPUT->help_icon('enableemailnotification', 'assignment');
+ $emailnotification .= $OUTPUT->help_icon('enablenotification', 'assignment');
echo html_writer::tag('div', $emailnotification, array('class'=>'emailnotification'));
$savefeedback = html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'fastg', 'value'=>get_string('saveallfeedback', 'assignment')));
$eventdata->fullmessage = $posttext;
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = $posthtml;
- $eventdata->smallmessage = '';
+ $eventdata->smallmessage = $postsubject;
$eventdata->name = 'assignment_updates';
$eventdata->component = 'mod_assignment';
default :
break;
}
- $lastmailinfo = get_user_preferences('assignment_mailinfo', 1) ? array('checked'=>'checked') : array();
$mform->addElement('hidden', 'mailinfo_h', "0");
$mform->setType('mailinfo_h', PARAM_INT);
- $mform->addElement('checkbox', 'mailinfo',get_string('enableemailnotification','assignment').
- $OUTPUT->help_icon('enableemailnotification', 'assignment') .':' );
- $mform->updateElementAttr('mailinfo', $lastmailinfo);
+ $mform->addElement('checkbox', 'mailinfo',get_string('enablenotification','assignment').
+ $OUTPUT->help_icon('enablenotification', 'assignment') .':' );
$mform->setType('mailinfo', PARAM_INT);
}
}
$eventdata->fullmessage = $posttext;
$eventdata->fullmessageformat = FORMAT_PLAIN;
$eventdata->fullmessagehtml = $posthtml;
- $eventdata->smallmessage = '';
+ $eventdata->smallmessage = get_string('assignmentmailsmall', 'assignment', $assignmentinfo);
$eventdata->name = 'assignment_updates';
$eventdata->component = 'mod_assignment';
$eventdata->notification = 1;
+ $eventdata->contexturl = $assignmentinfo->url;
+ $eventdata->contexturlname = $assignmentinfo->assignment;
message_send($eventdata);
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function assignment_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function assignment_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array(
'mod-assignment-*'=>get_string('page-mod-assignment-x', 'assignment'),
'mod-assignment-view'=>get_string('page-mod-assignment-view', 'assignment'),
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function chat_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function chat_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-chat-*'=>get_string('page-mod-chat-x', 'chat'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function choice_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function choice_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-choice-*'=>get_string('page-mod-choice-x', 'choice'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function data_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function data_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-data-*'=>get_string('page-mod-data-x', 'data'));
return $module_pagetype;
}
$analysedVals = $analysedItem[2];
$pixnr = 0;
foreach($analysedVals as $val) {
- if( function_exists("bcmod")) {
- $intvalue = bcmod($pixnr, 10);
- }else {
- $intvalue = 0;
- }
+ $intvalue = $pixnr % 10;
$pix = "pics/$intvalue.gif";
$pixnr++;
$pixwidth = intval($val->quotient * FEEDBACK_MAX_PIX_LENGTH);
$pixnr = 0;
$avg = 0.0;
foreach($analysedVals as $val) {
- if( function_exists("bcmod")) {
- $intvalue = bcmod($pixnr, 10);
- }else {
- $intvalue = 0;
- }
+ $intvalue = $pixnr % 10;
$pix = "pics/$intvalue.gif";
$pixnr++;
$pixwidth = intval($val->quotient * FEEDBACK_MAX_PIX_LENGTH);
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function feedback_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function feedback_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-feedback-*'=>get_string('page-mod-feedback-x', 'feedback'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function folder_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function folder_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-folder-*'=>get_string('page-mod-folder-x', 'folder'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function forum_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function forum_page_type_list($pagetype, $parentcontext, $currentcontext) {
$forum_pagetype = array(
'mod-forum-*'=>get_string('page-mod-forum-x', 'forum'),
'mod-forum-view'=>get_string('page-mod-forum-view', 'forum'),
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function glossary_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function glossary_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-glossary-*'=>get_string('page-mod-glossary-x', 'glossary'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function imscp_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function imscp_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-imscp-*'=>get_string('page-mod-imscp-x', 'imscp'));
return $module_pagetype;
}
// record answer (if necessary) and show response (if none say if answer is correct or not)
$page = $lesson->load_page(required_param('pageid', PARAM_INT));
+
+$userhasgrade = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$USER->id));
+$reviewmode = false;
+if ($userhasgrade && !$lesson->retake) {
+ $reviewmode = true;
+}
+
// Check the page has answers [MDL-25632]
if (count($page->answers) > 0) {
$result = $page->record_attempt($context);
if (isset($USER->modattempts[$lesson->id])) {
// make sure if the student is reviewing, that he/she sees the same pages/page path that he/she saw the first time
- if ($USER->modattempts[$lesson->id] == $page->id && $page->nextpageid == 0) { // remember, this session variable holds the pageid of the last page that the user saw
+ if ($USER->modattempts[$lesson->id]->pageid == $page->id && $page->nextpageid == 0) { // remember, this session variable holds the pageid of the last page that the user saw
$result->newpageid = LESSON_EOL;
} else {
$nretakes = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$USER->id));
}
}
// Report attempts remaining
-if ($result->attemptsremaining != 0 && !$lesson->review) {
+if ($result->attemptsremaining != 0 && !$lesson->review && !$reviewmode) {
$lesson->add_message(get_string('attemptsremaining', 'lesson', $result->attemptsremaining));
}
// Report if max attempts reached
-if ($result->maxattemptsreached != 0 && !$lesson->review) {
+if ($result->maxattemptsreached != 0 && !$lesson->review && !$reviewmode) {
$lesson->add_message('('.get_string("maximumnumberofattemptsreached", "lesson").')');
}
echo '<a name="maincontent" id="maincontent" title="'.get_string('anchortitle', 'lesson').'"></a>';
}
// This calculates and prints the ongoing score message
-if ($lesson->ongoing) {
+if ($lesson->ongoing && !$reviewmode) {
echo $lessonoutput->ongoing_score($lesson);
}
echo $result->feedback;
// User is modifying attempts - save button and some instructions
if (isset($USER->modattempts[$lesson->id])) {
$url = $CFG->wwwroot.'/mod/lesson/view.php';
- $content = $OUTPUT->box(get_string("savechangesandeol", "lesson"), 'center');
+ $content = $OUTPUT->box(get_string("gotoendoflesson", "lesson"), 'center');
$content .= $OUTPUT->box(get_string("or", "lesson"), 'center');
- $content .= $OUTPUT->box(get_string("continuetoanswer", "lesson"), 'center');
+ $content .= $OUTPUT->box(get_string("continuetonextpage", "lesson"), 'center');
$content .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=>$cm->id));
$content .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'pageid', 'value'=>LESSON_EOL));
- $content .= html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'submit', 'value'=>get_string('savechanges', 'lesson')));
+ $content .= html_writer::empty_tag('input', array('type'=>'submit', 'name'=>'submit', 'value'=>get_string('finish', 'lesson')));
echo html_writer::tag('form', "<div>$content</div>", array('method'=>'post', 'action'=>$url));
}
// Review button back
-if ($lesson->review && !$result->correctanswer && !$result->noanswer && !$result->isessayquestion) {
+if (!$result->correctanswer && !$result->noanswer && !$result->isessayquestion && !$reviewmode) {
$url = $CFG->wwwroot.'/mod/lesson/view.php';
$content = html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'id', 'value'=>$cm->id));
$content .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'pageid', 'value'=>$page->id));
$answer->timecreated = $timenow;
$answer->grade = $question->fraction[$key] * 100;
$answer->answer = $dataanswer;
- $answer->response = $question->feedback[$key];
+ $answer->response = $question->feedback[$key]['text'];
+ $answer->responseformat = $question->feedback[$key]['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
$answers[] = $answer->id;
if ($question->fraction[$key] > $maxfraction) {
$max = $question->answer[$key] + $question->tolerance[$key];
$answer->answer = $min.":".$max;
// $answer->answer = $question->min[$key].":".$question->max[$key]; original line for min/max
- $answer->response = $question->feedback[$key];
+ $answer->response = $question->feedback[$key]['text'];
+ $answer->responseformat = $question->feedback[$key]['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
$answers[] = $answer->id;
$answer->pageid = $question->id;
$answer->timecreated = $timenow;
$answer->answer = get_string("true", "quiz");
- $answer->grade = $question->answer * 100;
+ $answer->grade = $question->correctanswer * 100;
if ($answer->grade > 50 ) {
$answer->jumpto = LESSON_NEXTPAGE;
}
if (isset($question->feedbacktrue)) {
- $answer->response = $question->feedbacktrue;
+ $answer->response = $question->feedbacktrue['text'];
+ $answer->responseformat = $question->feedbacktrue['format'];
}
$DB->insert_record("lesson_answers", $answer);
$answer->pageid = $question->id;
$answer->timecreated = $timenow;
$answer->answer = get_string("false", "quiz");
- $answer->grade = (1 - (int)$question->answer) * 100;
+ $answer->grade = (1 - (int)$question->correctanswer) * 100;
if ($answer->grade > 50 ) {
$answer->jumpto = LESSON_NEXTPAGE;
}
if (isset($question->feedbackfalse)) {
- $answer->response = $question->feedbackfalse;
+ $answer->response = $question->feedbackfalse['text'];
+ $answer->responseformat = $question->feedbackfalse['format'];
}
$DB->insert_record("lesson_answers", $answer);
$answer->score = 1;
}
// end Replace
- $answer->answer = $dataanswer;
- $answer->response = $question->feedback[$key];
+ $answer->answer = $dataanswer['text'];
+ $answer->answerformat = $dataanswer['format'];
+ $answer->response = $question->feedback[$key]['text'];
+ $answer->responseformat = $question->feedback[$key]['format'];
$answer->id = $DB->insert_record("lesson_answers", $answer);
// for Sanity checks
if ($question->fraction[$key] > 0) {
$answertext = $question->subanswers[$key];
if (!empty($questiontext) and !empty($answertext)) {
$answer = clone($defaultanswer);
- $answer->answer = $questiontext;
+ $answer->answer = $questiontext['text'];
+ $answer->answerformat = $questiontext['format'];
$answer->response = $answertext;
if ($i == 0) {
// first answer contains the correct answer jump
return false;
}
- echo $OUTPUT->notification(get_string('importcount', 'lesson', sizeof($questions)));
+ //Avoid category as question type
+ echo $OUTPUT->notification(get_string('importcount', 'lesson',
+ $this->count_questions($questions)), 'notifysuccess');
$count = 0;
foreach ($questions as $question) { // Process and store each question
switch ($question->qtype) {
+ //TODO: Bad way to bypass category in data... Quickfix for MDL-27964
+ case 'category':
+ break;
// the good ones
case SHORTANSWER :
case NUMERICAL :
case MATCH :
$count++;
- echo "<hr><p><b>$count</b>. ".$question->questiontext."</p>";
+ //Show nice formated question in one line.
+ echo "<hr><p><b>$count</b>. ".$this->format_question_text($question)."</p>";
+
$newpage = new stdClass;
$newpage->lessonid = $lesson->id;
$newpage->qtype = $this->qtypeconvert[$question->qtype];
return true;
}
+ /**
+ * Count all non-category questions in the questions array.
+ *
+ * @param array questions An array of question objects.
+ * @return int The count.
+ *
+ */
+ protected function count_questions($questions) {
+ $count = 0;
+ if (!is_array($questions)) {
+ return $count;
+ }
+ foreach ($questions as $question) {
+ if (!is_object($question) || !isset($question->qtype) ||
+ ($question->qtype == 'category')) {
+ continue;
+ }
+ $count++;
+ }
+ return $count;
+ }
function readdata($filename) {
/// Returns complete file with an array, one item per line
$question = new stdClass();
$question->shuffleanswers = get_config('quiz', 'shuffleanswers');
- $question->defaultgrade = 1;
+ $question->defaultmark = 1;
$question->image = "";
$question->usecase = 0;
$question->multiplier = array();
$question->qoption = 0;
$question->layout = 1;
+ // this option in case the questiontypes class wants
+ // to know where the data came from
+ $question->export_process = true;
+ $question->import_process = true;
+
return $question;
}
return true;
}
+ /**
+ * Convert the question text to plain text, so it can safely be displayed
+ * during import to let the user see roughly what is going on.
+ */
+ protected function format_question_text($question) {
+ $formatoptions = new stdClass();
+ $formatoptions->noclean = true;
+ return html_to_text(format_text($question->questiontext,
+ $question->questiontextformat, $formatoptions), 0, false);
+ }
}
require_sesskey();
- if (!$importfile = $mform->get_importfile_name()) {
- print_error('uploadproblem', 'moodle');
- }
+ $realfilename = $mform->get_new_filename('questionfile');
+ //TODO: Leave all imported questions in Questionimport for now.
+ $importfile = "{$CFG->dataroot}/temp/questionimport/{$realfilename}";
+ make_upload_directory('temp/questionimport');
+ if (!$result = $mform->save_file('questionfile', $importfile, true)) {
+ throw new moodle_exception('uploadproblem');
+ }
$formatclass = 'qformat_'.$data->format;
$formatclassfile = $CFG->dirroot.'/question/format/'.$data->format.'/format.php';
$mform->display();
}
-echo $OUTPUT->footer();
+echo $OUTPUT->footer();
\ No newline at end of file
$mform->setType('format', 'text');
$mform->addRule('format', null, 'required');
- $mform->addElement('file', 'newfile', get_string('upload'), array('size'=>'50'));
- $mform->addRule('newfile', null, 'required');
-
- $this->add_action_buttons(null, get_string("uploadthisfile"));
+ //Using filemanager as filepicker
+ $mform->addElement('filepicker', 'questionfile', get_string('upload'));
+ $mform->addRule('questionfile', null, 'required', null, 'client');
+ $this->add_action_buttons(null, get_string("import"));
}
- public function get_importfile_name(){
- if ($this->is_submitted() and $this->is_validated()) {
- // return the temporary filename to process
- return $_FILES['newfile']['tmp_name'];
- }else{
- return NULL;
+ /**
+ * Checks that a file has been uploaded, and that it is of a plausible type.
+ * @param array $data the submitted data.
+ * @param array $errors the errors so far.
+ * @return array the updated errors.
+ */
+ protected function validate_uploaded_file($data, $errors) {
+ global $CFG;
+
+ if (empty($data['questionfile'])) {
+ $errors['questionfile'] = get_string('required');
+ return $errors;
+ }
+
+ $files = $this->get_draft_files('questionfile');
+ if (count($files) < 1) {
+ $errors['questionfile'] = get_string('required');
+ return $errors;
}
- }
- public function get_importfile_realname(){
- if ($this->is_submitted() and $this->is_validated()) {
- // return the temporary filename to process
- // TODO change this to use the files API properly.
- return $_FILES['newfile']['name'];
- }else{
- return NULL;
+ $formatfile = $CFG->dirroot.'/question/format/'.$data['format'].'/format.php';
+ if (!is_readable($formatfile)) {
+ throw new moodle_exception('formatnotfound', 'lesson', '', $data['format']);
}
+
+ require_once($formatfile);
+
+ $classname = 'qformat_' . $data['format'];
+ $qformat = new $classname();
+
+ return $errors;
}
+ public function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+ $errors = $this->validate_uploaded_file($data, $errors);
+ return $errors;
+ }
}
\ No newline at end of file
$string['congratulations'] = 'Congratulations - end of lesson reached';
$string['continue'] = 'Continue';
$string['continuetoanswer'] = 'Continue to change answers.';
+$string['continuetonextpage'] = 'Continue to next page.';
$string['correctanswerjump'] = 'Correct answer jump';
$string['correctanswerscore'] = 'Correct answer score';
$string['correctresponse'] = 'Correct response';
$string['essays'] = 'Essays';
$string['essayscore'] = 'Essay score';
$string['fileformat'] = 'File format';
+$string['finish'] = 'Finish';
$string['firstanswershould'] = 'First answer should jump to the "Correct" page';
$string['firstwrong'] = 'Unfortunately you cannot earn this one point, because your response was not correct. Would you like to keep guessing, just for the sheer joy of learning (but for no point credit)?';
$string['flowcontrol'] = 'Flow control';
$string['full'] = 'Expanded';
$string['general'] = 'General';
+$string['gotoendoflesson'] = 'Go to the end of the lesson';
$string['grade'] = 'Grade';
$string['gradebetterthan'] = 'Grade better than (%)';
$string['gradebetterthanerror'] = 'Earn a grade better than {$a} percent';
$string['studentname'] = '{$a} Name';
$string['studentoneminwarning'] = 'Warning: You have 1 minute or less to finish the lesson.';
$string['studentresponse'] = '{$a}\'s response';
+$string['submit'] = 'Submit';
$string['submitname'] = 'Submit name';
$string['teacherjumpwarning'] = 'An {$a->cluster} jump or an {$a->unseen} jump is being used in this lesson. The next page jump will be used instead. Login as a student to test these jumps.';
$string['teacherongoingwarning'] = 'Ongoing score is only displayed for student. Login as a student to test ongoing score';
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function lesson_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function lesson_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-lesson-*'=>get_string('page-mod-lesson-x', 'lesson'));
return $module_pagetype;
}
$attempt->retry = $nretakes - 1; // they are going through on review, $nretakes will be too high
}
- $DB->insert_record("lesson_attempts", $attempt);
+ if ($this->lesson->retake || (!$this->lesson->retake && $nretakes == 0)) {
+ $DB->insert_record("lesson_attempts", $attempt);
+ }
// "number of attempts remaining" message if $this->lesson->maxattempts > 1
// displaying of message(s) is at the end of page for more ergonomic display
if (!$result->correctanswer && ($result->newpageid == 0)) {
// wrong answer and student is stuck on this page - check how many attempts
// the student has had at this page/question
- $nattempts = $DB->count_records("lesson_attempts", array("pageid"=>$this->properties->id, "userid"=>$USER->id, "retry" => $nretakes));
+ $nattempts = $DB->count_records("lesson_attempts", array("pageid"=>$this->properties->id, "userid"=>$USER->id, "retry" => $attempt->retry));
// retreive the number of attempts left counter for displaying at bottom of feedback page
if ($nattempts >= $this->lesson->maxattempts) {
if ($this->lesson->maxattempts > 1) { // don't bother with message if only one attempt
public function display($renderer, $attempt) {
global $PAGE, $CFG, $USER;
- $mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents()));
+ $mform = new lesson_display_answer_form_essay($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents(), 'lessonid'=>$this->lesson->id));
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
if (isset($USER->modattempts[$this->lesson->id])) {
$essayinfo = unserialize($attempt->useranswer);
- $data->answer = array('text'=>$essayinfo->answer, 'format'=>FORMAT_HTML);
+ $data->answer = $essayinfo->answer;
}
$mform->set_data($data);
return $mform->display();
$mform = $this->_form;
$contents = $this->_customdata['contents'];
+ $hasattempt = false;
+ $attrs = '';
+ $useranswer = '';
+ $useranswerraw = '';
+ if (isset($this->_customdata['lessonid'])) {
+ $lessonid = $this->_customdata['lessonid'];
+ if (isset($USER->modattempts[$lessonid]->useranswer) && !empty($USER->modattempts[$lessonid]->useranswer)) {
+ $attrs = array('disabled' => 'disabled');
+ $hasattempt = true;
+ $useranswer = unserialize($USER->modattempts[$lessonid]->useranswer);
+ $useranswer = htmlspecialchars_decode($useranswer->answer, ENT_QUOTES);
+ }
+ }
+
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
- $mform->addElement('editor', 'answer', get_string('youranswer', 'lesson'), null, null);
- $mform->setType('answer', PARAM_RAW);
-
- $this->add_action_buttons(null, get_string("pleaseenteryouranswerinthebox", "lesson"));
+ if ($hasattempt) {
+ $mform->addElement('hidden', 'answer', $useranswerraw);
+ $mform->setType('answer', PARAM_CLEANHTML);
+ $mform->addElement('html', $OUTPUT->container(get_string('youranswer', 'lesson'), 'youranswer'));
+ $mform->addElement('html', $OUTPUT->container($useranswer, 'reviewessay'));
+ $this->add_action_buttons(null, get_string("nextpage", "lesson"));
+ } else {
+ $mform->addElement('editor', 'answer', get_string('youranswer', 'lesson'), null, null);
+ $mform->setType('answer', PARAM_RAW);
+ $this->add_action_buttons(null, get_string("submit", "lesson"));
+ }
}
-
}
protected function make_answer_form($attempt=null) {
global $USER, $CFG;
// don't shuffle answers (could be an option??)
- $answers = array_slice($this->get_answers(), 2);
+ $getanswers = array_slice($this->get_answers(), 2);
+
+ $answers = array();
+ foreach ($getanswers as $getanswer) {
+ $answers[$getanswer->id] = $getanswer;
+ }
+
$responses = array();
foreach ($answers as $answer) {
// get all the response
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
+ $hasattempt = false;
+ $disabled = '';
+ if (isset($useranswers) && !empty($useranswers)) {
+ $hasattempt = true;
+ $disabled = array('disabled' => 'disabled');
+ }
+
$options = new stdClass;
$options->para = false;
$options->noclean = true;
foreach ($answers as $answer) {
$mform->addElement('html', '<div class="answeroption">');
if ($answer->response != NULL) {
- $mform->addElement('select', 'response['.$answer->id.']', format_text($answer->answer,$answer->answerformat,$options), $responseoptions);
- $mform->setType('response['.$answer->id.']', PARAM_TEXT);
- if (isset($USER->modattempts[$lessonid])) {
- $mform->setDefault('response['.$answer->id.']', htmlspecialchars(trim($answers[$useranswers[$i]]->response))); //TODO: this is suspicious
+ $responseid = 'response['.$answer->id.']';
+ if ($hasattempt) {
+ $responseid = 'response_'.$answer->id;
+ $mform->addElement('hidden', 'response['.$answer->id.']', htmlspecialchars(trim($answers[$useranswers[$i]]->response)));
+ $mform->setType('response['.$answer->id.']', PARAM_TEXT);
+ }
+ $mform->addElement('select', $responseid, format_text($answer->answer,$answer->answerformat,$options), $responseoptions, $disabled);
+ $mform->setType($responseid, PARAM_TEXT);
+ if ($hasattempt) {
+ $mform->setDefault($responseid, htmlspecialchars(trim($answers[$useranswers[$i]]->response))); //TODO: this is suspicious
} else {
- $mform->setDefault('response['.$answer->id.']', 'answeroption');
+ $mform->setDefault($responseid, 'answeroption');
}
}
$mform->addElement('html', '</div>');
$i++;
}
-
- $this->add_action_buttons(null, get_string("pleasematchtheabovepairs", "lesson"));
+ if ($hasattempt) {
+ $this->add_action_buttons(null, get_string("nextpage", "lesson"));
+ } else {
+ $this->add_action_buttons(null, get_string("submit", "lesson"));
+ }
}
}
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
+ $hasattempt = false;
+ $disabled = '';
+ if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
+ $hasattempt = true;
+ $disabled = array('disabled' => 'disabled');
+ }
+
$options = new stdClass;
$options->para = false;
$options->noclean = true;
$i = 0;
foreach ($answers as $answer) {
$mform->addElement('html', '<div class="answeroption">');
- $mform->addElement('radio','answerid',null,format_text($answer->answer, $answer->answerformat, $options),$answer->id);
+ $mform->addElement('radio','answerid',null,format_text($answer->answer, $answer->answerformat, $options),$answer->id, $disabled);
$mform->setType('answer'.$i, PARAM_INT);
- if (isset($USER->modattempts[$lessonid]) && $answer->id == $USER->modattempts[$lessonid]->answerid) {
- $mform->setDefault('answerid', true);
+ if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) {
+ $mform->setDefault('answerid', $USER->modattempts[$lessonid]->answerid);
}
$mform->addElement('html', '</div>');
$i++;
}
- $this->add_action_buttons(null, get_string("pleasecheckoneanswer", "lesson"));
+ if ($hasattempt) {
+ $this->add_action_buttons(null, get_string("nextpage", "lesson"));
+ } else {
+ $this->add_action_buttons(null, get_string("submit", "lesson"));
+ }
}
}
}
public function display($renderer, $attempt) {
global $USER, $CFG, $PAGE;
- $mform = new lesson_display_answer_form_shortanswer($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents()));
+ $mform = new lesson_display_answer_form_shortanswer($CFG->wwwroot.'/mod/lesson/continue.php', array('contents'=>$this->get_contents(), 'lessonid'=>$this->lesson->id));
$data = new stdClass;
$data->id = $PAGE->cm->id;
$data->pageid = $this->properties->id;
class lesson_display_answer_form_shortanswer extends moodleform {
public function definition() {
- global $OUTPUT;
+ global $OUTPUT, $USER;
$mform = $this->_form;
$contents = $this->_customdata['contents'];
+ $hasattempt = false;
+ $attrs = array('size'=>'50', 'maxlength'=>'200');
+ if (isset($this->_customdata['lessonid'])) {
+ $lessonid = $this->_customdata['lessonid'];
+ if (isset($USER->modattempts[$lessonid]->useranswer)) {
+ $attrs['readonly'] = 'readonly';
+ $hasattempt = true;
+ }
+ }
+
$mform->addElement('header', 'pageheader');
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
$mform->addElement('hidden', 'pageid');
$mform->setType('pageid', PARAM_INT);
- $mform->addElement('text', 'answer', get_string('youranswer', 'lesson'), array('size'=>'50', 'maxlength'=>'200'));
+ $mform->addElement('text', 'answer', get_string('youranswer', 'lesson'), $attrs);
$mform->setType('answer', PARAM_TEXT);
- $this->add_action_buttons(null, get_string("pleaseenteryouranswerinthebox", "lesson"));
+ if ($hasattempt) {
+ $this->add_action_buttons(null, get_string("nextpage", "lesson"));
+ } else {
+ $this->add_action_buttons(null, get_string("submit", "lesson"));
+ }
}
}
$mform->addElement('html', $OUTPUT->container($contents, 'contents'));
+ $hasattempt = false;
+ $disabled = '';
+ if (isset($USER->modattempts[$lessonid]) && !empty($USER->modattempts[$lessonid])) {
+ $hasattempt = true;
+ $disabled = array('disabled' => 'disabled');
+ }
+
$options = new stdClass();
$options->para = false;
$options->noclean = true;
$i = 0;
foreach ($answers as $answer) {
$mform->addElement('html', '<div class="answeroption">');
- $mform->addElement('radio', 'answerid', null, format_text($answer->answer, $answer->answerformat, $options), $answer->id);
- $mform->setType('answerid', PARAM_INT);
- if (isset($USER->modattempts[$lessonid]) && $answer->id == $attempt->answerid) {
- $mform->setDefault('answerid', true);
+ $ansid = 'answerid';
+ if ($hasattempt) {
+ $ansid = 'answer_id';
+ }
+
+ $mform->addElement('radio', $ansid, null, format_text($answer->answer, $answer->answerformat, $options), $answer->id, $disabled);
+ $mform->setType($ansid, PARAM_INT);
+ if ($hasattempt && $answer->id == $USER->modattempts[$lessonid]->answerid) {
+ $mform->setDefault($ansid, $attempt->answerid);
+ $mform->addElement('hidden', 'answerid', $answer->id);
+ $mform->setType('answerid', PARAM_INT);
}
$mform->addElement('html', '</div>');
$i++;
}
- $this->add_action_buttons(null, get_string("pleasecheckoneanswer", "lesson"));
+ if ($hasattempt) {
+ $this->add_action_buttons(null, get_string("nextpage", "lesson"));
+ } else {
+ $this->add_action_buttons(null, get_string("submit", "lesson"));
+ }
+
}
}
/**
* Style for view.php
**/
-#page-mod-lesson-view .password-form .submitbutton {display: inline;}
\ No newline at end of file
+#page-mod-lesson-view .password-form .submitbutton {display: inline;}
+.path-mod-lesson .reviewessay {width:40%; border:1px solid #DDDDDD; background-color: #EEEEEE;}
$lessonoutput = $PAGE->get_renderer('mod_lesson');
+$reviewmode = false;
+$userhasgrade = $DB->count_records("lesson_grades", array("lessonid"=>$lesson->id, "userid"=>$USER->id));
+if ($userhasgrade && !$lesson->retake) {
+ $reviewmode = true;
+}
+
/// Check these for students only TODO: Find a better method for doing this!
/// Check lesson availability
/// Check for password
$a->minquestions = $lesson->minquestions;
$lesson->add_message(get_string('numberofpagesviewednotice', 'lesson', $a));
}
- $lesson->add_message(get_string("numberofcorrectanswers", "lesson", $gradeinfo->earned), 'notify');
+
$a = new stdClass;
$a->grade = number_format($gradeinfo->grade * $lesson->grade / 100, 1);
$a->total = $lesson->grade;
- $lesson->add_message(get_string('yourcurrentgradeisoutof', 'lesson', $a), 'notify');
+ if (!$reviewmode && !$lesson->retake){
+ $lesson->add_message(get_string("numberofcorrectanswers", "lesson", $gradeinfo->earned), 'notify');
+ $lesson->add_message(get_string('yourcurrentgradeisoutof', 'lesson', $a), 'notify');
+ }
}
}
} else {
print_error('cannotfindpreattempt', 'lesson');
}
$attempt = end($attempts);
+ $USER->modattempts[$lesson->id] = $attempt;
} else {
$attempt = false;
}
echo $OUTPUT->heading(get_string('attempt', 'lesson', $retries));
}
/// This calculates and prints the ongoing score
- if ($lesson->ongoing && !empty($pageid)) {
+ if ($lesson->ongoing && !empty($pageid) && !$reviewmode) {
echo $lessonoutput->ongoing_score($lesson);
}
if ($lesson->displayleft) {
// $ntries is decremented above
if (!$attempts = $lesson->get_attempts($ntries)) {
$attempts = array();
+ $url = new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id));
+ } else {
+ $firstattempt = current($attempts);
+ $pageid = $firstattempt->pageid;
+ // IF the student wishes to review, need to know the last question page that the student answered. This will help to make
+ // sure that the student can leave the lesson via pushing the continue button.
+ $lastattempt = end($attempts);
+ $USER->modattempts[$lesson->id] = $lastattempt->pageid;
+
+ $url = new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id, 'pageid'=>$pageid));
}
- $firstattempt = current($attempts);
- $pageid = $firstattempt->pageid;
- // IF the student wishes to review, need to know the last question page that the student answered. This will help to make
- // sure that the student can leave the lesson via pushing the continue button.
- $lastattempt = end($attempts);
- $USER->modattempts[$lesson->id] = $lastattempt->pageid;
-
- $url = new moodle_url('/mod/lesson/view.php', array('id'=>$PAGE->cm->id, 'pageid'=>$pageid));
$lessoncontent .= html_writer::link($url, get_string('reviewlesson', 'lesson'), array('class' => 'centerpadded lessonbutton standardbutton'));
-
} elseif ($lesson->modattempts && $canmanage) {
$lessoncontent .= $lessonoutput->paragraph(get_string("modattemptsnoteacher", "lesson"), 'centerpadded');
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function page_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function page_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-page-*'=>get_string('page-mod-page-x', 'page'));
return $module_pagetype;
}
}
protected function process_quiz_attempt($data) {
- global $DB;
-
$data = (object)$data;
- $oldid = $data->id;
- $olduniqueid = $data->uniqueid;
$data->quiz = $this->get_new_parentid('quiz');
$data->attempt = $data->attemptnum;
- $data->uniqueid = 0; // filled in later by {@link inform_new_usage_id()}
-
$data->userid = $this->get_mappingid('user', $data->userid);
$data->timestart = $this->apply_date_offset($data->timestart);
$data->timefinish = $this->apply_date_offset($data->timefinish);
$data->timemodified = $this->apply_date_offset($data->timemodified);
- $newitemid = $DB->insert_record('quiz_attempts', $data);
-
- // Save quiz_attempt->id mapping, because logs use it
- $this->set_mapping('quiz_attempt', $oldid, $newitemid, false);
+ // The data is actually inserted into the database later in inform_new_usage_id.
+ $this->currentquizattempt = clone($data);
}
protected function process_quiz_attempt_legacy($data) {
protected function inform_new_usage_id($newusageid) {
global $DB;
- $DB->set_field('quiz_attempts', 'uniqueid', $newusageid, array('id' =>
- $this->get_new_parentid('quiz_attempt')));
+
+ $data = $this->currentquizattempt;
+
+ $oldid = $data->id;
+ $data->uniqueid = $newusageid;
+
+ $newitemid = $DB->insert_record('quiz_attempts', $data);
+
+ // Save quiz_attempt->id mapping, because logs use it
+ $this->set_mapping('quiz_attempt', $oldid, $newitemid, false);
}
protected function after_execute() {
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function quiz_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function quiz_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-quiz-*'=>get_string('page-mod-quiz-x', 'quiz'));
return $module_pagetype;
}
* @return the HTML for a preview question icon.
*/
function quiz_question_preview_button($quiz, $question, $label = false) {
- global $CFG, $COURSE, $OUTPUT;
+ global $CFG, $OUTPUT;
if (!question_has_capability_on($question, 'use', $question->category)) {
return '';
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function resource_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function resource_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-resource-*'=>get_string('page-mod-resource-x', 'resource'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function scorm_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function scorm_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-scorm-*'=>get_string('page-mod-scorm-x', 'scorm'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function survey_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function survey_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-survey-*'=>get_string('page-mod-survey-x', 'survey'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function url_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function url_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-url-*'=>get_string('page-mod-url-x', 'url'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function wiki_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function wiki_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array(
'mod-wiki-*'=>get_string('page-mod-wiki-x', 'wiki'),
'mod-wiki-view'=>get_string('page-mod-wiki-view', 'wiki'),
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function workshop_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function workshop_page_type_list($pagetype, $parentcontext, $currentcontext) {
$module_pagetype = array('mod-workshop-*'=>get_string('page-mod-workshop-x', 'workshop'));
return $module_pagetype;
}
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function note_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function note_page_type_list($pagetype, $parentcontext, $currentcontext) {
return array('notes-*'=>get_string('page-notes-x', 'notes'));
}
// ========================================================================================================================
} else if ($component === 'user') {
if ($filearea === 'icon' and $context->contextlevel == CONTEXT_USER) {
+ // XXX: pix_url will initialize $PAGE, so we have to set up context here
+ // this temp hack should be fixed by better solution
+ $PAGE->set_context(get_system_context());
if (!empty($CFG->forcelogin) and !isloggedin()) {
// protect images if login required and not logged in;
// do not use require_login() because it is expensive and not suitable here anyway
}
public function preview_question_url($question) {
- return question_preview_url($question->id);
+ return question_preview_url($question->id, null, null, null, null,
+ $this->contexts->lowest());
}
/**
class qbehaviour_adaptive_converter extends question_behaviour_attempt_updater {
protected $try;
+ protected $laststepwasatry = false;
protected $finished = false;
protected $bestrawgrade = 0;
protected function process0($step, $state) {
$this->try = 1;
- $step->data['-_try'] = $this->try;
+ $this->laststepwasatry = false;
parent::process0($step, $state);
}
$step->fraction = $state->grade / $this->question->maxmark;
}
+ $this->laststepwasatry = false;
parent::process2($step, $state);
}
$this->bestrawgrade = max($state->raw_grade, $this->bestrawgrade);
- $this->try += 1;
$step->data['-_try'] = $this->try;
+ $this->try += 1;
+ $this->laststepwasatry = true;
if ($this->question->maxmark > 0) {
$step->data['-_rawfraction'] = $state->raw_grade / $this->question->maxmark;
} else {
}
$step->data['-finish'] = 1;
+ if ($this->laststepwasatry) {
+ $this->try -= 1;
+ }
$step->data['-_try'] = $this->try;
if ($this->question->maxmark > 0) {
$step->data['-_rawfraction'] = $state->raw_grade / $this->question->maxmark;
// Get and validate question id.
$id = required_param('id', PARAM_INT);
$question = question_bank::load_question($id);
-require_login();
-$category = $DB->get_record('question_categories',
- array('id' => $question->category), '*', MUST_EXIST);
-question_require_capability_on($question, 'use');
$PAGE->set_pagelayout('popup');
-$PAGE->set_context(get_context_instance_by_id($category->contextid));
+
+// Were we given a particular context to run the question in?
+// This affects things like filter settings, or forced theme or language.
+if ($cmid = optional_param('cmid', 0, PARAM_INT)) {
+ $cm = get_coursemodule_from_id(false, $cmid);
+ require_login($cm->course, false, $cm);
+ $context = get_context_instance(CONTEXT_MODULE, $cmid);
+
+} else if ($courseid = optional_param('courseid', 0, PARAM_INT)) {
+ require_login($courseid);
+ $context = get_context_instance(CONTEXT_COURSE, $courseid);
+
+} else {
+ require_login();
+ $category = $DB->get_record('question_categories',
+ array('id' => $question->category), '*', MUST_EXIST);
+ $context = get_context_instance_by_id($category->contextid);
+ $PAGE->set_context($context);
+ // Note that in the other cases, require_login will set the correct page context.
+}
+question_require_capability_on($question, 'use');
// Get and validate display options.
$maxvariant = $question->get_num_variants();
$options = new question_preview_options($question);
$options->load_user_defaults();
$options->set_from_request();
-$PAGE->set_url(question_preview_url($id, $options->behaviour, $options->maxmark, $options));
+$PAGE->set_url(question_preview_url($id, $options->behaviour, $options->maxmark,
+ $options, $options->variant, $context));
// Get and validate exitsing preview, or start a new one.
$previewid = optional_param('previewid', 0, PARAM_INT);
} catch (Exception $e) {
print_error('submissionoutofsequencefriendlymessage', 'question',
question_preview_url($question->id, $options->behaviour,
- $options->maxmark, $options), null, $e);
+ $options->maxmark, $options, $options->variant, $context), null, $e);
}
$slot = $quba->get_first_question_number();
$usedquestion = $quba->get_question($slot);
$options->variant = $quba->get_variant($slot);
} else {
- $quba = question_engine::make_questions_usage_by_activity('core_question_preview',
- get_context_instance_by_id($category->contextid));
+ $quba = question_engine::make_questions_usage_by_activity(
+ 'core_question_preview', $context);
$quba->set_preferred_behaviour($options->behaviour);
$slot = $quba->add_question($question, $options->maxmark);
$options->maxmark = $quba->get_question_max_mark($slot);
// Create the settings form, and initialise the fields.
-$optionsform = new preview_options_form(new moodle_url('/question/preview.php',
- array('id' => $question->id)), array('quba' => $quba, 'maxvariant' => $maxvariant));
+$optionsform = new preview_options_form(question_preview_form_url($question->id, $context),
+ array('quba' => $quba, 'maxvariant' => $maxvariant));
$optionsform->set_data($options);
// Process change of settings, if that was requested.
if (!isset($newoptions->variant)) {
$newoptions->variant = $options->variant;
}
- restart_preview($previewid, $question->id, $newoptions);
+ restart_preview($previewid, $question->id, $newoptions, $context);
}
// Prepare a URL that is used in various places.
-$actionurl = question_preview_action_url($question->id, $quba->get_id(), $options);
+$actionurl = question_preview_action_url($question->id, $quba->get_id(), $options, $context);
// Process any actions from the buttons at the bottom of the form.
if (data_submitted() && confirm_sesskey()) {
if (optional_param('restart', false, PARAM_BOOL)) {
- restart_preview($previewid, $question->id, $options);
+ restart_preview($previewid, $question->id, $options, $context);
} else if (optional_param('fill', null, PARAM_BOOL)) {
$correctresponse = $quba->get_correct_response($slot);
$quba = question_engine::load_questions_usage_by_activity($qubaid);
- if ($quba->get_owning_context()->id != $context->id) {
- send_file_not_found();
- }
-
if (!question_has_capability_on($quba->get_question($slot), 'use')) {
send_file_not_found();
}
* @param question_preview_options $options the options in use.
*/
function question_preview_action_url($questionid, $qubaid,
- question_preview_options $options) {
+ question_preview_options $options, $context) {
$params = array(
'id' => $questionid,
'previewid' => $qubaid,
);
+ if ($context->contextlevel == CONTEXT_MODULE) {
+ $params['cmid'] = $context->instanceid;
+ } else if ($context->contextlevel == CONTEXT_COURSE) {
+ $params['courseid'] = $context->instanceid;
+ }
$params = array_merge($params, $options->get_url_params());
return new moodle_url('/question/preview.php', $params);
}
+/**
+ * The the URL to use for actions relating to this preview.
+ * @param int $questionid the question being previewed.
+ * @param int $qubaid the id of the question usage for this preview.
+ * @param question_preview_options $options the options in use.
+ */
+function question_preview_form_url($questionid, $context) {
+ $params = array(
+ 'id' => $questionid,
+ );
+ if ($context->contextlevel == CONTEXT_MODULE) {
+ $params['cmid'] = $context->instanceid;
+ } else if ($context->contextlevel == CONTEXT_COURSE) {
+ $params['courseid'] = $context->instanceid;
+ }
+ return new moodle_url('/question/preview.php', $params);
+}
+
/**
* Delete the current preview, if any, and redirect to start a new preview.
* @param int $previewid
* @param int $questionid
* @param object $displayoptions
+ * @param object $context
*/
-function restart_preview($previewid, $questionid, $displayoptions) {
+function restart_preview($previewid, $questionid, $displayoptions, $context) {
global $DB;
if ($previewid) {
$transaction->allow_commit();
}
redirect(question_preview_url($questionid, $displayoptions->behaviour,
- $displayoptions->maxmark, $displayoptions, $displayoptions->variant));
+ $displayoptions->maxmark, $displayoptions, $displayoptions->variant, $context));
}
'fraction' => null,
'timecreated' => 1305830650,
'userid' => 4,
- 'data' => array('-_try' => 1, '_separators' => '.$,',
+ 'data' => array('_separators' => '.$,',
'_var_a' => '7.5', '_var_b' => '4.9'),
),
1 => (object) array(
'fraction' => null,
'timecreated' => 1305830661,
'userid' => 4,
- 'data' => array('-_try' => 1, '_separators' => '.$,',
+ 'data' => array('_separators' => '.$,',
'_var_a' => '5.1', '_var_b' => '4.5'),
),
1 => (object) array(
'fraction' => 0.5,
'timecreated' => 1305830714,
'userid' => 4,
- 'data' => array('answer' => 9.6, '-_try' => 2,
+ 'data' => array('answer' => 9.6, '-_try' => 1,
'-_rawfraction' => 0.5, '-submit' => 1),
),
2 => (object) array(
'fraction' => 0.9,
'timecreated' => 1305830722,
'userid' => 4,
- 'data' => array('answer' => '9.6 m', '-_try' => 3,
+ 'data' => array('answer' => '9.6 m', '-_try' => 2,
'-_rawfraction' => 1, '-submit' => 1),
),
3 => (object) array(
'fraction' => 0.9,
'timecreated' => 1305830722,
'userid' => 4,
- 'data' => array('answer' => '9.6 m', '-_try' => 3,
+ 'data' => array('answer' => '9.6 m', '-_try' => 2,
'-_rawfraction' => 1, '-finish' => 1),
),
),
'fraction' => null,
'timecreated' => 1305830744,
'userid' => 3,
- 'data' => array('-_try' => 1, '_separators' => '.$,',
+ 'data' => array('_separators' => '.$,',
'_var_a' => '9.9', '_var_b' => '2.5'),
),
1 => (object) array(
'fraction' => 0,
'timecreated' => 1305830775,
'userid' => 3,
- 'data' => array('answer' => '123 cm', '-_try' => 2,
+ 'data' => array('answer' => '123 cm', '-_try' => 1,
'-_rawfraction' => 0, '-submit' => 1),
),
2 => (object) array(
* @param $x
*/
public function format_float($x, $length = null, $format = null) {
- if (!is_null($format) && !is_null($format)) {
+ if (!is_null($length) && !is_null($format)) {
if ($format == 1) {
// Decimal places.
$x = sprintf('%.' . $length . 'F', $x);
* @param $x
*/
public function format_float($x, $length = null, $format = null) {
- if (!is_null($format) && !is_null($format)) {
+ if (!is_null($length) && !is_null($format)) {
if ($format == 1) {
// Decimal places.
$x = sprintf('%.' . $length . 'F', $x);
'fraction' => null,
'timecreated' => 1305830650,
'userid' => 4,
- 'data' => array('-_try' => 1,
- '_order' => '24,26,27,25', '_var_a' => '4.3', '_var_b' => '5.4'),
+ 'data' => array('_order' => '24,26,27,25', '_var_a' => '4.3', '_var_b' => '5.4'),
),
1 => (object) array(
'sequencenumber' => 1,
'fraction' => null,
'timecreated' => 1305830661,
'userid' => 4,
- 'data' => array('-_try' => 1,
- '_order' => '25,24,27,26', '_var_a' => '3.7', '_var_b' => '6.0'),
+ 'data' => array('_order' => '25,24,27,26', '_var_a' => '3.7', '_var_b' => '6.0'),
),
1 => (object) array(
'sequencenumber' => 1,
'fraction' => 1,
'timecreated' => 1305830699,
'userid' => 4,
- 'data' => array('answer' => '0', '-submit' => 1, '-_try' => 2, '-_rawfraction' => 1),
+ 'data' => array('answer' => '0', '-submit' => 1, '-_try' => 1, '-_rawfraction' => 1),
),
2 => (object) array(
'sequencenumber' => 2,
'fraction' => 1,
'timecreated' => 1305830699,
'userid' => 4,
- 'data' => array('answer' => '0', '-finish' => 1, '-_try' => 2, '-_rawfraction' => 1),
+ 'data' => array('answer' => '0', '-finish' => 1, '-_try' => 1, '-_rawfraction' => 1),
),
),
);
'fraction' => null,
'timecreated' => 1305830744,
'userid' => 3,
- 'data' => array('-_try' => 1,
- '_order' => '26,24,25,27', '_var_a' => '4.4', '_var_b' => '8.2'),
+ 'data' => array('_order' => '26,24,25,27', '_var_a' => '4.4', '_var_b' => '8.2'),
),
1 => (object) array(
'sequencenumber' => 1,
'fraction' => 0,
'timecreated' => 1305830759,
'userid' => 3,
- 'data' => array('answer' => '3', '-submit' => 1, '-_try' => 2, '-_rawfraction' => 0),
+ 'data' => array('answer' => '3', '-submit' => 1, '-_try' => 1, '-_rawfraction' => 0),
),
2 => (object) array(
'sequencenumber' => 2,
'fraction' => 0,
'timecreated' => 1305830761,
'userid' => 3,
- 'data' => array('answer' => '1', '-submit' => 1, '-_try' => 3, '-_rawfraction' => 0),
+ 'data' => array('answer' => '1', '-submit' => 1, '-_try' => 2, '-_rawfraction' => 0),
),
3 => (object) array(
'sequencenumber' => 3,
'fraction' => 0,
'timecreated' => 1305830764,
'userid' => 3,
- 'data' => array('answer' => '0', '-submit' => 1, '-_try' => 4, '-_rawfraction' => 0),
+ 'data' => array('answer' => '0', '-submit' => 1, '-_try' => 3, '-_rawfraction' => 0),
),
4 => (object) array(
'sequencenumber' => 4,
'fraction' => 0.7,
'timecreated' => 1305830766,
'userid' => 3,
- 'data' => array('answer' => '2', '-submit' => 1, '-_try' => 5, '-_rawfraction' => 1),
+ 'data' => array('answer' => '2', '-submit' => 1, '-_try' => 4, '-_rawfraction' => 1),
),
5 => (object) array(
'sequencenumber' => 5,
'fraction' => 0.7,
'timecreated' => 1305830768,
'userid' => 3,
- 'data' => array('answer' => '1', '-submit' => 1, '-_try' => 6, '-_rawfraction' => 0),
+ 'data' => array('answer' => '1', '-submit' => 1, '-_try' => 5, '-_rawfraction' => 0),
),
),
);
* @param $x
*/
public function format_float($x, $length = null, $format = null) {
- if (!is_null($format) && !is_null($format)) {
+ if (!is_null($length) && !is_null($format)) {
if ($format == 1) {
// Decimal places.
$x = sprintf('%.' . $length . 'F', $x);
'fraction' => null,
'timecreated' => 1305830650,
'userid' => 4,
- 'data' => array('-_try' => 1, '_separators' => '.$,',
+ 'data' => array('_separators' => '.$,',
'_var_a' => '3', '_var_b' => '6'),
),
1 => (object) array(
'fraction' => null,
'timecreated' => 1305830661,
'userid' => 4,
- 'data' => array('-_try' => 1, '_separators' => '.$,',
+ 'data' => array('_separators' => '.$,',
'_var_a' => '6.4', '_var_b' => '9'),
),
1 => (object) array(
'fraction' => 0,
'timecreated' => 1305830668,
'userid' => 4,
- 'data' => array('answer' => '9.00', '-submit' => 1, '-_try' => 2, '-_rawfraction' => 0),
+ 'data' => array('answer' => '9.00', '-submit' => 1, '-_try' => 1, '-_rawfraction' => 0),
),
2 => (object) array(
'sequencenumber' => 2,
'fraction' => 0.9,
'timecreated' => 1305830679,
'userid' => 4,
- 'data' => array('answer' => '15.40', '-submit' => 1, '-_try' => 3, '-_rawfraction' => 1),
+ 'data' => array('answer' => '15.40', '-submit' => 1, '-_try' => 2, '-_rawfraction' => 1),
),
3 => (object) array(
'sequencenumber' => 3,
'fraction' => 0.9,
'timecreated' => 1305830679,
'userid' => 4,
- 'data' => array('answer' => '15.40', '-finish' => 1, '-_try' => 3, '-_rawfraction' => 1),
+ 'data' => array('answer' => '15.40', '-finish' => 1, '-_try' => 2, '-_rawfraction' => 1),
),
),
);
'fraction' => null,
'timecreated' => 1305830744,
'userid' => 3,
- 'data' => array('-_try' => 1, '_separators' => '.$,',
+ 'data' => array('_separators' => '.$,',
'_var_a' => '6.1', '_var_b' => '7'),
),
1 => (object) array(
'fraction' => null,
'timecreated' => 1306425691,
'userid' => 4,
- 'data' => array('-_try' => 1),
+ 'data' => array(),
),
1 => (object) array(
'sequencenumber' => 1,
'fraction' => null,
'timecreated' => 1306425757,
'userid' => 4,
- 'data' => array('-_try' => '1'),
+ 'data' => array(),
),
1 => (object) array(
'sequencenumber' => 1,
'fraction' => null,
'timecreated' => 1306425784,
'userid' => 3,
- 'data' => array('-_try' => '1'),
+ 'data' => array(),
),
1 => (object) array(
'sequencenumber' => 1,
'fraction' => 1,
'timecreated' => 1306425917,
'userid' => 3,
- 'data' => array('sub1_answer' => 'frog', '-_try' => 2,
+ 'data' => array('sub1_answer' => 'frog', '-_try' => 1,
'-_rawfraction' => 1.0, '-submit' => 1),
),
3 => (object) array(
'fraction' => 1,
'timecreated' => 1306425917,
'userid' => 3,
- 'data' => array('sub1_answer' => 'frog', '-_try' => 2,
+ 'data' => array('sub1_answer' => 'frog', '-_try' => 1,
'-_rawfraction' => 1.0, '-finish' => 1),
),
),
'fraction' => null,
'timecreated' => 1306425691,
'userid' => 4,
- 'data' => array('-_try' => '1', '_sub1_order' => '29,30,31,32',
+ 'data' => array('_sub1_order' => '29,30,31,32',
'_sub3_separators' => '.$,', '_sub4_order' => '38,39,40,41',
'_sub5_order' => '42,43,44,45', '_sub7_order' => '49,50',
'_sub8_separators' => '.$,'),
'data' => array('sub1_answer' => '2', 'sub2_answer' => 'dsf',
'sub3_answer' => 'sadf', 'sub4_answer' => '1',
'sub5_answer' => '2', 'sub6_answer' => 'MOODLE',
- 'sub7_answer' => 'sadf', 'sub8_answer' => '100%', '-_try' => '2',
+ 'sub7_answer' => 'sadf', 'sub8_answer' => '100%', '-_try' => '1',
'-_rawfraction' => '0.38461538461538466', '-submit' => '1'),
),
2 => (object) array(
'data' => array('sub1_answer' => '2', 'sub2_answer' => 'dsf',
'sub3_answer' => 'sadf', 'sub4_answer' => '1',
'sub5_answer' => '2', 'sub6_answer' => 'MOODLE',
- 'sub7_answer' => 'sadf', 'sub8_answer' => '100%', '-_try' => '2',
+ 'sub7_answer' => 'sadf', 'sub8_answer' => '100%', '-_try' => '1',
'-_rawfraction' => '0.38461538461538466', '-finish' => '1'),
),
),
'fraction' => null,
'timecreated' => 1306425757,
'userid' => 4,
- 'data' => array('-_try' => '1', '_sub1_order' => '29,30,31,32',
+ 'data' => array('_sub1_order' => '29,30,31,32',
'_sub3_separators' => '.$,', '_sub4_order' => '38,39,40,41',
'_sub5_order' => '42,43,44,45', '_sub7_order' => '49,50',
'_sub8_separators' => '.$,'),
'fraction' => null,
'timecreated' => 1306425784,
'userid' => 3,
- 'data' => array('-_try' => '1', '_sub1_order' => '29,30,31,32',
+ 'data' => array('_sub1_order' => '29,30,31,32',
'_sub3_separators' => '.$,', '_sub4_order' => '38,39,40,41',
'_sub5_order' => '42,43,44,45', '_sub7_order' => '49,50',
'_sub8_separators' => '.$,'),
'data' => array('sub1_answer' => '3', 'sub2_answer' => 'asdgf',
'sub3_answer' => 'saedf', 'sub4_answer' => '1',
'sub5_answer' => '1', 'sub6_answer' => 'sadf',
- '-_try' => '2',
+ '-_try' => '1',
'-_rawfraction' => '0.038461538461538464', '-submit' => '1'),
),
2 => (object) array(
'data' => array('sub1_answer' => '2', 'sub2_answer' => 'asdgf',
'sub3_answer' => '28.3', 'sub4_answer' => '2',
'sub5_answer' => '2', 'sub6_answer' => 'MOODLE',
- 'sub7_answer' => '0', 'sub8_answer' => '13', '-_try' => '3',
+ 'sub7_answer' => '0', 'sub8_answer' => '13', '-_try' => '2',
'-_rawfraction' => '0.53846153846153845', '-submit' => '1'),
),
3 => (object) array(
'data' => array('sub1_answer' => '2', 'sub2_answer' => 'Correct answer',
'sub3_answer' => '23.8', 'sub4_answer' => '2',
'sub5_answer' => '2', 'sub6_answer' => 'MOODLE',
- 'sub7_answer' => '0', 'sub8_answer' => '3', '-_try' => '4',
+ 'sub7_answer' => '0', 'sub8_answer' => '3', '-_try' => '3',
'-_rawfraction' => '0.92307692307692311', '-submit' => '1'),
),
4 => (object) array(
'data' => array('sub1_answer' => '2', 'sub2_answer' => 'Correct answer',
'sub3_answer' => '23.8', 'sub4_answer' => '2',
'sub5_answer' => '2', 'sub6_answer' => 'MOODLE',
- 'sub7_answer' => '0', 'sub8_answer' => '3', '-_try' => '4',
+ 'sub7_answer' => '0', 'sub8_answer' => '3', '-_try' => '3',
'-_rawfraction' => '0.92307692307692311', '-finish' => '1'),
),
),
$response = $qa->get_last_qt_var($fieldname);
if ($subq->qtype->name() == 'shortanswer') {
$matchinganswer = $subq->get_matching_answer(array('answer' => $response));
+ } else if ($subq->qtype->name() == 'numerical') {
+ $matchinganswer = $subq->get_matching_answer($response, 1);
} else {
$matchinganswer = $subq->get_matching_answer($response);
}
defined('MOODLE_INTERNAL') || die();
+require_once($CFG->dirroot . '/question/type/edit_question_form.php');
require_once($CFG->dirroot . '/question/type/numerical/questiontype.php');
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qtype_numerical_edit_form extends question_edit_form {
+ protected $ap = null;
protected function definition_inner($mform) {
$this->add_per_answer_fields($mform, get_string('answerno', 'qtype_numerical', '{no}'),
$tolerance = $mform->createElement('text', 'tolerance',
get_string('acceptederror', 'qtype_numerical'));
$repeatedoptions['tolerance']['type'] = PARAM_NUMBER;
+ $repeatedoptions['tolerance']['default'] = 0;
array_splice($repeated, 3, 0, array($tolerance));
$repeated[1]->setSize(10);
$unitinputoptions = array(
qtype_numerical::UNITINPUT => get_string('editableunittext', 'qtype_numerical'),
- qtype_numerical::UNITSELECT => get_string('unitchoice', 'qtype_numerical'),
+ qtype_numerical::UNITRADIO => get_string('unitchoice', 'qtype_numerical'),
+ qtype_numerical::UNITSELECT => get_string('unitselect', 'qtype_numerical'),
);
$mform->addElement('select', 'multichoicedisplay',
get_string('studentunitanswer', 'qtype_numerical'), $unitinputoptions);
if ($trimmedanswer != '') {
$answercount++;
if (!$this->is_valid_answer($trimmedanswer, $data)) {
- $errors['answer[' . $key . ']'] = $this->valid_answer_message();
+ $errors['answer[' . $key . ']'] = $this->valid_answer_message($trimmedanswer);
}
if ($data['fraction'][$key] == 1) {
$maxgrade = true;
}
} else if ($data['fraction'][$key] != 0 ||
!html_is_blank($data['feedback'][$key]['text'])) {
- $errors['answer[' . $key . ']'] = $this->valid_answer_message();
+ $errors['answer[' . $key . ']'] = $this->valid_answer_message($trimmedanswer);
$answercount++;
}
}
if ($maxgrade == false) {
$errors['fraction[0]'] = get_string('fractionsnomax', 'question');
}
+
+ return $errors;
}
/**
* @return bool whether this is a valid answer.
*/
protected function is_valid_answer($answer, $data) {
- return $answer == '*' || is_numeric($answer);
+ return $answer == '*' || $this->is_valid_number($x);
+ }
+
+ /**
+ * Validate that a string is a nubmer formatted correctly for the current locale.
+ * @param string $x a string
+ * @return bool whether $x is a number that the numerical question type can interpret.
+ */
+ protected function is_valid_number($x) {
+ if (is_null($this->ap)) {
+ $this->ap = new qtype_numerical_answer_processor(array());
+ }
+
+ list($value, $unit) = $this->ap->apply_units($x);
+
+ return !is_null($value) && !$unit;
}
/**
$string['addingnumerical'] = 'Adding a Numerical question';
$string['addmoreanswerblanks'] = 'Blanks for {no} More Answers';
$string['addmoreunitblanks'] = 'Blanks for {no} More Units';
-$string['answermustbenumberorstar'] = 'The answer must be a number, or \'*\'.';
+$string['answermustbenumberorstar'] = 'The answer must be a number, for example -1.234 or 3e8, or \'*\'.';
$string['answerno'] = 'Answer {$a}';
$string['decfractionofquestiongrade'] = 'as a fraction (0-1) of the question grade';
$string['decfractionofresponsegrade'] = 'as a fraction (0-1) of the response grade';
$string['onlynumerical'] = 'Units are not used at all. Only the numerical value is graded.';
$string['oneunitshown'] = 'Unit 1 is automatically displayed beside the answer box.';
$string['pleaseenterananswer'] = 'Please enter an answer.';
+$string['pleaseenteranswerwithoutthousandssep'] = 'Please enter your answer without using the thousand separator ({$a}).';
$string['relative'] = 'Relative';
$string['rightexample'] = 'on the right, for example 1.00cm or 1.00km';
$string['selectunits'] = 'Select units';
$string['studentunitanswer'] = 'Units are input using';
$string['tolerancetype'] = 'Tolerance type';
$string['unit'] = 'Unit';
+$string['unitappliedpenalty'] = 'These marks include a penalty of {$a} for bad unit.';
$string['unitchoice'] = 'a multiple choice selection';
$string['unitedit'] = 'Edit unit';
$string['unitgraded'] = 'The unit must be given, and will be graded.';
+$string['unithandling'] = 'Unit handling';
$string['unithdr'] = 'Unit {$a}';
$string['unitincorrect'] = 'You did not give the correct unit.';
$string['unitmandatory'] = 'Mandatory';
* The unit penalty will be applied if the unit field is empty
';
+$string['unitnotselected'] = 'You must select a unit.';
$string['unitonerequired'] = 'You must enter at least one unit';
$string['unitoptional'] = 'Optional unit';
$string['unitoptional_help'] = '
* If the unit is badly written or unknown, the response will be considered as non valid.
';
-$string['unitnotselected'] = 'You must select a unit.';
$string['unitpenalty'] = 'Unit penalty';
$string['unitpenalty_help'] = 'The penalty is applied if
* the wrong unit name is entered into the unit input, or
* a unit is entered into the value input box';
-$string['unitappliedpenalty'] = 'These marks include a penalty of {$a} for bad unit.';
$string['unitposition'] = 'Units go';
-$string['unithandling'] = 'Unit handling';
+$string['unitselect'] = 'a drop-down menu';
$string['validnumberformats'] = 'Valid number formats';
$string['validnumberformats_help'] = '
* regular numbers 13500.67, 13 500.67, 13500,67 or 13 500,67
/** @var array of question_answer. */
public $answers = array();
- /** @var int one of the constants UNITNONE, UNITSELECT or UNITINPUT. */
+ /** @var int one of the constants UNITNONE, UNITRADIO, UNITSELECT or UNITINPUT. */
public $unitdisplay;
/** @var int one of the constants UNITGRADEDOUTOFMARK or UNITGRADEDOUTOFMAX. */
public $unitgradingtype;
public function get_expected_data() {
$expected = array('answer' => PARAM_RAW_TRIMMED);
- if ($this->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($this->has_separate_unit_field()) {
$expected['unit'] = PARAM_RAW_TRIMMED;
}
return $expected;
}
+ public function has_separate_unit_field() {
+ return $this->unitdisplay == qtype_numerical::UNITRADIO ||
+ $this->unitdisplay == qtype_numerical::UNITSELECT;
+ }
+
public function start_attempt(question_attempt_step $step, $variant) {
$step->set_qt_var('_separators',
$this->ap->get_point() . '$' . $this->ap->get_separator());
$resp = null;
}
- if ($this->unitdisplay == qtype_numerical::UNITSELECT && !empty($response['unit'])) {
+ if ($this->has_separate_unit_field() && !empty($response['unit'])) {
$resp = $this->ap->add_unit($resp, $response['unit']);
}
return false;
}
- if ($this->unitdisplay == qtype_numerical::UNITSELECT && empty($response['unit'])) {
+ if ($this->has_separate_unit_field() && empty($response['unit'])) {
+ return false;
+ }
+
+ if ($this->ap->contains_thousands_seaparator($response['answer'])) {
return false;
}
return get_string('invalidnumbernounit', 'qtype_numerical');
}
- if ($this->unitdisplay == qtype_numerical::UNITSELECT && empty($response['unit'])) {
+ if ($this->has_separate_unit_field() && empty($response['unit'])) {
return get_string('unitnotselected', 'qtype_numerical');
}
+ if ($this->ap->contains_thousands_seaparator($response['answer'])) {
+ return get_string('pleaseenteranswerwithoutthousandssep', 'qtype_numerical',
+ $this->ap->get_separator());
+ }
+
return '';
}
return false;
}
- if ($this->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($this->has_separate_unit_field()) {
return question_utils::arrays_same_at_key_missing_is_blank(
$prevresponse, $newresponse, 'unit');
}
return array();
}
- $response = array('answer' => $answer->answer);
+ $response = array('answer' => str_replace('.', $this->ap->get_point(), $answer->answer));
- if ($this->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($this->has_separate_unit_field()) {
$response['unit'] = $this->ap->get_default_unit();
} else if ($this->unitdisplay == qtype_numerical::UNITINPUT) {
$response['answer'] = $this->ap->add_unit($answer->answer);
* Get an answer that contains the feedback and fraction that should be
* awarded for this resonse.
* @param number $value the numerical value of a response.
+ * @param number $multiplier for the unit the student gave, if any. When no
+ * unit was given, or an unrecognised unit was given, $multiplier will be null.
* @return question_answer the matching answer.
*/
- public function get_matching_answer($value) {
+ public function get_matching_answer($value, $multiplier) {
+ if (!is_null($multiplier)) {
+ $scaledvalue = $value * $multiplier;
+ } else {
+ $scaledvalue = $value;
+ }
foreach ($this->answers as $aid => $answer) {
- if ($answer->within_tolerance($value)) {
- $answer->id = $aid;
+ if ($answer->within_tolerance($scaledvalue)) {
+ $answer->unitisright = !is_null($multiplier);
+ return $answer;
+ } else if ($answer->within_tolerance($value)) {
+ $answer->unitisright = false;
return $answer;
}
}
return null;
}
- public function apply_unit_penalty($fraction, $unit) {
- if (!empty($unit) && $this->ap->is_known_unit($unit)) {
+ /**
+ * Adjust the fraction based on whether the unit was correct.
+ * @param number $fraction
+ * @param bool $unitisright
+ * @return number
+ */
+ public function apply_unit_penalty($fraction, $unitisright) {
+ if ($unitisright) {
return $fraction;
}
}
public function grade_response(array $response) {
- if ($this->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($this->has_separate_unit_field()) {
$selectedunit = $response['unit'];
} else {
$selectedunit = null;
}
- list($value, $unit) = $this->ap->apply_units($response['answer'], $selectedunit);
- $answer = $this->get_matching_answer($value);
+ list($value, $unit, $multiplier) = $this->ap->apply_units(
+ $response['answer'], $selectedunit);
+
+ $answer = $this->get_matching_answer($value, $multiplier);
if (!$answer) {
return array(0, question_state::$gradedwrong);
}
- $fraction = $this->apply_unit_penalty($answer->fraction, $unit);
+ $fraction = $this->apply_unit_penalty($answer->fraction, $answer->unitisright);
return array($fraction, question_state::graded_state_for_fraction($fraction));
}
return array($this->id => question_classified_response::no_response());
}
- if ($this->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($this->has_separate_unit_field()) {
$selectedunit = $response['unit'];
} else {
$selectedunit = null;
}
- list($value, $unit) = $this->ap->apply_units($response['answer'], $selectedunit);
- $ans = $this->get_matching_answer($value);
+ list($value, $unit, $multiplier) = $this->ap->apply_units($response['answer'], $selectedunit);
+ $ans = $this->get_matching_answer($value, $multiplier);
if (!$ans) {
return array($this->id => question_classified_response::no_response());
}
$resp = $response['answer'];
- if ($this->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($this->has_separate_unit_field()) {
$resp = $this->ap->add_unit($resp, $unit);
}
return array($this->id => new question_classified_response($ans->id,
$resp,
- $this->apply_unit_penalty($ans->fraction, $unit)));
+ $this->apply_unit_penalty($ans->fraction, $ans->unitisright)));
}
public function check_file_access($qa, $options, $component, $filearea, $args,
$forcedownload) {
if ($component == 'question' && $filearea == 'answerfeedback') {
+ $question = $qa->get_question();
$currentanswer = $qa->get_last_qt_var('answer');
- $answer = $qa->get_question()->get_matching_answer(array('answer' => $currentanswer));
+ if ($this->has_separate_unit_field()) {
+ $selectedunit = $qa->get_last_qt_var('unit');
+ } else {
+ $selectedunit = null;
+ }
+ list($value, $unit, $multiplier) = $question->ap->apply_units(
+ $currentanswer, $selectedunit);
+ $answer = $question->get_matching_answer($value, $multiplier);
$answerid = reset($args); // itemid is answer id.
return $options->feedback && $answerid == $answer->id;
*/
class qtype_numerical extends question_type {
const UNITINPUT = 0;
- const UNITSELECT = 1;
+ const UNITRADIO = 1;
+ const UNITSELECT = 2;
const UNITNONE = 3;
const UNITGRADED = 1;
*/
public function apply_unit($rawresponse, $units, $unitsleft) {
$ap = $this->make_answer_processor($units, $unitsleft);
- list($value, $unit) = $ap->apply_units($rawresponse);
+ list($value, $unit, $multiplier) = $ap->apply_units($rawresponse);
+ if (!is_null($multiplier)) {
+ $value *= $multiplier;
+ }
return $value;
}
return $this->thousandssep;
}
+ /**
+ * @return book If the student's response contains a '.' or a ',' that
+ * matches the thousands separator in the current locale. In this case, the
+ * parsing in apply_unit can give a result that the student did not expect.
+ */
+ public function contains_thousands_seaparator($value) {
+ if (!in_array($this->thousandssep, array('.', ','))) {
+ return false;
+ }
+
+ return strpos($value, $this->thousandssep) !== false;
+ }
+
/**
* Create the regular expression that {@link parse_response()} requires.
* @return string
}
/**
+ * This method can be used for more locale-strict parsing of repsonses. At the
+ * moment we don't use it, and instead use the more lax parsing in apply_units.
+ * This is just a note that this funciton was used in the past, so if you are
+ * intersted, look through version control history.
+ *
* Take a string which is a number with or without a decimal point and exponent,
* and possibly followed by one of the units, and split it into bits.
* @param string $response a value, optionally with a unit.
}
/**
- * Takes a number in localised form, that is, using the decsep and thousandssep
- * defined in the lanuage pack, and possibly with a unit after it. It separates
- * off the unit, if present, and converts to the default unit, by using the
- * given unit multiplier.
+ * Takes a number in almost any localised form, and possibly with a unit
+ * after it. It separates off the unit, if present, and converts to the
+ * default unit, by using the given unit multiplier.
*
* @param string $response a value, optionally with a unit.
* @return array(numeric, sting) the value with the unit stripped, and normalised
* by the unit multiplier, if any, and the unit string, for reference.
*/
public function apply_units($response, $separateunit = null) {
- list($beforepoint, $decimals, $exponent, $unit) = $this->parse_response($response);
+ // Strip spaces (which may be thousands separators) and change other forms
+ // of writing e to e.
+ $response = str_replace(' ', '', $response);
+ $response = preg_replace('~(?:e|E|(?:x|\*|×)10(?:\^|\*\*))([+-]?\d+)~', 'e$1', $response);
+
+ // If a . is present or there are multiple , (i.e. 2,456,789 ) assume ,
+ // is a thouseands separator, and strip it, else assume it is a decimal
+ // separator, and change it to ..
+ if (strpos($response, '.') !== false || substr_count($response, ',') > 1) {
+ $response = str_replace(',', '', $response);
+ } else {
+ $response = str_replace(',', '.', $response);
+ }
- if (is_null($beforepoint)) {
- return array(null, null);
+ $regex = '[+-]?(?:\d+(?:\\.\d*)?|\\.\d+)(?:e[-+]?\d+)?';
+ if ($this->unitsbefore) {
+ $regex = "/$regex$/";
+ } else {
+ $regex = "/^$regex/";
+ }
+ if (!preg_match($regex, $response, $matches)) {
+ return array(null, null, null);
}
- $numberstring = $beforepoint . '.' . $decimals;
- if ($exponent) {
- $numberstring .= 'e' . $exponent;
+ $numberstring = $matches[0];
+ if ($this->unitsbefore) {
+ $unit = substr($response, 0, -strlen($numberstring));
+ } else {
+ $unit = substr($response, strlen($numberstring));
}
if (!is_null($separateunit)) {
}
if ($unit && $this->is_known_unit($unit)) {
- $value = $numberstring / $this->units[$unit];
+ $multiplier = 1 / $this->units[$unit];
} else {
- $value = $numberstring * 1;
+ $multiplier = null;
}
- return array($value, $unit);
+ return array($numberstring + 0, $unit, $multiplier); // + 0 to convert to number.
}
/**
$question = $qa->get_question();
$currentanswer = $qa->get_last_qt_var('answer');
- if ($question->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($question->has_separate_unit_field()) {
$selectedunit = $qa->get_last_qt_var('unit');
} else {
$selectedunit = null;
$feedbackimg = '';
if ($options->correctness) {
- list($value, $unit) = $question->ap->apply_units($currentanswer, $selectedunit);
- $answer = $question->get_matching_answer($value);
+ list($value, $unit, $multiplier) = $question->ap->apply_units(
+ $currentanswer, $selectedunit);
+ $answer = $question->get_matching_answer($value, $multiplier);
if ($answer) {
- $fraction = $question->apply_unit_penalty($answer->fraction, $unit);
+ $fraction = $question->apply_unit_penalty($answer->fraction, $answer->unitisright);
} else {
$fraction = 0;
}
$input = html_writer::empty_tag('input', $inputattributes) . $feedbackimg;
- if ($question->unitdisplay == qtype_numerical::UNITSELECT) {
- $unitselect = html_writer::select($question->ap->get_unit_options(),
- $qa->get_qt_field_name('unit'), $selectedunit, array(''=>'choosedots'),
- array('disabled' => $options->readonly));
+ if ($question->has_separate_unit_field()) {
+ if ($question->unitdisplay == qtype_numerical::UNITRADIO) {
+ $choices = array();
+ $i = 1;
+ foreach ($question->ap->get_unit_options() as $unit) {
+ $id = $qa->get_qt_field_name('unit') . '_' . $i++;
+ $radioattrs = array('type' => 'radio', 'id' => $id, 'value' => $unit,
+ 'name' => $qa->get_qt_field_name('unit'));
+ if ($unit == $selectedunit) {
+ $radioattrs['checked'] = 'checked';
+ }
+ $choices[] = html_writer::tag('label',
+ html_writer::empty_tag('input', $radioattrs) . $unit,
+ array('for' => $id, 'class' => 'unitchoice'));
+ }
+
+ $unitchoice = html_writer::tag('span', implode(' ', $choices),
+ array('class' => 'unitchoices'));
+
+ } else if ($question->unitdisplay == qtype_numerical::UNITSELECT) {
+ $unitchoice = html_writer::select($question->ap->get_unit_options(),
+ $qa->get_qt_field_name('unit'), $selectedunit, array(''=>'choosedots'),
+ array('disabled' => $options->readonly));
+ }
+
if ($question->ap->are_units_before()) {
- $input = $unitselect . ' ' . $input;
+ $input = $unitchoice . ' ' . $input;
} else {
- $input = $input . ' ' . $unitselect;
+ $input = $input . ' ' . $unitchoice;
}
}
public function specific_feedback(question_attempt $qa) {
$question = $qa->get_question();
- if ($question->unitdisplay == qtype_numerical::UNITSELECT) {
+ if ($question->has_separate_unit_field()) {
$selectedunit = $qa->get_last_qt_var('unit');
} else {
$selectedunit = null;
}
- list($value, $unit) = $question->ap->apply_units(
+ list($value, $unit, $multiplier) = $question->ap->apply_units(
$qa->get_last_qt_var('answer'), $selectedunit);
- $answer = $question->get_matching_answer($value);
+ $answer = $question->get_matching_answer($value, $multiplier);
if ($answer && $answer->feedback) {
$feedback = $question->format_text($answer->feedback, $answer->feedbackformat,
return '';
}
- $response = $answer->answer;
+ $response = str_replace('.', $question->ap->get_point(), $answer->answer);
if ($question->unitdisplay != qtype_numerical::UNITNONE) {
$response = $question->ap->add_unit($response);
}
- return get_string('correctansweris', 'qtype_shortanswer', s($response));
+ return get_string('correctansweris', 'qtype_shortanswer', $response);
}
}
$this->assertEqual(array(null, null, null, null), $ap->parse_response(','));
}
- protected function verify_value_and_unit($exectedval, $expectedunit,
+ protected function verify_value_and_unit($exectedval, $expectedunit, $expectedmultiplier,
qtype_numerical_answer_processor $ap, $input, $separateunit = null) {
- list($val, $unit) = $ap->apply_units($input, $separateunit);
+ list($val, $unit, $multiplier) = $ap->apply_units($input, $separateunit);
if (is_null($exectedval)) {
$this->assertNull($val);
} else {
$this->assertWithinMargin($exectedval, $val, 0.0001);
}
$this->assertEqual($expectedunit, $unit);
+ if (is_null($expectedmultiplier)) {
+ $this->assertNull($multiplier);
+ } else {
+ $this->assertWithinMargin($expectedmultiplier, $multiplier, 0.0001);
+ }
}
public function test_apply_units() {
array('m/s' => 1, 'c' => 3.3356409519815E-9,
'mph' => 2.2369362920544), false, '.', ',');
- $this->verify_value_and_unit(3e8, 'm/s', $ap, '3x10^8 m/s');
- $this->verify_value_and_unit(3e8, '', $ap, '3x10^8');
- $this->verify_value_and_unit(299792458, 'c', $ap, '1c');
- $this->verify_value_and_unit(0.44704, 'mph', $ap, '0001.000 mph');
+ $this->verify_value_and_unit(3e8, 'm/s', 1, $ap, '3x10^8 m/s');
+ $this->verify_value_and_unit(3e8, '', null, $ap, '3x10^8');
+ $this->verify_value_and_unit(1, 'c', 299792458, $ap, '1c');
+ $this->verify_value_and_unit(1, 'mph', 0.44704, $ap, '0001.000 mph');
- $this->verify_value_and_unit(1, 'frogs', $ap, '1 frogs');
- $this->verify_value_and_unit(null, null, $ap, '. m/s');
+ $this->verify_value_and_unit(1, 'frogs', null, $ap, '1 frogs');
+ $this->verify_value_and_unit(null, null, null, $ap, '. m/s');
}
public function test_apply_units_separate_unit() {
array('m/s' => 1, 'c' => 3.3356409519815E-9,
'mph' => 2.2369362920544), false, '.', ',');
- $this->verify_value_and_unit(3e8, 'm/s', $ap, '3x10^8', 'm/s');
- $this->verify_value_and_unit(3e8, '', $ap, '3x10^8', '');
- $this->verify_value_and_unit(299792458, 'c', $ap, '1', 'c');
- $this->verify_value_and_unit(0.44704, 'mph', $ap, '0001.000', 'mph');
+ $this->verify_value_and_unit(3e8, 'm/s', 1, $ap, '3x10^8', 'm/s');
+ $this->verify_value_and_unit(3e8, '', null, $ap, '3x10^8', '');
+ $this->verify_value_and_unit(1, 'c', 299792458, $ap, '1', 'c');
+ $this->verify_value_and_unit(1, 'mph', 0.44704, $ap, '0001.000', 'mph');
- $this->verify_value_and_unit(1, 'frogs', $ap, '1', 'frogs');
- $this->verify_value_and_unit(null, null, $ap, '.', 'm/s');
+ $this->verify_value_and_unit(1, 'frogs', null, $ap, '1', 'frogs');
+ $this->verify_value_and_unit(null, null, null, $ap, '.', 'm/s');
}
public function test_euro_style() {
$ap = new qtype_numerical_answer_processor(array(), false, ',', ' ');
- $this->assertEqual(array(-1000, ''), $ap->apply_units('-1 000'));
- $this->assertEqual(array(3.14159, ''), $ap->apply_units('3,14159'));
+ $this->assertEqual(array(-1000, '', null), $ap->apply_units('-1 000'));
+ $this->assertEqual(array(3.14159, '', null), $ap->apply_units('3,14159'));
}
public function test_percent() {
$ap = new qtype_numerical_answer_processor(array('%' => 100), false, '.', ',');
- $this->assertEqual(array('0.03', '%'), $ap->apply_units('3%'));
- $this->assertEqual(array('1e-8', '%'), $ap->apply_units('1e-6 %'));
- $this->assertEqual(array('100', ''), $ap->apply_units('100'));
+ $this->assertEqual(array('3', '%', 0.01), $ap->apply_units('3%'));
+ $this->assertEqual(array('1e-6', '%', 0.01), $ap->apply_units('1e-6 %'));
+ $this->assertEqual(array('100', '', null), $ap->apply_units('100'));
}
public function test_currency() {
$ap = new qtype_numerical_answer_processor(array('$' => 1, '£' => 1), true, '.', ',');
- $this->assertEqual(array('1234.56', '£'), $ap->apply_units('£1,234.56'));
- $this->assertEqual(array('100', '$'), $ap->apply_units('$100'));
- $this->assertEqual(array('100', '$'), $ap->apply_units('$100.'));
- $this->assertEqual(array('100.00', '$'), $ap->apply_units('$100.00'));
- $this->assertEqual(array('100', ''), $ap->apply_units('100'));
- $this->assertEqual(array('100', 'frog'), $ap->apply_units('frog 100'));
+ $this->assertEqual(array('1234.56', '£', 1), $ap->apply_units('£1,234.56'));
+ $this->assertEqual(array('100', '$', 1), $ap->apply_units('$100'));
+ $this->assertEqual(array('100', '$', 1), $ap->apply_units('$100.'));
+ $this->assertEqual(array('100.00', '$', 1), $ap->apply_units('$100.00'));
+ $this->assertEqual(array('100', '', null), $ap->apply_units('100'));
+ $this->assertEqual(array('100', 'frog', null), $ap->apply_units('frog 100'));
}
}
--- /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/>.
+
+/**
+ * Unit tests for (some of) question/type/numerical/edit_numerical_form.php.
+ *
+ * @package qtype
+ * @subpackage numerical
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/question/type/numerical/edit_numerical_form.php');
+
+
+/**
+ * Test sub-class, so we can force the locale.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class test_qtype_numerical_edit_form extends qtype_numerical_edit_form {
+ public function __construct() {
+ // Warning, avoid running the parent constructor. That means the form is
+ // not properly tested but for now that is OK, we are only testing a few
+ // methods.
+ $this->ap = new qtype_numerical_answer_processor(array(), false, ',', ' ');
+ }
+ public function is_valid_number($x) {
+ return parent::is_valid_number($x);
+ }
+}
+
+
+/**
+ * Unit tests for question/type/numerical/edit_numerical_form.php.
+ *
+ * @copyright 2011 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_numerical_form_test extends UnitTestCase {
+ public static $includecoverage = array(
+ 'question/type/numerical/edit_numerical_form.php'
+ );
+
+ protected $form;
+
+ public function setUp() {
+ $this->form = new test_qtype_numerical_edit_form();
+ }
+
+ public function tearDown() {
+ $this->form = null;
+ }
+
+ public function test_is_valid_number() {
+ $this->assertTrue($this->form->is_valid_number('1,001'));
+ $this->assertTrue($this->form->is_valid_number('1.001'));
+ $this->assertTrue($this->form->is_valid_number('1'));
+ $this->assertTrue($this->form->is_valid_number('1,e8'));
+ $this->assertFalse($this->form->is_valid_number('1001 xxx'));
+ $this->assertTrue($this->form->is_valid_number('1.e8'));
+ }
+}
public function test_grading_with_units() {
$question = test_question_maker::make_question('numerical');
+ $question->unitgradingtype = qtype_numerical::UNITOPTIONAL;
$question->ap = new qtype_numerical_answer_processor(
array('m' => 1, 'cm' => 100), false, '.', ',');
$question->grade_response(array('answer' => '314000000x10^-8m')));
}
+ public function test_grading_with_units_graded() {
+ $question = test_question_maker::make_question('numerical');
+ $question->unitgradingtype = qtype_numerical::UNITGRADED;
+ $question->ap = new qtype_numerical_answer_processor(
+ array('m' => 1, 'cm' => 100), false, '.', ',');
+
+ $this->assertEqual(array(0.8, question_state::$gradedpartial),
+ $question->grade_response(array('answer' => '3.14 frogs')));
+ $this->assertEqual(array(0.8, question_state::$gradedpartial),
+ $question->grade_response(array('answer' => '3.14')));
+ $this->assertEqual(array(1, question_state::$gradedright),
+ $question->grade_response(array('answer' => '3.14 m')));
+ $this->assertEqual(array(1, question_state::$gradedright),
+ $question->grade_response(array('answer' => '314cm')));
+ $this->assertEqual(array(1, question_state::$gradedright),
+ $question->grade_response(array('answer' => '314000000x10^-8m')));
+ $this->assertEqual(array(0.8, question_state::$gradedpartial),
+ $question->grade_response(array('answer' => '3.14 cm')));
+ $this->assertEqual(array(0, question_state::$gradedwrong),
+ $question->grade_response(array('answer' => '314 m')));
+ }
+
public function test_grading_unit() {
$question = test_question_maker::make_question('numerical', 'unit');
$this->assertEqual(array(1, question_state::$gradedright),
$question->grade_response(array('answer' => '$1332')));
$this->assertEqual(array(1, question_state::$gradedright),
- $question->grade_response(array('answer' => '$ 1,332')));
+ $question->grade_response(array('answer' => '$ 1332')));
$this->assertEqual(array(0.8, question_state::$gradedpartial),
$question->grade_response(array('answer' => 'frog 1332')));
$this->assertEqual(array(0.8, question_state::$gradedpartial),
$question->grade_response(array('answer' => '1332')));
$this->assertEqual(array(0.8, question_state::$gradedpartial),
- $question->grade_response(array('answer' => ' 1,332')));
+ $question->grade_response(array('answer' => ' 1332')));
$this->assertEqual(array(0, question_state::$gradedwrong),
$question->grade_response(array('answer' => '1332 $')));
$this->assertEqual(array(0, question_state::$gradedwrong),
- $question->grade_response(array('answer' => '1,332 frogs')));
+ $question->grade_response(array('answer' => '1332 frogs')));
$this->assertEqual(array(0, question_state::$gradedwrong),
$question->grade_response(array('answer' => '$1')));
}
new question_classified_response(14, '$100', 0)),
$num->classify_response(array('answer' => '$100')));
$this->assertEqual(array(
- new question_classified_response(13, '1,332', 0.8)),
- $num->classify_response(array('answer' => '1,332')));
+ new question_classified_response(13, '1 332', 0.8)),
+ $num->classify_response(array('answer' => '1 332')));
}
}
\ No newline at end of file
$this->compare_qas($expectedqa, $qa);
}
+
+ public function test_truefalse_adaptive_qsession119() {
+ $quiz = (object) array(
+ 'id' => '6',
+ 'course' => '3',
+ 'name' => 'Simply quiz',
+ 'intro' => '<p>One quiz with 1 true/false Q</p>',
+ 'introformat' => '1',
+ 'timeopen' => '0',
+ 'timeclose' => '0',
+ 'attempts' => '0',
+ 'attemptonlast' => '0',
+ 'grademethod' => '1',
+ 'decimalpoints' => '2',
+ 'questiondecimalpoints' => '-1',
+ 'review' => '4459503',
+ 'questionsperpage' => '1',
+ 'shufflequestions' => '0',
+ 'shuffleanswers' => '1',
+ 'questions' => '30,0',
+ 'sumgrades' => '10.00000',
+ 'grade' => '10.00000',
+ 'timecreated' => '0',
+ 'timemodified' => '1309103209',
+ 'timelimit' => '0',
+ 'password' => '',
+ 'subnet' => '',
+ 'popup' => '0',
+ 'delay1' => '0',
+ 'delay2' => '0',
+ 'showuserpicture' => '0',
+ 'showblocks' => '0',
+ 'preferredbehaviour' => 'adaptive',
+ );
+ $attempt = (object) array(
+ 'id' => '20',
+ 'uniqueid' => '20',
+ 'quiz' => '6',
+ 'userid' => '7',
+ 'attempt' => '1',
+ 'sumgrades' => '10.00000',
+ 'timestart' => '1309103112',
+ 'timefinish' => '1309103120',
+ 'timemodified' => '1309103120',
+ 'layout' => '30,0',
+ 'preview' => '0',
+ 'needsupgradetonewqe' => 1,
+ );
+ $question = (object) array(
+ 'id' => '30',
+ 'category' => '10',
+ 'parent' => '0',
+ 'name' => '1 + 1 = 2 ?',
+ 'questiontext' => '<p>1 +1 = 2 ?</p>',
+ 'questiontextformat' => '1',
+ 'generalfeedback' => '<p>this is general feedback</p>',
+ 'generalfeedbackformat' => '1',
+ 'penalty' => '1.0000000',
+ 'qtype' => 'truefalse',
+ 'length' => '1',
+ 'stamp' => '127.0.0.1+110626154410+wFrWwP',
+ 'version' => '127.0.0.1+110626154410+u7CoaA',
+ 'hidden' => '0',
+ 'timecreated' => '1309103050',
+ 'timemodified' => '1309103050',
+ 'createdby' => '6',
+ 'modifiedby' => '6',
+ 'maxmark' => '10.0000000',
+ 'options' => (object) array(
+ 'id' => '4',
+ 'question' => '30',
+ 'trueanswer' => '53',
+ 'falseanswer' => '54',
+ 'answers' => array(
+ 53 => (object) array(
+ 'id' => '53',
+ 'question' => '30',
+ 'answer' => 'True',
+ 'answerformat' => '0',
+ 'fraction' => '1.0000000',
+ 'feedback' => '<p>this is correct (for true) feedback</p>',
+ 'feedbackformat' => '1',
+ ),
+ 54 => (object) array(
+ 'id' => '54',
+ 'question' => '30',
+ 'answer' => 'False',
+ 'answerformat' => '0',
+ 'fraction' => '0.0000000',
+ 'feedback' => '<p>this is incorrect (for false) feedback</p>',
+ 'feedbackformat' => '1',
+ ),
+ ),
+ ),
+ 'defaultmark' => '1.0000000',
+ );
+ $qsession = (object) array(
+ 'id' => '119',
+ 'attemptid' => '20',
+ 'questionid' => '30',
+ 'newest' => '312',
+ 'newgraded' => '312',
+ 'sumpenalty' => '10.0000000',
+ 'manualcomment' => '',
+ 'manualcommentformat' => '1',
+ 'flagged' => '0',
+ );
+ $qstates = array(
+ 310 => (object) array(
+ 'id' => '310',
+ 'attempt' => '20',
+ 'question' => '30',
+ 'seq_number' => '0',
+ 'answer' => '',
+ 'timestamp' => '1309103112',
+ 'event' => '0',
+ 'grade' => '0.0000000',
+ 'raw_grade' => '0.0000000',
+ 'penalty' => '0.0000000',
+ ),
+ 311 => (object) array(
+ 'id' => '311',
+ 'attempt' => '20',
+ 'question' => '30',
+ 'seq_number' => '1',
+ 'answer' => '53',
+ 'timestamp' => '1309103115',
+ 'event' => '3',
+ 'grade' => '10.0000000',
+ 'raw_grade' => '10.0000000',
+ 'penalty' => '10.0000000',
+ ),
+ 312 => (object) array(
+ 'id' => '312',
+ 'attempt' => '20',
+ 'question' => '30',
+ 'seq_number' => '1',
+ 'answer' => '53',
+ 'timestamp' => '1309103115',
+ 'event' => '6',
+ 'grade' => '10.0000000',
+ 'raw_grade' => '10.0000000',
+ 'penalty' => '10.0000000',
+ ),
+ );
+
+ $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates);
+
+ $expectedqa = (object) array(
+ 'behaviour' => 'adaptive',
+ 'questionid' => 30,
+ 'variant' => 1,
+ 'maxmark' => 10.0000000,
+ 'minfraction' => 0,
+ 'flagged' => 0,
+ 'questionsummary' => '1 +1 = 2 ?',
+ 'rightanswer' => 'True',
+ 'responsesummary' => 'True',
+ 'timemodified' => 1309103115,
+ 'steps' => array(
+ 0 => (object) array(
+ 'sequencenumber' => 0,
+ 'state' => 'todo',
+ 'fraction' => null,
+ 'timecreated' => 1309103112,
+ 'userid' => 7,
+ 'data' => array(),
+ ),
+ 1 => (object) array(
+ 'sequencenumber' => 1,
+ 'state' => 'complete',
+ 'fraction' => 1.0,
+ 'timecreated' => 1309103115,
+ 'userid' => 7,
+ 'data' => array('answer' => '1', '-_try' => '1',
+ '-_rawfraction' => '1', '-submit' => '1'),
+ ),
+ 2 => (object) array(
+ 'sequencenumber' => 2,
+ 'state' => 'gradedright',
+ 'fraction' => 1.0,
+ 'timecreated' => 1309103115,
+ 'userid' => 7,
+ 'data' => array('answer' => '1', '-_try' => '1',
+ '-_rawfraction' => '1', '-finish' => '1'),
+ ),
+ ),
+ );
+
+ $this->compare_qas($expectedqa, $qa);
+ }
+
+ public function test_truefalse_adaptive_qsession120() {
+ $quiz = (object) array(
+ 'id' => '6',
+ 'course' => '3',
+ 'name' => 'Simply quiz',
+ 'intro' => '<p>One quiz with 1 true/false Q</p>',
+ 'introformat' => '1',
+ 'timeopen' => '0',
+ 'timeclose' => '0',
+ 'attempts' => '0',
+ 'attemptonlast' => '0',
+ 'grademethod' => '1',
+ 'decimalpoints' => '2',
+ 'questiondecimalpoints' => '-1',
+ 'review' => '4459503',
+ 'questionsperpage' => '1',
+ 'shufflequestions' => '0',
+ 'shuffleanswers' => '1',
+ 'questions' => '30,0',
+ 'sumgrades' => '10.00000',
+ 'grade' => '10.00000',
+ 'timecreated' => '0',
+ 'timemodified' => '1309103209',
+ 'timelimit' => '0',
+ 'password' => '',
+ 'subnet' => '',
+ 'popup' => '0',
+ 'delay1' => '0',
+ 'delay2' => '0',
+ 'showuserpicture' => '0',
+ 'showblocks' => '0',
+ 'preferredbehaviour' => 'adaptive',
+ );
+ $attempt = (object) array(
+ 'id' => '21',
+ 'uniqueid' => '21',
+ 'quiz' => '6',
+ 'userid' => '7',
+ 'attempt' => '2',
+ 'sumgrades' => '0.00000',
+ 'timestart' => '1309103130',
+ 'timefinish' => '1309103136',
+ 'timemodified' => '1309103136',
+ 'layout' => '30,0',
+ 'preview' => '0',
+ 'needsupgradetonewqe' => 1,
+ );
+ $question = (object) array(
+ 'id' => '30',
+ 'category' => '10',
+ 'parent' => '0',
+ 'name' => '1 + 1 = 2 ?',
+ 'questiontext' => '<p>1 +1 = 2 ?</p>',
+ 'questiontextformat' => '1',
+ 'generalfeedback' => '<p>this is general feedback</p>',
+ 'generalfeedbackformat' => '1',
+ 'penalty' => '1.0000000',
+ 'qtype' => 'truefalse',
+ 'length' => '1',
+ 'stamp' => '127.0.0.1+110626154410+wFrWwP',
+ 'version' => '127.0.0.1+110626154410+u7CoaA',
+ 'hidden' => '0',
+ 'timecreated' => '1309103050',
+ 'timemodified' => '1309103050',
+ 'createdby' => '6',
+ 'modifiedby' => '6',
+ 'maxmark' => '10.0000000',
+ 'options' => (object) array(
+ 'id' => '4',
+ 'question' => '30',
+ 'trueanswer' => '53',
+ 'falseanswer' => '54',
+ 'answers' => array(
+ 53 => (object) array(
+ 'id' => '53',
+ 'question' => '30',
+ 'answer' => 'True',
+ 'answerformat' => '0',
+ 'fraction' => '1.0000000',
+ 'feedback' => '<p>this is correct (for true) feedback</p>',
+ 'feedbackformat' => '1',
+ ),
+ 54 => (object) array(
+ 'id' => '54',
+ 'question' => '30',
+ 'answer' => 'False',
+ 'answerformat' => '0',
+ 'fraction' => '0.0000000',
+ 'feedback' => '<p>this is incorrect (for false) feedback</p>',
+ 'feedbackformat' => '1',
+ ),
+ ),
+ ),
+ 'defaultmark' => '1.0000000',
+ );
+ $qsession = (object) array(
+ 'id' => '120',
+ 'attemptid' => '21',
+ 'questionid' => '30',
+ 'newest' => '315',
+ 'newgraded' => '315',
+ 'sumpenalty' => '10.0000000',
+ 'manualcomment' => '',
+ 'manualcommentformat' => '1',
+ 'flagged' => '0',
+ );
+ $qstates = array(
+ 313 => (object) array(
+ 'id' => '313',
+ 'attempt' => '21',
+ 'question' => '30',
+ 'seq_number' => '0',
+ 'answer' => '',
+ 'timestamp' => '1309103130',
+ 'event' => '0',
+ 'grade' => '0.0000000',
+ 'raw_grade' => '0.0000000',
+ 'penalty' => '0.0000000',
+ ),
+ 314 => (object) array(
+ 'id' => '314',
+ 'attempt' => '21',
+ 'question' => '30',
+ 'seq_number' => '1',
+ 'answer' => '54',
+ 'timestamp' => '1309103132',
+ 'event' => '2',
+ 'grade' => '0.0000000',
+ 'raw_grade' => '0.0000000',
+ 'penalty' => '10.0000000',
+ ),
+ 315 => (object) array(
+ 'id' => '315',
+ 'attempt' => '21',
+ 'question' => '30',
+ 'seq_number' => '2',
+ 'answer' => '54',
+ 'timestamp' => '1309103132',
+ 'event' => '6',
+ 'grade' => '0.0000000',
+ 'raw_grade' => '0.0000000',
+ 'penalty' => '10.0000000',
+ ),
+ );
+
+ $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates);
+
+ $expectedqa = (object) array(
+ 'behaviour' => 'adaptive',
+ 'questionid' => 30,
+ 'variant' => 1,
+ 'maxmark' => 10.0000000,
+ 'minfraction' => 0,
+ 'flagged' => 0,
+ 'questionsummary' => '1 +1 = 2 ?',
+ 'rightanswer' => 'True',
+ 'responsesummary' => 'False',
+ 'timemodified' => 1309103132,
+ 'steps' => array(
+ 0 => (object) array(
+ 'sequencenumber' => 0,
+ 'state' => 'todo',
+ 'fraction' => null,
+ 'timecreated' => 1309103130,
+ 'userid' => 7,
+ 'data' => array(),
+ ),
+ 1 => (object) array(
+ 'sequencenumber' => 1,
+ 'state' => 'complete',
+ 'fraction' => null,
+ 'timecreated' => 1309103132,
+ 'userid' => 7,
+ 'data' => array('answer' => '0'),
+ ),
+ 2 => (object) array(
+ 'sequencenumber' => 2,
+ 'state' => 'gradedwrong',
+ 'fraction' => 0.0,
+ 'timecreated' => 1309103132,
+ 'userid' => 7,
+ 'data' => array('answer' => 0, '-finish' => 1,
+ '-_try' => 1, '-_rawfraction' => 0),
+ ),
+ ),
+ );
+
+ $this->compare_qas($expectedqa, $qa);
+ }
}
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
- * Version information for the true/fales question type.
+ * Version information for the true/false question type.
*
* @package qtype
* @subpackage truefalse
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function tag_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function tag_page_type_list($pagetype, $parentcontext, $currentcontext) {
return array(
'tag-*'=>get_string('page-tag-x', 'tag'),
'tag-index'=>get_string('page-tag-index', 'tag'),
public static function get_users_by_id($userids) {
global $CFG, $USER, $DB;
require_once($CFG->dirroot . "/user/lib.php");
- require_once($CFG->dirroot . "/user/profile/lib.php"); //custom field library
- require_once($CFG->dirroot . "/lib/filelib.php"); // file handling on description and friends
-
- $isadmin = is_siteadmin($USER);
$params = self::validate_parameters(self::get_users_by_id_parameters(),
array('userids'=>$userids));
$users = $DB->get_recordset_sql($usersql, $params);
$result = array();
+ $hasuserupdatecap = has_capability('moodle/user:update', get_system_context());
foreach ($users as $user) {
if (!empty($user->deleted)) {
continue;
}
context_instance_preload($user);
- // cached
- $context = get_context_instance(CONTEXT_USER, $user->id);
- $hasviewdetailscap = has_capability('moodle/user:viewdetails', $context);
- $hasuserupdatecap = has_capability('moodle/user:update', get_system_context());
-
- self::validate_context($context);
-
+ $usercontext = get_context_instance(CONTEXT_USER, $user->id);
+ self::validate_context($usercontext);
$currentuser = ($user->id == $USER->id);
- if (!$currentuser && !$hasviewdetailscap && !has_coursecontact_role($user->id)) {
- throw new moodle_exception('usernotavailable', 'error');
- }
-
- $userarray = array();
-
- //basic fields
- $userarray['id'] = $user->id;
- if ($isadmin) {
- $userarray['username'] = $user->username;
- }
- if ($isadmin or has_capability('moodle/site:viewfullnames', $context)) {
- $userarray['firstname'] = $user->firstname;
- $userarray['lastname'] = $user->lastname;
- }
- $userarray['fullname'] = fullname($user);
-
- //fields matching permissions from /user/editadvanced.php
- if ($currentuser or $hasuserupdatecap) {
- $userarray['auth'] = $user->auth;
- $userarray['confirmed'] = $user->confirmed;
- $userarray['idnumber'] = $user->idnumber;
- $userarray['lang'] = $user->lang;
- $userarray['theme'] = $user->theme;
- $userarray['timezone'] = $user->timezone;
- $userarray['mailformat'] = $user->mailformat;
- }
-
- //Custom fields (matching /user/profil/lib.php - profile_display_fields code logic)
- $fields = $DB->get_recordset_sql("SELECT f.*
- FROM {user_info_field} f
- JOIN {user_info_category} c
- ON f.categoryid=c.id
- ORDER BY c.sortorder ASC, f.sortorder ASC");
- foreach ($fields as $field) {
- require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
- $newfield = 'profile_field_'.$field->datatype;
- $formfield = new $newfield($field->id, $user->id);
- if ($formfield->is_visible() and !$formfield->is_empty()) {
- $userarray['customfields'][] =
- array('name' => $formfield->field->name, 'value' => $formfield->data,
- 'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
- }
- }
- $fields->close();
-
- //image profiles urls (public, no permission required in fact)
- $profileimageurl = moodle_url::make_pluginfile_url($context->id, 'user', 'icon', NULL, '/', 'f1');
- $userarray['profileimageurl'] = $profileimageurl->out(false);
- $profileimageurlsmall = moodle_url::make_pluginfile_url($context->id, 'user', 'icon', NULL, '/', 'f2');
- $userarray['profileimageurlsmall'] = $profileimageurlsmall->out(false);
-
- //hidden user field
- if (has_capability('moodle/user:viewhiddendetails', $context)) {
- $hiddenfields = array();
- } else {
- $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
- }
-
- if (isset($user->description) && (!isset($hiddenfields['description']) or $isadmin)) {
- if (empty($CFG->profilesforenrolledusersonly) || $currentuser) {
- $user->description = file_rewrite_pluginfile_urls($user->description, 'pluginfile.php', $context->id, 'user', 'profile', null);
- $userarray['description'] = $user->description;
- $userarray['descriptionformat'] = $user->descriptionformat;
- }
- }
-
- if ((! isset($hiddenfields['country']) or $isadmin) && $user->country) {
- $userarray['country'] = $user->country;
- }
-
- if ((! isset($hiddenfields['city']) or $isadmin) && $user->city) {
- $userarray['city'] = $user->city;
- }
-
- if (has_capability('moodle/user:viewhiddendetails', $context)) {
- if ($user->address) {
- $userarray['address'] = $user->address;
- }
- if ($user->phone1) {
- $userarray['phone1'] = $user->phone1;
- }
- if ($user->phone2) {
- $userarray['phone2'] = $user->phone2;
- }
- }
-
- if ($currentuser
- or $user->maildisplay == 1
- or has_capability('moodle/course:useremail', $context)
- or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER))) {
- $userarray['email'] = $user->email;;
- }
-
- if ($user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
- $url = $user->url;
- if (strpos($user->url, '://') === false) {
- $url = 'http://'. $url;
- }
- $user->url = clean_param($user->url, PARAM_URL);
- $userarray['url'] = $user->url;
- }
-
- if ($user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
- $userarray['icq'] = $user->icq;
- }
-
- if ($user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
- $userarray['skype'] = $user->skype;
- }
- if ($user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
- $userarray['yahoo'] = $user->yahoo;
- }
- if ($user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
- $userarray['aim'] = $user->aim;
- }
- if ($user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
- $userarray['msn'] = $user->msn;
- }
-
- if ((!isset($hiddenfields['firstaccess'])) or $isadmin) {
- if ($user->firstaccess) {
- $userarray['firstaccess'] = $user->firstaccess;
- } else {
- $userarray['firstaccess'] = 0;
- }
- }
- if ((!isset($hiddenfields['lastaccess'])) or $isadmin) {
- if ($user->lastaccess) {
- $userarray['lastaccess'] = $user->lastaccess;
- } else {
- $userarray['lastaccess'] = 0;
+ if ($userarray = user_get_user_details($user)) {
+ //fields matching permissions from /user/editadvanced.php
+ if ($currentuser or $hasuserupdatecap) {
+ $userarray['auth'] = $user->auth;
+ $userarray['confirmed'] = $user->confirmed;
+ $userarray['idnumber'] = $user->idnumber;
+ $userarray['lang'] = $user->lang;
+ $userarray['theme'] = $user->theme;
+ $userarray['timezone'] = $user->timezone;
+ $userarray['mailformat'] = $user->mailformat;
}
+ $result[] = $userarray;
}
- /// Printing tagged interests
- if (!empty($CFG->usetags)) {
- require_once($CFG->dirroot . '/tag/lib.php');
- if ($interests = tag_get_tags_csv('user', $user->id, TAG_RETURN_TEXT) ) {
- $userarray['interests'] = $interests;
- }
- }
-
- //Departement/Institution are not displayed on any profile, however you can get them from editing profile.
- if ($isadmin or $currentuser) {
- if ($user->institution) {
- $userarray['institution'] = $user->institution;
- }
- if (isset($user->department)) { //isset because it's ok to have department 0
- $userarray['department'] = $user->department;
- }
- }
-
- //list of courses where the user is enrolled
- $enrolledcourses = array();
- if (!isset($hiddenfields['mycourses'])) {
- if ($mycourses = enrol_get_users_courses($user->id, true, NULL, 'visible DESC,sortorder ASC')) {
- $courselisting = '';
- foreach ($mycourses as $mycourse) {
- if ($mycourse->category) {
- if ($mycourse->visible == 0) {
- $ccontext = get_context_instance(CONTEXT_COURSE, $mycourse->id);
- if (!has_capability('moodle/course:viewhiddencourses', $ccontext)) {
- continue;
- }
- }
- $enrolledcourse = array();
- $enrolledcourse['id'] = $mycourse->id;
- $enrolledcourse['fullname'] = $mycourse->fullname;
- $enrolledcourses[] = $enrolledcourse;
- }
- }
- $userarray['enrolledcourses'] = $enrolledcourses;
- }
- }
-
- //user preferences
- if ($currentuser) {
- $preferences = array();
- $userpreferences = get_user_preferences();
- foreach($userpreferences as $prefname => $prefvalue) {
- $preferences[] = array('name' => $prefname, 'value' => $prefvalue);
- }
- $userarray['preferences'] = $preferences;
- }
- $result[] = $userarray;
}
$users->close();
new external_single_structure(
array(
'id' => new external_value(PARAM_INT, 'Id of the course'),
- 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course')
+ 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
+ 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
)
), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
)
public static function get_course_participants_by_id($userlist) {
global $CFG, $USER, $DB;
require_once($CFG->dirroot . "/user/lib.php");
- require_once($CFG->dirroot . "/user/profile/lib.php"); //custom field library
- require_once($CFG->dirroot . "/lib/filelib.php"); // file handling on description and friends
-
- $isadmin = is_siteadmin($USER);
-
$params = self::validate_parameters(self::get_course_participants_by_id_parameters(), array('userlist'=>$userlist));
$userids = array();
continue;
}
context_instance_preload($user);
- $usercontext = get_context_instance(CONTEXT_USER, $user->id);
$course = $courses[$courseids[$user->id]];
$context = get_context_instance(CONTEXT_COURSE, $courseids[$user->id]);
- $hasviewdetailscap = has_capability('moodle/user:viewdetails', $context) || has_capability('moodle/user:viewdetails', $usercontext);
-
self::validate_context($context);
-
- $currentuser = ($user->id == $USER->id);
-
- if (!$currentuser && !$hasviewdetailscap && !has_coursecontact_role($user->id)) {
- throw new moodle_exception('usernotavailable', 'error');
- }
- $userarray = array();
-
- //basic fields
- $userarray['id'] = $user->id;
- if ($isadmin) {
- $userarray['username'] = $user->username;
- }
- if ($isadmin or has_capability('moodle/site:viewfullnames', $context)) {
- $userarray['firstname'] = $user->firstname;
- $userarray['lastname'] = $user->lastname;
- }
- $userarray['fullname'] = fullname($user);
-
- //Custom fields (matching /user/profile/lib.php - profile_display_fields code logic)
- $userarray['customfields'] = array();
-
- $fields = $DB->get_recordset_sql("SELECT f.*
- FROM {user_info_field} f
- JOIN {user_info_category} c
- ON f.categoryid=c.id
- ORDER BY c.sortorder ASC, f.sortorder ASC");
- foreach ($fields as $field) {
- require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
- $newfield = 'profile_field_'.$field->datatype;
- $formfield = new $newfield($field->id, $user->id);
- if ($formfield->is_visible() and !$formfield->is_empty()) {
- $userarray['customfields'][] =
- array('name' => $formfield->field->name, 'value' => $formfield->data,
- 'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
- }
- }
- $fields->close();
-
- //image profiles urls (public, no permission required in fact)
- $profileimageurl = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f1');
- $userarray['profileimageurl'] = $profileimageurl->out(false);
- $profileimageurlsmall = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f2');
- $userarray['profileimageurlsmall'] = $profileimageurlsmall->out(false);
-
- //hidden user field
- if (has_capability('moodle/course:viewhiddenuserfields', $context)) {
- $hiddenfields = array();
- } else {
- $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
- }
-
- if (isset($user->description) && (!isset($hiddenfields['description']) or $isadmin)) {
- if (empty($CFG->profilesforenrolledusersonly) || $currentuser) {
- $user->description = file_rewrite_pluginfile_urls($user->description, 'pluginfile.php', $context->id, 'user', 'profile', null);
- $userarray['description'] = $user->description;
- $userarray['descriptionformat'] = $user->descriptionformat;
- }
- }
-
- if ((! isset($hiddenfields['country']) or $isadmin) && $user->country) {
- $userarray['country'] = $user->country;
+ if ($userarray = user_get_user_details($user, $course)) {
+ $result[] = $userarray;
}
+ }
- if ((! isset($hiddenfields['city']) or $isadmin) && $user->city) {
- $userarray['city'] = $user->city;
- }
+ $users->close();
- if (has_capability('moodle/course:viewhiddenuserfields', $context)) {
- if ($user->address) {
- $userarray['address'] = $user->address;
- }
- if ($user->phone1) {
- $userarray['phone1'] = $user->phone1;
- }
- if ($user->phone2) {
- $userarray['phone2'] = $user->phone2;
- }
- }
+ return $result;
+ }
- if ($currentuser
- or $user->maildisplay == 1
- or has_capability('moodle/course:useremail', $context)
- or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER))) {
- $userarray['email'] = $user->email;;
- }
+ /**
+ * Returns description of method result value
+ * @return external_description
+ */
+ public static function get_course_participants_by_id_returns() {
+ return new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_NUMBER, 'ID of the user'),
+ 'username' => new external_value(PARAM_RAW, 'Username policy is defined in Moodle security config', VALUE_OPTIONAL),
+ 'firstname' => new external_value(PARAM_NOTAGS, 'The first name(s) of the user', VALUE_OPTIONAL),
+ 'lastname' => new external_value(PARAM_NOTAGS, 'The family name of the user', VALUE_OPTIONAL),
+ 'fullname' => new external_value(PARAM_NOTAGS, 'The fullname of the user'),
+ 'email' => new external_value(PARAM_TEXT, 'An email address - allow email as root@localhost', VALUE_OPTIONAL),
+ 'address' => new external_value(PARAM_MULTILANG, 'Postal address', VALUE_OPTIONAL),
+ 'phone1' => new external_value(PARAM_NOTAGS, 'Phone 1', VALUE_OPTIONAL),
+ 'phone2' => new external_value(PARAM_NOTAGS, 'Phone 2', VALUE_OPTIONAL),
+ 'icq' => new external_value(PARAM_NOTAGS, 'icq number', VALUE_OPTIONAL),
+ 'skype' => new external_value(PARAM_NOTAGS, 'skype id', VALUE_OPTIONAL),
+ 'yahoo' => new external_value(PARAM_NOTAGS, 'yahoo id', VALUE_OPTIONAL),
+ 'aim' => new external_value(PARAM_NOTAGS, 'aim id', VALUE_OPTIONAL),
+ 'msn' => new external_value(PARAM_NOTAGS, 'msn number', VALUE_OPTIONAL),
+ 'department' => new external_value(PARAM_TEXT, 'department', VALUE_OPTIONAL),
+ 'institution' => new external_value(PARAM_TEXT, 'institution', VALUE_OPTIONAL),
+ 'interests' => new external_value(PARAM_TEXT, 'user interests (separated by commas)', VALUE_OPTIONAL),
+ 'firstaccess' => new external_value(PARAM_INT, 'first access to the site (0 if never)', VALUE_OPTIONAL),
+ 'lastaccess' => new external_value(PARAM_INT, 'last access to the site (0 if never)', VALUE_OPTIONAL),
+ 'description' => new external_value(PARAM_RAW, 'User profile description', VALUE_OPTIONAL),
+ 'descriptionformat' => new external_value(PARAM_INT, 'User profile description format', VALUE_OPTIONAL),
+ 'city' => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
+ 'url' => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
+ 'country' => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
+ 'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
+ 'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
+ 'customfields' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'type' => new external_value(PARAM_ALPHANUMEXT, 'The type of the custom field - text field, checkbox...'),
+ 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
+ 'name' => new external_value(PARAM_RAW, 'The name of the custom field'),
+ 'shortname' => new external_value(PARAM_RAW, 'The shortname of the custom field - to be able to build the field class in the code'),
+ )
+ ), 'User custom fields (also known as user profil fields)', VALUE_OPTIONAL),
+ 'groups' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'group id'),
+ 'name' => new external_value(PARAM_RAW, 'group name'),
+ 'description' => new external_value(PARAM_RAW, 'group description'),
+ )
+ ), 'user groups', VALUE_OPTIONAL),
+ 'roles' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'roleid' => new external_value(PARAM_INT, 'role id'),
+ 'name' => new external_value(PARAM_RAW, 'role name'),
+ 'shortname' => new external_value(PARAM_ALPHANUMEXT, 'role shortname'),
+ 'sortorder' => new external_value(PARAM_INT, 'role sortorder')
+ )
+ ), 'user roles', VALUE_OPTIONAL),
+ 'preferences' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_ALPHANUMEXT, 'The name of the preferences'),
+ 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
+ )
+ ), 'User preferences', VALUE_OPTIONAL),
+ 'enrolledcourses' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'Id of the course'),
+ 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
+ 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
+ )
+ ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
+ )
+ )
+ );
+ }
- if ($user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
- $url = $user->url;
- if (strpos($user->url, '://') === false) {
- $url = 'http://'. $url;
- }
- $user->url = clean_param($user->url, PARAM_URL);
- $userarray['url'] = $user->url;
- }
+ /**
+ * Returns description of method parameters
+ * @return external_function_parameters
+ */
+ public static function get_users_by_courseid_parameters() {
+ return new external_function_parameters(
+ array(
+ 'courseid' => new external_value(PARAM_INT, 'course id'),
+ 'options' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_ALPHANUMEXT, 'option name'),
+ 'value' => new external_value(PARAM_RAW, 'option value')
+ )
+ ), 'method options'),
+ )
+ );
+ }
- if ($user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
- $userarray['icq'] = $user->icq;
- }
+ /**
+ * Get course participants details
+ * @param int $courseid course id
+ * @param array $options options {
+ * 'name' => option name
+ * 'value' => option value
+ * }
+ * @return array An array of users
+ */
+ public static function get_users_by_courseid($courseid, $options) {
+ global $CFG, $USER, $DB;
+ require_once($CFG->dirroot . "/user/lib.php");
- if ($user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
- $userarray['skype'] = $user->skype;
- }
- if ($user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
- $userarray['yahoo'] = $user->yahoo;
- }
- if ($user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
- $userarray['aim'] = $user->aim;
- }
- if ($user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
- $userarray['msn'] = $user->msn;
+ $params = self::validate_parameters(
+ self::get_users_by_courseid_parameters(),
+ array(
+ 'courseid'=>$courseid,
+ 'options'=>$options
+ )
+ );
+ $withcapability = '';
+ $groupid = 0;
+ $onlyactive = false;
+ foreach ($options as $option) {
+ switch ($option['name']) {
+ case 'withcapability':
+ $withcapability = $option['value'];
+ break;
+ case 'groupid':
+ $groupid = (int)$option['value'];
+ break;
+ case 'onlyactive':
+ $onlyactive = !empty($option['value']);
+ break;
}
+ }
- if ((!isset($hiddenfields['firstaccess'])) or $isadmin) {
- if ($user->firstaccess) {
- $userarray['firstaccess'] = $user->firstaccess;
- } else {
- $userarray['firstaccess'] = 0;
- }
- }
- if ((!isset($hiddenfields['lastaccess'])) or $isadmin) {
- if ($user->lastaccess) {
- $userarray['lastaccess'] = $user->lastaccess;
- } else {
- $userarray['lastaccess'] = 0;
- }
- }
- /// Printing tagged interests
- if (!empty($CFG->usetags)) {
- require_once($CFG->dirroot . '/tag/lib.php');
- if ($interests = tag_get_tags_csv('user', $user->id, TAG_RETURN_TEXT) ) {
- $userarray['interests'] = $interests;
- }
- }
+ // to overwrite this parameter, you need role:review capability
+ if ($withcapability) {
+ require_capability('moodle/role:review', $coursecontext);
+ }
+ // need accessallgroups capability if you want to overwrite this option
+ if (!empty($groupid) && groups_is_member($groupid)) {
+ require_capability('moodle/site:accessallgroups', $context);
+ }
+ // to overwrite this option, you need course:enrolereview permission
+ if ($onlyactive) {
+ require_capability('moodle/course:enrolreview', $coursecontext);
+ }
- //Departement/Institution are not displayed on any profile, however you can get them from editing profile.
- if ($isadmin or $currentuser) {
- if ($user->institution) {
- $userarray['institution'] = $user->institution;
- }
- if (isset($user->department)) { //isset because it's ok to have department 0
- $userarray['department'] = $user->department;
- }
- }
+ list($coursectxselect, $coursectxjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
+ $coursesql = "SELECT c.* $coursectxselect
+ FROM {course} c $coursectxjoin
+ WHERE c.id = $courseid";
+ $course = $DB->get_record_sql($coursesql);
+ context_instance_preload($course);
+ $coursecontext = get_context_instance(CONTEXT_COURSE, $params['courseid']);
+ if ($courseid == SITEID) {
+ $context = get_system_context();
+ } else {
+ $context = $coursecontext;
+ }
+ try {
+ self::validate_context($context);
+ } catch (Exception $e) {
+ $exceptionparam = new stdClass();
+ $exceptionparam->message = $e->getMessage();
+ $exceptionparam->courseid = $params['courseid'];
+ throw new moodle_exception(get_string('errorcoursecontextnotvalid' , 'webservice', $exceptionparam));
+ }
- // not a big secret
- $userarray['roles'] = array();
- $roles = get_user_roles($context, $user->id, false);
- foreach ($roles as $role) {
- $userarray['roles'][] = array(
- 'roleid' => $role->roleid,
- 'name' => $role->name,
- 'shortname' => $role->shortname,
- 'sortorder' => $role->sortorder
- );
+ list($enrolledsql, $enrolledparams) = get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
+ list($ctxselect, $ctxjoin) = context_instance_preload_sql('u.id', CONTEXT_USER, 'ctx');
+ $records = $DB->get_records_sql($enrolledsql, $enrolledparams);
+ $sqlparams['courseid'] = $courseid;
+ $sql = "SELECT u.* $ctxselect
+ FROM {user} u $ctxjoin
+ WHERE u.id IN ($enrolledsql)
+ ORDER BY u.id ASC";
+ $enrolledusers = $DB->get_recordset_sql($sql, $enrolledparams);
+ $users = array();
+ foreach ($enrolledusers as $user) {
+ if (!empty($user->deleted)) {
+ continue;
}
-
- // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group
- if (has_capability('moodle/site:accessallgroups', $context)) {
- $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name,g.description');
- foreach ($usergroups as $group) {
- $group->description = file_rewrite_pluginfile_urls($group->description, 'pluginfile.php', $context->id, 'group', 'description', $group->id);
- $userarray['groups'][] = array('id'=>$group->id, 'name'=>$group->name, 'description'=>$group->description);
- }
+ context_instance_preload($user);
+ if ($userdetails = user_get_user_details($user, $course)) {
+ $users[] = $userdetails;
}
- $result[] = $userarray;
}
+ $enrolledusers->close();
- $users->close();
-
- return $result;
+ return $users;
}
-
/**
* Returns description of method result value
* @return external_description
*/
- public static function get_course_participants_by_id_returns() {
+ public static function get_users_by_courseid_returns() {
return new external_multiple_structure(
new external_single_structure(
array(
'sortorder' => new external_value(PARAM_INT, 'role sortorder')
)
), 'user roles', VALUE_OPTIONAL),
+ 'preferences' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'name' => new external_value(PARAM_ALPHANUMEXT, 'The name of the preferences'),
+ 'value' => new external_value(PARAM_RAW, 'The value of the custom field'),
+ )
+ ), 'User preferences', VALUE_OPTIONAL),
+ 'enrolledcourses' => new external_multiple_structure(
+ new external_single_structure(
+ array(
+ 'id' => new external_value(PARAM_INT, 'Id of the course'),
+ 'fullname' => new external_value(PARAM_RAW, 'Fullname of the course'),
+ 'shortname' => new external_value(PARAM_RAW, 'Shortname of the course')
+ )
+ ), 'Courses where the user is enrolled - limited by which courses the user is able to see', VALUE_OPTIONAL)
)
)
);
return $DB->get_records_list('user', 'id', $userids);
}
+
+/**
+ *
+ * Give user record from mdl_user, build an array conntains
+ * all user details
+ * @param stdClass $user user record from mdl_user
+ * @param stdClass $context context object
+ * @param stdClass $course moodle course
+ * @return array
+ */
+function user_get_user_details($user, $course = null) {
+ global $USER, $DB, $CFG;
+ require_once($CFG->dirroot . "/user/profile/lib.php"); //custom field library
+ require_once($CFG->dirroot . "/lib/filelib.php"); // file handling on description and friends
+
+ if (!empty($course)) {
+ $context = get_context_instance(CONTEXT_COURSE, $course->id);
+ $usercontext = get_context_instance(CONTEXT_USER, $user->id);
+ $canviewdetailscap = has_capability('moodle/user:viewdetails', $usercontext);
+ } else {
+ $context = get_context_instance(CONTEXT_USER, $user->id);
+ $usercontext = $context;
+ }
+
+ $currentuser = ($user->id == $USER->id);
+ $isadmin = is_siteadmin($USER);
+
+ if (!empty($course)) {
+ $canviewhiddenuserfields = has_capability('moodle/course:viewhiddenuserfields', $context);
+ } else {
+ $canviewhiddenuserfields = has_capability('moodle/user:viewhiddendetails', $context);
+ }
+ $canviewdetailscap = ($canviewdetailscap || has_capability('moodle/user:viewdetails', $context));
+ $canviewfullnames = has_capability('moodle/site:viewfullnames', $context);
+ if (!empty($course)) {
+ $canviewuseremail = has_capability('moodle/course:useremail', $context);
+ } else {
+ $canviewuseremail = false;
+ }
+ $cannotviewdescription = !empty($CFG->profilesforenrolledusersonly) && !$currentuser && !$DB->record_exists('role_assignments', array('userid'=>$user->id));
+ if (!empty($course)) {
+ $canaccessallgroups = has_capability('moodle/site:accessallgroups', $context);
+ } else {
+ $canaccessallgroups = false;
+ }
+
+ if (!$currentuser && !$canviewdetailscap && !has_coursecontact_role($user->id)) {
+ // skip this user details
+ return null;
+ }
+
+ $userdetails = array();
+ $userdetails['id'] = $user->id;
+
+ if ($isadmin or $currentuser) {
+ $userdetails['username'] = $user->username;
+ }
+ if ($isadmin or $canviewfullnames) {
+ $userdetails['firstname'] = $user->firstname;
+ $userdetails['lastname'] = $user->lastname;
+ }
+ $userdetails['fullname'] = fullname($user);
+
+ $fields = $DB->get_recordset_sql("SELECT f.*
+ FROM {user_info_field} f
+ JOIN {user_info_category} c
+ ON f.categoryid=c.id
+ ORDER BY c.sortorder ASC, f.sortorder ASC");
+ $userdetails['customfields'] = array();
+ foreach ($fields as $field) {
+ require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
+ $newfield = 'profile_field_'.$field->datatype;
+ $formfield = new $newfield($field->id, $user->id);
+ if ($formfield->is_visible() and !$formfield->is_empty()) {
+ $userdetails['customfields'][] =
+ array('name' => $formfield->field->name, 'value' => $formfield->data,
+ 'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
+ }
+ }
+ $fields->close();
+ // unset customfields if it's empty
+ if (empty($userdetails['customfields'])) {
+ unset($userdetails['customfields']);
+ }
+
+ // profile image
+ $profileimageurl = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f1');
+ $userdetails['profileimageurl'] = $profileimageurl->out(false);
+ $profileimageurlsmall = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f2');
+ $userdetails['profileimageurlsmall'] = $profileimageurlsmall->out(false);
+
+ //hidden user field
+ if ($canviewhiddenuserfields) {
+ $hiddenfields = array();
+ // address, phone1 and phone2 not appears in hidden fields list
+ // but require viewhiddenfields capability
+ // according to user/profile.php
+ if ($user->address) {
+ $userdetails['address'] = $user->address;
+ }
+ if ($user->phone1) {
+ $userdetails['phone1'] = $user->phone1;
+ }
+ if ($user->phone2) {
+ $userdetails['phone2'] = $user->phone2;
+ }
+ } else {
+ $hiddenfields = array_flip(explode(',', $CFG->hiddenuserfields));
+ }
+
+ if (isset($user->description) && (!isset($hiddenfields['description']) or $isadmin)) {
+ if (!$cannotviewdescription) {
+ $user->description = file_rewrite_pluginfile_urls($user->description, 'pluginfile.php', $usercontext->id, 'user', 'profile', null);
+ $userdetails['description'] = $user->description;
+ $userdetails['descriptionformat'] = $user->descriptionformat;
+ }
+ }
+
+ if ((!isset($hiddenfields['country']) or $isadmin) && $user->country) {
+ $userdetails['country'] = $user->country;
+ }
+
+ if ((!isset($hiddenfields['city']) or $isadmin) && $user->city) {
+ $userdetails['city'] = $user->city;
+ }
+
+ if ($user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
+ $url = $user->url;
+ if (strpos($user->url, '://') === false) {
+ $url = 'http://'. $url;
+ }
+ $user->url = clean_param($user->url, PARAM_URL);
+ $userdetails['url'] = $user->url;
+ }
+
+ if ($user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
+ $userdetails['icq'] = $user->icq;
+ }
+
+ if ($user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
+ $userdetails['skype'] = $user->skype;
+ }
+ if ($user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
+ $userdetails['yahoo'] = $user->yahoo;
+ }
+ if ($user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
+ $userdetails['aim'] = $user->aim;
+ }
+ if ($user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
+ $userdetails['msn'] = $user->msn;
+ }
+
+ if (!isset($hiddenfields['firstaccess']) or $isadmin) {
+ if ($user->firstaccess) {
+ $userdetails['firstaccess'] = $user->firstaccess;
+ } else {
+ $userdetails['firstaccess'] = 0;
+ }
+ }
+ if (!isset($hiddenfields['lastaccess']) or $isadmin) {
+ if ($user->lastaccess) {
+ $userdetails['lastaccess'] = $user->lastaccess;
+ } else {
+ $userdetails['lastaccess'] = 0;
+ }
+ }
+
+ if ($currentuser
+ or $canviewuseremail // this is a capability in course context, it will be false in usercontext
+ or $user->maildisplay == 1
+ or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER))) {
+ $userdetails['email'] = $user->email;;
+ }
+
+ if (!empty($CFG->usetags)) {
+ require_once($CFG->dirroot . '/tag/lib.php');
+ if ($interests = tag_get_tags_csv('user', $user->id, TAG_RETURN_TEXT) ) {
+ $userdetails['interests'] = $interests;
+ }
+ }
+
+ //Departement/Institution are not displayed on any profile, however you can get them from editing profile.
+ if ($isadmin or $currentuser) {
+ if ($user->institution) {
+ $userdetails['institution'] = $user->institution;
+ }
+ if (isset($user->department)) { //isset because it's ok to have department 0
+ $userdetails['department'] = $user->department;
+ }
+ }
+
+ if (!empty($course)) {
+ // not a big secret
+ $roles = get_user_roles($context, $user->id, false);
+ $userdetails['roles'] = array();
+ foreach ($roles as $role) {
+ $userdetails['roles'][] = array(
+ 'roleid' => $role->roleid,
+ 'name' => $role->name,
+ 'shortname' => $role->shortname,
+ 'sortorder' => $role->sortorder
+ );
+ }
+ }
+
+ // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group
+ if (!empty($course) && $canaccessallgroups) {
+ $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name,g.description');
+ $userdetails['groups'] = array();
+ foreach ($usergroups as $group) {
+ $group->description = file_rewrite_pluginfile_urls($group->description, 'pluginfile.php', $context->id, 'group', 'description', $group->id);
+ $userdetails['groups'][] = array('id'=>$group->id, 'name'=>$group->name, 'description'=>$group->description);
+ }
+ }
+ //list of courses where the user is enrolled
+ if (!isset($hiddenfields['mycourses'])) {
+ $enrolledcourses = array();
+ if ($mycourses = enrol_get_users_courses($user->id, true)) {
+ foreach ($mycourses as $mycourse) {
+ if ($mycourse->category) {
+ $enrolledcourse = array();
+ $enrolledcourse['id'] = $mycourse->id;
+ $enrolledcourse['fullname'] = $mycourse->fullname;
+ $enrolledcourse['shortname'] = $mycourse->shortname;
+ $enrolledcourses[] = $enrolledcourse;
+ }
+ }
+ $userdetails['enrolledcourses'] = $enrolledcourses;
+ }
+ }
+
+ //user preferences
+ if ($currentuser) {
+ $preferences = array();
+ $userpreferences = get_user_preferences();
+ foreach($userpreferences as $prefname => $prefvalue) {
+ $preferences[] = array('name' => $prefname, 'value' => $prefvalue);
+ }
+ $userdetails['preferences'] = $preferences;
+ }
+ return $userdetails;
+}
+
/**
* Return a list of page types
* @param string $pagetype current page type
* @param stdClass $parentcontext Block's parent context
* @param stdClass $currentcontext Current context of block
*/
-function user_pagetypelist($pagetype, $parentcontext, $currentcontext) {
+function user_page_type_list($pagetype, $parentcontext, $currentcontext) {
return array(
'user-profile'=>get_string('page-user-profile', 'pagetype'),
'my-index'=>get_string('page-my-index', 'pagetype')
-$version = 2011062700.00; // YYYYMMDD = weekly release date of this DEV branch
+$version = 2011062700.04; // YYYYMMDD = weekly release date of this DEV branch
// RR = release increments - 00 in DEV branches
// .XX = incremental changes