$temp->add(new admin_setting_configtext('navcourselimit',get_string('navcourselimit','admin'),get_string('confignavcourselimit', 'admin'),20,PARAM_INT));
$temp->add(new admin_setting_configcheckbox('navlinkcoursesections', get_string('navlinkcoursesections', 'admin'), get_string('navlinkcoursesections_help', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('usesitenameforsitepages', get_string('usesitenameforsitepages', 'admin'), get_string('configusesitenameforsitepages', 'admin'), 0));
+ $temp->add(new admin_setting_configcheckbox('navadduserpostslinks', get_string('navadduserpostslinks', 'admin'), get_string('navadduserpostslinks_help', 'admin'), 1));
$ADMIN->add('appearance', $temp);
$temp->add(new admin_setting_configcheckbox('xmlstrictheaders', get_string('xmlstrictheaders', 'admin'), get_string('configxmlstrictheaders', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('debugsmtp', get_string('debugsmtp', 'admin'), get_string('configdebugsmtp', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('perfdebug', get_string('perfdebug', 'admin'), get_string('configperfdebug', 'admin'), '7', '15', '7'));
- $temp->add(new admin_setting_configcheckbox('debugstringids', get_string('debugstringids', 'admin'), get_string('configdebugstringids', 'admin'), 0));
+ $temp->add(new admin_setting_configcheckbox('debugstringids', get_string('debugstringids', 'admin'), get_string('debugstringids_desc', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('debugvalidators', get_string('debugvalidators', 'admin'), get_string('configdebugvalidators', 'admin'), 0));
$temp->add(new admin_setting_configcheckbox('debugpageinfo', get_string('debugpageinfo', 'admin'), get_string('configdebugpageinfo', 'admin'), 0));
$ADMIN->add('development', $temp);
} else if ($data = $mform->get_data()) {
$newcategory = new stdClass();
$newcategory->name = $data->name;
+ $newcategory->idnumber = $data->idnumber;
$newcategory->description_editor = $data->description_editor;
$newcategory->parent = $data->parent; // if $data->parent = 0, the new category will be a top-level category
$mform->addElement('select', 'parent', get_string('parentcategory'), $options);
$mform->addElement('text', 'name', get_string('categoryname'), array('size'=>'30'));
$mform->addRule('name', get_string('required'), 'required', null);
+ $mform->addElement('text', 'idnumber', get_string('idnumbercoursecategory'),'maxlength="100" size="10"');
+ $mform->addHelpButton('idnumber', 'idnumbercoursecategory');
$mform->addElement('editor', 'description_editor', get_string('description'), null, $editoroptions);
$mform->setType('description_editor', PARAM_RAW);
if (!empty($CFG->allowcategorythemes)) {
$this->add_action_buttons(true, $strsubmit);
}
+
+ function validation($data, $files) {
+ global $DB;
+ $errors = parent::validation($data, $files);
+ if (!empty($data['idnumber'])) {
+ if ($existing = $DB->get_record('course_categories', array('idnumber' => $data['idnumber']))) {
+ if (!$data['id'] || $existing->id != $data['id']) {
+ $errors['idnumber']= get_string('idnumbertaken');
+ }
+ }
+ }
+
+ return $errors;
+ }
}
case "complete" :
get_all_mods($course->id, $mods, $modnames, $modnamesplural, $modnamesused);
$sections = get_all_sections($course->id);
+ $itemsprinted = false;
for ($i=0; $i<=$course->numsections; $i++) {
$showsection = (has_capability('moodle/course:viewhiddensections', $coursecontext) or $section->visible or !$course->hiddensections);
if ($showsection) { // prevent hidden sections in user activity. Thanks to Geoff Wilbert!
-
+ // Check the section has a sequence. This is the sequence of modules/resources.
+ // If there is no sequence there is nothing to display.
if ($section->sequence) {
+ $itemsprinted = true;
echo '<div class="section">';
echo '<h2>';
echo get_section_name($course, $section);
}
}
}
+
+ if (!$itemsprinted) {
+ echo $OUTPUT->notification(get_string('nothingtodisplay'));
+ }
+
break;
case "coursecompletion":
case "coursecompletions":
break;
case 'defaultcohortroleloaded':
defaultrole = this.get(DEFAULTCOHORTROLE);
- panel.get('contentBox').one('.'+CSS.PANELROLES).all('option').each(function(){
- if (this.get('value')==defaultrole) {
- this.setAttribute('selected', true);
- }
- });
+ panel.get('contentBox').one('.'+CSS.PANELROLES+' select').set('value', defaultrole);
break;
}
},
}
}
-}, '@VERSION@', {requires:['base','node', 'overlay', 'io-base', 'test', 'json-parse', 'event-delegate', 'dd-plugin', 'event-key', 'moodle-enrol-notification']});
\ No newline at end of file
+}, '@VERSION@', {requires:['base','node', 'overlay', 'io-base', 'test', 'json-parse', 'event-delegate', 'dd-plugin', 'event-key', 'moodle-enrol-notification']});
user_accesstime_log();
}
+ $hassiteconfig = has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
+
$PAGE->set_url('/');
$PAGE->set_course($SITE);
/// If the site is currently under maintenance, then print a message
- if (!empty($CFG->maintenance_enabled) and !has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
+ if (!empty($CFG->maintenance_enabled) and !$hassiteconfig) {
print_maintenance_message();
}
- if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
- if (moodle_needs_upgrading()) {
- redirect($CFG->wwwroot .'/'. $CFG->admin .'/index.php');
- }
- } else if (get_home_page() != HOMEPAGE_SITE) {
+ if ($hassiteconfig && moodle_needs_upgrading()) {
+ redirect($CFG->wwwroot .'/'. $CFG->admin .'/index.php');
+ }
+
+ if (get_home_page() != HOMEPAGE_SITE) {
// Redirect logged-in users to My Moodle overview if required
if (optional_param('setdefaulthome', false, PARAM_BOOL)) {
set_user_preference('user_home_page_preference', HOMEPAGE_SITE);
- } else if ($CFG->defaulthomepage == HOMEPAGE_MY && optional_param('redirect', true, PARAM_BOOL)) {
+ } else if ($CFG->defaulthomepage == HOMEPAGE_MY && (optional_param('redirect', true, PARAM_BOOL) || !$hassiteconfig)) {
redirect($CFG->wwwroot .'/my/');
} else if (!empty($CFG->defaulthomepage) && $CFG->defaulthomepage == HOMEPAGE_USER) {
$PAGE->settingsnav->get('usercurrentsettings')->add(get_string('makethismyhome'), new moodle_url('/', array('setdefaulthome'=>true)), navigation_node::TYPE_SETTING);
break;
case FRONTPAGECOURSELIST:
- if (isloggedin() and !has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)) and !isguestuser() and empty($CFG->disablemycourses)) {
+ if (isloggedin() and !$hassiteconfig and !isguestuser() and empty($CFG->disablemycourses)) {
echo html_writer::tag('a', get_string('skipa', 'access', moodle_strtolower(get_string('mycourses'))), array('href'=>'#skipmycourses', 'class'=>'skip-block'));
echo $OUTPUT->heading(get_string('mycourses'), 2, 'headingblock header');
print_my_moodle();
echo html_writer::tag('span', '', array('class'=>'skip-block-to', 'id'=>'skipmycourses'));
- } else if ((!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)) and !isguestuser()) or ($DB->count_records('course') <= FRONTPAGECOURSELIMIT)) {
+ } else if ((!$hassiteconfig and !isguestuser()) or ($DB->count_records('course') <= FRONTPAGECOURSELIMIT)) {
// admin should not see list of courses when there are too many of them
echo html_writer::tag('a', get_string('skipa', 'access', moodle_strtolower(get_string('availablecourses'))), array('href'=>'#skipavailablecourses', 'class'=>'skip-block'));
echo $OUTPUT->heading(get_string('availablecourses'), 2, 'headingblock header');
$string['configdebugdisplay'] = 'Set to on, the error reporting will go to the HTML page. This is practical, but breaks XHTML, JS, cookies and HTTP headers in general. Set to off, it will send the output to your server logs, allowing better debugging. The PHP setting error_log controls which log this goes to.';
$string['configdebugpageinfo'] = 'Enable if you want page information printed in page footer.';
$string['configdebugsmtp'] = 'Enable verbose debug information during sending of email messages to SMTP server.';
-$string['configdebugstringids'] = 'This option is designed to help translators. It shows the language file and string id beside each string that is output. (Changing this setting will only take effect on the next page load.)';
$string['configdebugvalidators'] = 'Enable if you want to have links to external validator servers in page footer. You may need to create new user with username <em>w3cvalidator</em>, and enable guest access. These changes may allow unauthorized access to server, do not enable on production sites!';
$string['configdefaultallowedmodules'] = 'For the courses which fall into the above category, which modules do you want to allow by default <b>when the course is created</b>?';
$string['configdefaulthomepage'] = 'This determines the home page for logged in users';
$string['debugpageinfo'] = 'Show page information';
$string['debugsmtp'] = 'Debug email sending';
$string['debugstringids'] = 'Show origin of languages strings';
+$string['debugstringids_desc'] = 'This option is designed to help translators. When this option is enabled, if you add the parameter strings=1 to a request URL, it will show the language file and string id beside each string that is output.';
$string['debugvalidators'] = 'Show validator links';
$string['defaultallowedmodules'] = 'Default allowed modules';
$string['defaultcity'] = 'Default city';
$string['mypagelocked'] = 'Lock default page';
$string['mysql416bypassed'] = 'However, if your site is using iso-8859-1 (latin) languages ONLY, you may continue using your currently installed MySQL 4.1.12 (or higher).';
$string['mysql416required'] = 'MySQL 4.1.16 is the minimum version required for Moodle 1.6 in order to guarantee that all data can be converted to UTF-8 in the future.';
+$string['navadduserpostslinks'] = 'Add links to view user posts';
+$string['navadduserpostslinks_help'] = 'If enabled two links will be added to each user in the navigation to view discussions the user has started and posts the user has made in forums throughout the site or in specific courses.';
$string['navigationupgrade'] = 'This upgrade introduces two new navigation blocks that will replace these blocks: Administration, Courses, Activities and Participants. If you had set any special permissions on those blocks you should check to make sure everything is behaving as you want it.';
$string['navcourselimit'] = 'Course limit';
$string['navlinkcoursesections'] = 'Link course sections';
$string['categoryduplicate'] = 'A category named \'{$a}\' already exists!';
$string['categorymodifiedcancel'] = 'Category was modified! Please cancel and try again.';
$string['categoryname'] = 'Category name';
+$string['idnumbercoursecategory'] = 'Category ID number';
+$string['idnumbercoursecategory_help'] = 'The ID number of a course category is only used when matching the category against external systems and is not displayed anywhere on the site. If the category has an official code name it may be entered, otherwise the field can be left blank.';
$string['categoryupdated'] = 'The category \'{$a}\' was updated';
$string['city'] = 'City/town';
$string['clambroken'] = 'Your administrator has enabled virus checking for file uploads but has misconfigured something.<br />Your file upload was NOT successful. Your administrator has been emailed to notify them so they can fix it.<br />Maybe try uploading this file later.';
//TODO: in order to let the administrator delete obsolete token, split this request in multiple request or use LEFT JOIN
//here retrieve token list (including linked users firstname/lastname and linked services name)
- $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.validuntil, s.id AS serviceid
+ $sql = "SELECT t.id, t.token, u.id AS userid, u.firstname, u.lastname, s.name, t.iprestriction, t.validuntil, s.id AS serviceid
FROM {external_tokens} t, {user} u, {external_services} s
WHERE t.creatorid=? AND t.tokentype = ? AND s.id = t.externalserviceid AND t.userid = u.id";
$tokens = $DB->get_records_sql($sql, array($USER->id, EXTERNAL_TOKEN_PERMANENT));
global $CFG;
$this->set_queue($langcode);
- $this->version = '2.1';
+ $this->version = '2.2';
if (!empty($CFG->langotherroot) and $CFG->langotherroot !== $CFG->dataroot . '/lang') {
debugging('The in-built language pack installer does not support alternative location ' .
<?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20110926" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20111007" COMMENT="XMLDB file for core Moodle tables"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
>
<TABLE NAME="course_categories" COMMENT="Course categories" PREVIOUS="course" NEXT="course_completion_aggr_methd">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="name"/>
- <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="description"/>
- <FIELD NAME="description" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="name" NEXT="descriptionformat"/>
+ <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="idnumber"/>
+ <FIELD NAME="idnumber" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false" PREVIOUS="name" NEXT="description"/>
+ <FIELD NAME="description" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="idnumber" NEXT="descriptionformat"/>
<FIELD NAME="descriptionformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="description" NEXT="parent"/>
<FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="descriptionformat" NEXT="sortorder"/>
<FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="parent" NEXT="coursecount"/>
upgrade_main_savepoint(true, 2011092800.03);
}
+ if ($oldversion < 2011100700.02) {
+ // Define field idnumber to be added to course_categories
+ $table = new xmldb_table('course_categories');
+ $field = new xmldb_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null, 'name');
+
+ // Conditionally launch add field idnumber
+ if (!$dbman->field_exists($table,$field)) {
+ $dbman->add_field($table, $field);
+ }
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2011100700.02);
+ }
+
return true;
}
unset($params[$key]);
}
if (!empty($params)) {
- //list all unexpected keys
- $keys = '';
- foreach($params as $key => $value) {
- $keys .= $key . ',';
- }
- throw new invalid_parameter_exception('Unexpected keys (' . $keys . ') detected in parameter array.');
+ throw new invalid_parameter_exception('Unexpected keys (' . implode(', ', array_keys($params)) . ') detected in parameter array.');
}
return $result;
}
}
+ if ($key === 'timecreated' or $key === 'timemodified') {
+ if (!is_number($value)) {
+ throw new file_exception('storedfileproblem', 'Invalid file '.$key);
+ }
+ if ($value < 0) {
+ //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak)
+ $value = 0;
+ }
+ }
+
$newrecord->$key = $value;
}
try {
$newrecord->id = $DB->insert_record('files', $newrecord);
} catch (dml_exception $e) {
- $newrecord->id = false;
- }
-
- if (!$newrecord->id) {
throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid,
- $newrecord->filepath, $newrecord->filename);
+ $newrecord->filepath, $newrecord->filename, $e->debuginfo);
}
$this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid);
}
$now = time();
+ if (isset($file_record->timecreated)) {
+ if (!is_number($file_record->timecreated)) {
+ throw new file_exception('storedfileproblem', 'Invalid file timecreated');
+ }
+ if ($file_record->timecreated < 0) {
+ //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak)
+ $file_record->timecreated = 0;
+ }
+ } else {
+ $file_record->timecreated = $now;
+ }
+
+ if (isset($file_record->timemodified)) {
+ if (!is_number($file_record->timemodified)) {
+ throw new file_exception('storedfileproblem', 'Invalid file timemodified');
+ }
+ if ($file_record->timemodified < 0) {
+ //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak)
+ $file_record->timemodified = 0;
+ }
+ } else {
+ $file_record->timemodified = $now;
+ }
$newrecord = new stdClass();
$newrecord->filepath = $file_record->filepath;
$newrecord->filename = $file_record->filename;
- $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated;
- $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified;
+ $newrecord->timecreated = $file_record->timecreated;
+ $newrecord->timemodified = $file_record->timemodified;
$newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype;
$newrecord->userid = empty($file_record->userid) ? null : $file_record->userid;
$newrecord->source = empty($file_record->source) ? null : $file_record->source;
try {
$newrecord->id = $DB->insert_record('files', $newrecord);
} catch (dml_exception $e) {
- $newrecord->id = false;
- }
-
- if (!$newrecord->id) {
if ($newfile) {
$this->deleted_file_cleanup($newrecord->contenthash);
}
throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid,
- $newrecord->filepath, $newrecord->filename);
+ $newrecord->filepath, $newrecord->filename, $e->debuginfo);
}
$this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid);
}
$now = time();
+ if (isset($file_record->timecreated)) {
+ if (!is_number($file_record->timecreated)) {
+ throw new file_exception('storedfileproblem', 'Invalid file timecreated');
+ }
+ if ($file_record->timecreated < 0) {
+ //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak)
+ $file_record->timecreated = 0;
+ }
+ } else {
+ $file_record->timecreated = $now;
+ }
+
+ if (isset($file_record->timemodified)) {
+ if (!is_number($file_record->timemodified)) {
+ throw new file_exception('storedfileproblem', 'Invalid file timemodified');
+ }
+ if ($file_record->timemodified < 0) {
+ //NOTE: unfortunately I make a mistake when creating the "files" table, we can not have negative numbers there, on the other hand no file should be older than 1970, right? (skodak)
+ $file_record->timemodified = 0;
+ }
+ } else {
+ $file_record->timemodified = $now;
+ }
$newrecord = new stdClass();
$newrecord->filepath = $file_record->filepath;
$newrecord->filename = $file_record->filename;
- $newrecord->timecreated = empty($file_record->timecreated) ? $now : $file_record->timecreated;
- $newrecord->timemodified = empty($file_record->timemodified) ? $now : $file_record->timemodified;
+ $newrecord->timecreated = $file_record->timecreated;
+ $newrecord->timemodified = $file_record->timemodified;
$newrecord->mimetype = empty($file_record->mimetype) ? mimeinfo('type', $file_record->filename) : $file_record->mimetype;
$newrecord->userid = empty($file_record->userid) ? null : $file_record->userid;
$newrecord->source = empty($file_record->source) ? null : $file_record->source;
try {
$newrecord->id = $DB->insert_record('files', $newrecord);
} catch (dml_exception $e) {
- $newrecord->id = false;
- }
-
- if (!$newrecord->id) {
if ($newfile) {
$this->deleted_file_cleanup($newrecord->contenthash);
}
throw new stored_file_creation_exception($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid,
- $newrecord->filepath, $newrecord->filename);
+ $newrecord->filepath, $newrecord->filename, $e->debuginfo);
}
$this->create_directory($newrecord->contextid, $newrecord->component, $newrecord->filearea, $newrecord->itemid, $newrecord->filepath, $newrecord->userid);
*/
class google_docs {
// need both docs and the spreadsheets realm
- const REALM = 'http://docs.google.com/feeds/ http://spreadsheets.google.com/feeds/ http://docs.googleusercontent.com/';
- const DOCUMENTFEED_URL = 'http://docs.google.com/feeds/default/private/full';
+ const REALM = 'https://docs.google.com/feeds/ https://spreadsheets.google.com/feeds/ https://docs.googleusercontent.com/';
+ const DOCUMENTFEED_URL = 'https://docs.google.com/feeds/default/private/full';
const USER_PREF_NAME = 'google_authsub_sesskey';
private $google_curl = null;
$xml = new SimpleXMLElement($content);
-
-
$files = array();
foreach($xml->entry as $gdoc){
$docid = (string) $gdoc->children('http://schemas.google.com/g/2005')->resourceId;
switch($type){
case 'document':
$title = $gdoc->title.'.rtf';
- $source = 'http://docs.google.com/feeds/download/documents/Export?id='.$docid.'&exportFormat=rtf';
+ $source = 'https://docs.google.com/feeds/download/documents/Export?id='.$docid.'&exportFormat=rtf';
break;
case 'presentation':
$title = $gdoc->title.'.ppt';
- $source = 'http://docs.google.com/feeds/download/presentations/Export?id='.$docid.'&exportFormat=ppt';
+ $source = 'https://docs.google.com/feeds/download/presentations/Export?id='.$docid.'&exportFormat=ppt';
break;
case 'spreadsheet':
$title = $gdoc->title.'.xls';
- $source = 'http://spreadsheets.google.com/feeds/download/spreadsheets/Export?key='.$docid.'&exportFormat=xls';
+ $source = 'https://spreadsheets.google.com/feeds/download/spreadsheets/Export?key='.$docid.'&exportFormat=xls';
break;
case 'pdf':
$title = (string)$gdoc->title;
class google_picasa {
const REALM = 'http://picasaweb.google.com/data/';
const USER_PREF_NAME = 'google_authsub_sesskey_picasa';
- const UPLOAD_LOCATION = 'http://picasaweb.google.com/data/feed/api/user/default/albumid/default';
- const ALBUM_PHOTO_LIST = 'http://picasaweb.google.com/data/feed/api/user/default/albumid/';
- const PHOTO_SEARCH_URL = 'http://picasaweb.google.com/data/feed/api/user/default?kind=photo&q=';
- const LIST_ALBUMS_URL = 'http://picasaweb.google.com/data/feed/api/user/default';
+ const UPLOAD_LOCATION = 'https://picasaweb.google.com/data/feed/api/user/default/albumid/default';
+ const ALBUM_PHOTO_LIST = 'https://picasaweb.google.com/data/feed/api/user/default/albumid/';
+ const PHOTO_SEARCH_URL = 'https://picasaweb.google.com/data/feed/api/user/default?kind=photo&q=';
+ const LIST_ALBUMS_URL = 'https://picasaweb.google.com/data/feed/api/user/default';
private $google_curl = null;
* @return string The localized string.
*/
function get_string($identifier, $component = '', $a = NULL) {
+ global $CFG;
$identifier = clean_param($identifier, PARAM_STRINGID);
if (empty($identifier)) {
}
}
- return get_string_manager()->get_string($identifier, $component, $a);
+ $result = get_string_manager()->get_string($identifier, $component, $a);
+
+ // Debugging feature lets you display string identifier and component
+ if ($CFG->debugstringids && optional_param('strings', 0, PARAM_INT)) {
+ $result .= ' {' . $identifier . '/' . $component . '}';
+ }
+ return $result;
}
/**
}
/**
- * Gets a list of all plugin API functions for given plugin type, function
- * name, and filename.
- * @param string $plugintype Plugin type, e.g. 'mod' or 'report'
- * @param string $function Name of function after the frankenstyle prefix;
- * e.g. if the function is called report_courselist_hook then this value
- * would be 'hook'
- * @param string $file Name of file that includes function within plugin,
- * default 'lib.php'
- * @return Array of plugin frankenstyle (e.g. 'report_courselist', 'mod_forum')
- * to valid, existing plugin function name (e.g. 'report_courselist_hook',
- * 'forum_hook')
+ * Get a list of all the plugins of a given type that contain a particular file.
+ * @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
+ * @param string $file the name of file that must be present in the plugin.
+ * (e.g. 'view.php', 'db/install.xml').
+ * @param bool $include if true (default false), the file will be include_once-ed if found.
+ * @return array with plugin name as keys (e.g. 'forum', 'courselist') and the path
+ * to the file relative to dirroot as value (e.g. "$CFG->dirroot/mod/forum/view.php").
*/
-function get_plugin_list_with_function($plugintype, $function, $file='lib.php') {
- global $CFG; // mandatory in case it is referenced by include()d PHP script
+function get_plugin_list_with_file($plugintype, $file, $include = false) {
+ global $CFG; // Necessary in case it is referenced by include()d PHP scripts.
- $result = array();
- // Loop through list of plugins with given type
- $list = get_plugin_list($plugintype);
- foreach($list as $plugin => $dir) {
+ $plugins = array();
+
+ foreach(get_plugin_list($plugintype) as $plugin => $dir) {
$path = $dir . '/' . $file;
- // If file exists, require it and look for function
if (file_exists($path)) {
- include_once($path);
- $fullfunction = $plugintype . '_' . $plugin . '_' . $function;
- if (function_exists($fullfunction)) {
- // Function exists with standard name. Store, indexed by
- // frankenstyle name of plugin
- $result[$plugintype . '_' . $plugin] = $fullfunction;
- } else if ($plugintype === 'mod') {
- // For modules, we also allow plugin without full frankenstyle
- // but just starting with the module name
- $shortfunction = $plugin . '_' . $function;
- if (function_exists($shortfunction)) {
- $result[$plugintype . '_' . $plugin] = $shortfunction;
- }
+ if ($include) {
+ include_once($path);
}
+ $plugins[$plugin] = $path;
}
}
- return $result;
+
+ return $plugins;
+}
+
+/**
+ * Get a list of all the plugins of a given type that define a certain API function
+ * in a certain file. The plugin component names and function names are returned.
+ *
+ * @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
+ * @param string $function the part of the name of the function after the
+ * frankenstyle prefix. e.g 'hook' if you are looking for functions with
+ * names like report_courselist_hook.
+ * @param string $file the name of file within the plugin that defines the
+ * function. Defaults to lib.php.
+ * @return array with frankenstyle plugin names as keys (e.g. 'report_courselist', 'mod_forum')
+ * and the function names as values (e.g. 'report_courselist_hook', 'forum_hook').
+ */
+function get_plugin_list_with_function($plugintype, $function, $file = 'lib.php') {
+ $pluginfunctions = array();
+ foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) {
+ $fullfunction = $plugintype . '_' . $plugin . '_' . $function;
+
+ if (function_exists($fullfunction)) {
+ // Function exists with standard name. Store, indexed by
+ // frankenstyle name of plugin
+ $pluginfunctions[$plugintype . '_' . $plugin] = $fullfunction;
+
+ } else if ($plugintype === 'mod') {
+ // For modules, we also allow plugin without full frankenstyle
+ // but just starting with the module name
+ $shortfunction = $plugin . '_' . $function;
+ if (function_exists($shortfunction)) {
+ $pluginfunctions[$plugintype . '_' . $plugin] = $shortfunction;
+ }
+ }
+ }
+ return $pluginfunctions;
+}
+
+/**
+ * Get a list of all the plugins of a given type that define a certain class
+ * in a certain file. The plugin component names and class names are returned.
+ *
+ * @param string $plugintype the type of plugin, e.g. 'mod' or 'report'.
+ * @param string $class the part of the name of the class after the
+ * frankenstyle prefix. e.g 'thing' if you are looking for classes with
+ * names like report_courselist_thing. If you are looking for classes with
+ * the same name as the plugin name (e.g. qtype_multichoice) then pass ''.
+ * @param string $file the name of file within the plugin that defines the class.
+ * @return array with frankenstyle plugin names as keys (e.g. 'report_courselist', 'mod_forum')
+ * and the class names as values (e.g. 'report_courselist_thing', 'qtype_multichoice').
+ */
+function get_plugin_list_with_class($plugintype, $class, $file) {
+ if ($class) {
+ $suffix = '_' . $class;
+ } else {
+ $suffix = '';
+ }
+
+ $pluginclasses = array();
+ foreach (get_plugin_list_with_file($plugintype, $file, true) as $plugin => $notused) {
+ $classname = $plugintype . '_' . $plugin . $suffix;
+ if (class_exists($classname)) {
+ $pluginclasses[$plugintype . '_' . $plugin] = $classname;
+ }
+ }
+
+ return $pluginclasses;
}
/**
$activity->hidden = (!$cm->visible);
$activity->modname = $cm->modname;
$activity->nodetype = navigation_node::NODETYPE_LEAF;
+ $activity->onclick = $cm->get_on_click();
$url = $cm->get_url();
if (!$url) {
$activity->url = null;
* @return array Array of activity nodes
*/
protected function load_section_activities(navigation_node $sectionnode, $sectionnumber, $activities) {
+ // A static counter for JS function naming
+ static $legacyonclickcounter = 0;
if ($activities instanceof course_modinfo) {
debugging('global_navigation::load_section_activities argument 3 should now recieve an array of activites. See that method for an example.', DEBUG_DEVELOPER);
} else {
$icon = new pix_icon('icon', get_string('modulename', $activity->modname), $activity->modname);
}
- $activitynode = $sectionnode->add(format_string($activity->name), $activity->url, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
+
+ // Prepare the default name and url for the node
+ $activityname = format_string($activity->name, true, array('context' => get_context_instance(CONTEXT_MODULE, $activity->id)));
+ $action = new moodle_url($activity->url);
+
+ // Check if the onclick property is set (puke!)
+ if (!empty($activity->onclick)) {
+ // Increment the counter so that we have a unique number.
+ $legacyonclickcounter++;
+ // Generate the function name we will use
+ $functionname = 'legacy_activity_onclick_handler_'.$legacyonclickcounter;
+ $propogrationhandler = '';
+ // Check if we need to cancel propogation. Remember inline onclick
+ // events would return false if they wanted to prevent propogation and the
+ // default action.
+ if (strpos($activity->onclick, 'return false')) {
+ $propogrationhandler = 'e.halt();';
+ }
+ // Decode the onclick - it has already been encoded for display (puke)
+ $onclick = htmlspecialchars_decode($activity->onclick);
+ // Build the JS function the click event will call
+ $jscode = "function {$functionname}(e) { $propogrationhandler $onclick }";
+ $this->page->requires->js_init_code($jscode);
+ // Override the default url with the new action link
+ $action = new action_link($action, $activityname, new component_action('click', $functionname));
+ }
+
+ $activitynode = $sectionnode->add($activityname, $action, navigation_node::TYPE_ACTIVITY, null, $activity->id, $icon);
$activitynode->title(get_string('modulename', $activity->modname));
$activitynode->hidden = $activity->hidden;
$activitynode->display = $activity->display;
}
}
- // Add nodes for forum posts and discussions if the user can view either or both
- // There are no capability checks here as the content of the page is based
- // purely on the forums the current user has access too.
- $forumtab = $usernode->add(get_string('forumposts', 'forum'));
- $forumtab->add(get_string('posts', 'forum'), new moodle_url('/mod/forum/user.php', $baseargs));
- $forumtab->add(get_string('discussions', 'forum'), new moodle_url('/mod/forum/user.php', array_merge($baseargs, array('mode'=>'discussions'))));
+ if (!empty($CFG->navadduserpostslinks)) {
+ // Add nodes for forum posts and discussions if the user can view either or both
+ // There are no capability checks here as the content of the page is based
+ // purely on the forums the current user has access too.
+ $forumtab = $usernode->add(get_string('forumposts', 'forum'));
+ $forumtab->add(get_string('posts', 'forum'), new moodle_url('/mod/forum/user.php', $baseargs));
+ $forumtab->add(get_string('discussions', 'forum'), new moodle_url('/mod/forum/user.php', array_merge($baseargs, array('mode'=>'discussions'))));
+ }
// Add blog nodes
if (!empty($CFG->bloglevel)) {
$coursenode->add(get_string('tags', 'tag'), new moodle_url('/tag/search.php'));
}
- // Calendar
- $calendarurl = new moodle_url('/calendar/view.php', array('view' => 'month'));
- $coursenode->add(get_string('calendar', 'calendar'), $calendarurl, self::TYPE_CUSTOM, null, 'calendar');
+ if (isloggedin()) {
+ // Calendar
+ $calendarurl = new moodle_url('/calendar/view.php', array('view' => 'month'));
+ $coursenode->add(get_string('calendar', 'calendar'), $calendarurl, self::TYPE_CUSTOM, null, 'calendar');
+ }
// View course reports
if (has_capability('moodle/site:viewreports', $this->page->context)) { // basic capability for listing of reports
/**
* Web service parameter exception class
- * @deprecated - use moodle exception instead
+ * @deprecated since Moodle 2.2 - use moodle exception instead
* This exception must be thrown to the web service client when a web service parameter is invalid
* The error string is gotten from webservice.php
*/
}
public function tearDown() {
- global $COURSE;
+ global $COURSE, $PAGE;
$this->testpage = NULL;
$COURSE = $this->originalcourse;
$PAGE = $this->originalpage;
/**
* Set all the attributes of one xmldb_key
*
- * @param string type XMLDB_KEY_PRIMARY, XMLDB_KEY_UNIQUE, XMLDB_KEY_FOREIGN
+ * @param string type XMLDB_KEY_[PRIMARY|UNIQUE|FOREIGN|FOREIGN_UNIQUE]
* @param array fields an array of fieldnames to build the key over
* @param string reftable name of the table the FK points to or null
* @param array reffields an array of fieldnames in the FK table or null
case XMLDB_KEY_FOREIGN:
$result .= 'XMLDB_KEY_FOREIGN' . ', ';
break;
+ case XMLDB_KEY_FOREIGN_UNIQUE:
+ $result .= 'XMLDB_KEY_FOREIGN_UNIQUE' . ', ';
+ break;
}
/// The fields
$keyfields = $this->getFields();
$result .= 'null';
}
/// The FKs attributes
- if ($this->getType() == XMLDB_KEY_FOREIGN) {
+ if ($this->getType() == XMLDB_KEY_FOREIGN ||
+ $this->getType() == XMLDB_KEY_FOREIGN_UNIQUE) {
/// The reftable
$reftable = $this->getRefTable();
if (!empty($reftable)) {
$button->set_format_by_file($file);
$output .= $button->to_html(PORTFOLIO_ADD_ICON_LINK);
}
- $output .= plagiarism_get_links(array('userid'=>$userid, 'file'=>$file, 'cmid'=>$this->cm->id, 'course'=>$this->course, 'assignment'=>$this->assignment));
- $output .= '<br />';
+
+ if ($CFG->enableplagiarism) {
+ require_once($CFG->libdir.'/plagiarismlib.php');
+ $output .= plagiarism_get_links(array('userid'=>$userid, 'file'=>$file, 'cmid'=>$this->cm->id, 'course'=>$this->course, 'assignment'=>$this->assignment));
+ $output .= '<br />';
+ }
}
if ($CFG->enableportfolios && count($files) > 1 && $this->portfolio_exportable() && has_capability('mod/assignment:exportownsubmission', $this->context)) {
$button->set_callback_options('assignment_portfolio_caller', array('id' => $this->cm->id, 'submissionid' => $submission->id), '/mod/assignment/locallib.php');
}
if (empty($submission->timemarked)) { /// Nothing to show, so print nothing
- if ($this->count_responsefiles($USER->id)) {
- echo $OUTPUT->heading(get_string('responsefiles', 'assignment'), 3);
- $responsefiles = $this->print_responsefiles($USER->id, true);
- echo $OUTPUT->box($responsefiles, 'generalbox boxaligncenter');
- }
return;
}
return;
}
- if ($grade->grade === null and empty($grade->str_feedback)) { /// Nothing to show yet
+ if ($grade->grade === null and empty($grade->str_feedback)) { // No grade to show yet
+ if ($this->count_responsefiles($USER->id)) { // but possibly response files are present
+ echo $OUTPUT->heading(get_string('responsefiles', 'assignment'), 3);
+ $responsefiles = $this->print_responsefiles($USER->id, true);
+ echo $OUTPUT->box($responsefiles, 'generalbox boxaligncenter');
+ }
return;
}
$updated->data2 = '';
$DB->update_record('assignment_submissions', $updated);
//TODO: add unfinalize action to log
- add_to_log($this->course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->assignment->id, $this->assignment->id, $this->cm->id);
+ add_to_log($this->course->id, 'assignment', 'view submission', 'submissions.php?id='.$this->cm->id.'&userid='.$userid.'&mode='.$mode.'&offset='.$offset, $this->assignment->id, $this->cm->id);
$submission = $this->get_submission($userid);
$this->update_grade($submission);
}
if ($choices->viewresponsecapability && $choices->deleterepsonsecapability) {
$selecturl = new moodle_url('#');
- $selectallactions = new component_action('click',"select_all_in", array('div',null,'tablecontainer'));
+ $selectallactions = new component_action('click',"checkall");
$selectall = new action_link($selecturl, get_string('selectall'), $selectallactions);
$actiondata .= $this->output->render($selectall) . ' / ';
- $deselectallactions = new component_action('click',"deselect_all_in", array('div',null,'tablecontainer'));
+ $deselectallactions = new component_action('click',"checknone");
$deselectall = new action_link($selecturl, get_string('deselectall'), $deselectallactions);
$actiondata .= $this->output->render($deselectall);
require_once("../../config.php");
require_once("lib.php");
require_once('delete_template_form.php');
+require_once($CFG->libdir.'/tablelib.php');
// $SESSION->feedback->current_tab = 'templates';
$current_tab = 'templates';
}
if(isset($formdata->confirmdelete) AND $formdata->confirmdelete == 1){
- feedback_delete_template($formdata->deletetempl);
+ if(!$template = $DB->get_record("feedback_template", array("id"=>$deletetempl))) {
+ print_error('error');
+ }
+
+ if($template->ispublic) {
+ $systemcontext = get_system_context();
+ require_capability('mod/feedback:createpublictemplate', $systemcontext);
+ require_capability('mod/feedback:deletetemplate', $systemcontext);
+ }
+
+ feedback_delete_template($template);
redirect($deleteurl->out(false));
}
/// Print the page header
$strfeedbacks = get_string("modulenameplural", "feedback");
$strfeedback = get_string("modulename", "feedback");
+$strdeletefeedback = get_string('delete_template','feedback');
$PAGE->set_heading(format_string($course->fullname));
$PAGE->set_title(format_string($feedback->name));
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
-echo $OUTPUT->heading(get_string('delete_template','feedback'));
+echo $OUTPUT->heading($strdeletefeedback);
if($shoulddelete == 1) {
echo $OUTPUT->box_start('generalbox errorboxcontent boxaligncenter boxwidthnormal');
$mform->display();
echo $OUTPUT->box_end();
}else {
- $templates = feedback_get_template_list($course, true);
- echo '<div class="mdl-align">';
+ //first we get the own templates
+ $templates = feedback_get_template_list($course, 'own');
if(!is_array($templates)) {
echo $OUTPUT->box(get_string('no_templates_available_yet', 'feedback'), 'generalbox boxaligncenter');
}else {
- echo '<table width="30%">';
- echo '<tr><th>'.get_string('templates', 'feedback').'</th><th> </th></tr>';
+ echo $OUTPUT->heading(get_string('course'), 3);
+ echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal');
+ $tablecolumns = array('template', 'action');
+ $tableheaders = array(get_string('template', 'feedback'), '');
+ $tablecourse = new flexible_table('feedback_template_course_table');
+
+ $tablecourse->define_columns($tablecolumns);
+ $tablecourse->define_headers($tableheaders);
+ $tablecourse->define_baseurl($deleteurl);
+ $tablecourse->column_style('action', 'width', '10%');
+
+ $tablecourse->sortable(false);
+ $tablecourse->set_attribute('width', '100%');
+ $tablecourse->set_attribute('class', 'generaltable');
+ $tablecourse->setup();
+
foreach($templates as $template) {
- echo '<tr><td align="center">'.$template->name.'</td>';
- echo '<td align="center">';
- echo '<form action="delete_template.php" method="post">';
- echo '<input title="'.get_string('delete_template','feedback').'" type="image" src="'.$OUTPUT->pix_url('t/delete') . '" hspace="1" height="11" width="11" border="0" />';
- echo '<input type="hidden" name="deletetempl" value="'.$template->id.'" />';
- echo '<input type="hidden" name="shoulddelete" value="1" />';
- echo '<input type="hidden" name="id" value="'.$id.'" />';
- echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
- echo '</form>';
- echo '</td></tr>';
+ $data = array();
+ $data[] = $template->name;
+ $url = new moodle_url($deleteurl, array(
+ 'id'=>$id,
+ 'deletetempl'=>$template->id,
+ 'shoulddelete'=>1,
+ ));
+
+ $data[] = $OUTPUT->single_button($url, $strdeletefeedback, 'post');
+ $tablecourse->add_data($data);
}
- echo '</table>';
+ $tablecourse->finish_output();
+ echo $OUTPUT->box_end();
}
-?>
- <form name="frm" action="delete_template.php" method="post">
- <input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
- <input type="hidden" name="id" value="<?php echo $id;?>" />
- <input type="hidden" name="canceldelete" value="0" />
- <button type="button" onclick="this.form.canceldelete.value=1;this.form.submit();"><?php print_string('cancel');?></button>
- </form>
- </div>
-<?php
+ //now we get the public templates if it is permitted
+ $systemcontext = get_system_context();
+ if(has_capability('mod/feedback:createpublictemplate', $systemcontext) AND
+ has_capability('mod/feedback:deletetemplate', $systemcontext)) {
+ $templates = feedback_get_template_list($course, 'public');
+ if(!is_array($templates)) {
+ echo $OUTPUT->box(get_string('no_templates_available_yet', 'feedback'), 'generalbox boxaligncenter');
+ }else {
+ echo $OUTPUT->heading(get_string('public', 'feedback'), 3);
+ echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal');
+ $tablecolumns = array('template', 'action');
+ $tableheaders = array(get_string('template', 'feedback'), '');
+ $tablepublic = new flexible_table('feedback_template_public_table');
+
+ $tablepublic->define_columns($tablecolumns);
+ $tablepublic->define_headers($tableheaders);
+ $tablepublic->define_baseurl($deleteurl);
+ $tablepublic->column_style('action', 'width', '10%');
+
+ $tablepublic->sortable(false);
+ $tablepublic->set_attribute('width', '100%');
+ $tablepublic->set_attribute('class', 'generaltable');
+ $tablepublic->setup();
+
+
+ // echo $OUTPUT->heading(get_string('public', 'feedback'), 3);
+ // echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
+ foreach($templates as $template) {
+ $data = array();
+ $data[] = $template->name;
+ $url = new moodle_url($deleteurl, array(
+ 'id'=>$id,
+ 'deletetempl'=>$template->id,
+ 'shoulddelete'=>1,
+ ));
+
+ $data[] = $OUTPUT->single_button($url, $strdeletefeedback, 'post');
+ $tablepublic->add_data($data);
+ }
+ $tablepublic->finish_output();
+ echo $OUTPUT->box_end();
+ }
+ }
+
+ echo $OUTPUT->box_start('boxaligncenter boxwidthnormal');
+ $url = new moodle_url($deleteurl, array(
+ 'id'=>$id,
+ 'canceldelete'=>1,
+ ));
+
+ echo $OUTPUT->single_button($url, get_string('back'), 'post');
+ echo $OUTPUT->box_end();
}
echo $OUTPUT->footer();
//the create_template-form
$create_template_form = new feedback_edit_create_template_form();
-$create_template_form->set_feedbackdata(array('context' => $context));
+$create_template_form->set_feedbackdata(array('context'=>$context, 'course'=>$course));
$create_template_form->set_form_elements();
$create_template_form->set_data(array('id'=>$id, 'do_show'=>'templates'));
$create_template_formdata = $create_template_form->get_data();
if(isset($create_template_formdata->savetemplate) && $create_template_formdata->savetemplate == 1) {
//check the capabilities to create templates
if(!has_capability('mod/feedback:createprivatetemplate', $context) AND
- !has_capability('mod/feedback:createpublictemplate', $context)) {
+ !has_capability('mod/feedback:createpublictemplate', $context)) {
print_error('cannotsavetempl', 'feedback');
}
- if(trim($create_template_formdata->templatename) == '')
- {
+ if(trim($create_template_formdata->templatename) == '') {
$savereturn = 'notsaved_name';
}else {
- //public templates are currently deaktivated
- // if(has_capability('mod/feedback:createpublictemplate', $context)) {
- // $create_template_formdata->ispublic = isset($create_template_formdata->ispublic) ? 1 : 0;
- // }else {
+ //if the feedback is located on the frontpage then templates can be public
+ if(has_capability('mod/feedback:createpublictemplate', get_system_context())) {
+ $create_template_formdata->ispublic = isset($create_template_formdata->ispublic) ? 1 : 0;
+ }else {
$create_template_formdata->ispublic = 0;
- // }
- if(!feedback_save_as_template($feedback, $create_template_formdata->templatename, $create_template_formdata->ispublic))
- {
+ }
+ if(!feedback_save_as_template($feedback, $create_template_formdata->templatename, $create_template_formdata->ispublic)) {
$savereturn = 'failed';
}else {
$savereturn = 'saved';
// visible elements
$templates_options = array();
- if($templates = feedback_get_template_list($this->feedbackdata->course)){//get the templates
- $templates_options[' '] = get_string('select');
- foreach($templates as $template) {
- $templates_options[$template->id] = $template->name;
+ $owntemplates = feedback_get_template_list($this->feedbackdata->course, 'own');
+ $publictemplates = feedback_get_template_list($this->feedbackdata->course, 'public');
+
+ $options = array();
+ if($owntemplates or $publictemplates) {
+ $options[''] = array('' => get_string('choose'));
+
+ if($owntemplates) {
+ $courseoptions = array();
+ foreach($owntemplates as $template) {
+ $courseoptions[$template->id] = $template->name;
+ }
+ $options[get_string('course')] = $courseoptions;
}
+
+ if($publictemplates) {
+ $publicoptions = array();
+ foreach($publictemplates as $template) {
+ $publicoptions[$template->id] = $template->name;
+ }
+ $options[get_string('public', 'feedback')] = $publicoptions;
+ }
+
$attributes = 'onChange="this.form.submit()"';
- $elementgroup[] =& $mform->createElement('select', 'templateid', '', $templates_options, $attributes);
- // buttons
+ $elementgroup[] =& $mform->createElement('selectgroups', 'templateid', '', $options, $attributes);
$elementgroup[] =& $mform->createElement('submit', 'use_template', get_string('use_this_template', 'feedback'));
}else {
$mform->addElement('static', 'info', get_string('no_templates_available_yet', 'feedback'));
$elementgroup[] =& $mform->createElement('static', 'templatenamelabel', get_string('name', 'feedback'));
$elementgroup[] =& $mform->createElement('text', 'templatename', get_string('name', 'feedback'), array('size'=>'40', 'maxlength'=>'200'));
- //public templates are currently deactivated
- // if(has_capability('mod/feedback:createpublictemplate', $this->feedbackdata->context)) {
- // $elementgroup[] =& $mform->createElement('checkbox', 'ispublic', get_string('public', 'feedback'), get_string('public', 'feedback'));
- // }
+ if(has_capability('mod/feedback:createpublictemplate', get_system_context())) {
+ $elementgroup[] =& $mform->createElement('checkbox', 'ispublic', get_string('public', 'feedback'), get_string('public', 'feedback'));
+ }
// buttons
$elementgroup[] =& $mform->createElement('submit', 'create_template', get_string('save_as_new_template', 'feedback'));
//is the item a template?
if(!$item->feedback AND $item->template) {
$template = $DB->get_record('feedback_template', array('id'=>$item->template));
- $context = get_context_instance(CONTEXT_COURSE, $template->course);
+ if($template->ispublic) {
+ $context = get_system_context();
+ }else {
+ $context = get_context_instance(CONTEXT_COURSE, $template->course);
+ }
$filearea = 'template';
}else {
$cm = get_coursemodule_from_instance('feedback', $item->feedback);
/**
* Serves the files included in feedback items like label. Implements needed access control ;-)
*
+ * There are two situations in general where the files will be sent.
+ * 1) filearea = item, 2) filearea = template
+ *
* @param object $course
* @param object $cm
* @param object $context
function feedback_pluginfile($course, $cm, $context, $filearea, $args, $forcedownload) {
global $CFG, $DB;
- require_login($course, false, $cm);
-
$itemid = (int)array_shift($args);
- require_course_login($course, true, $cm);
-
+ //get the item what includes the file
if (!$item = $DB->get_record('feedback_item', array('id'=>$itemid))) {
return false;
}
+
+ //if the filearea is "item" so we check the permissions like view/complete the feedback
+ if($filearea === 'item') {
+ //get the feedback
+ if(!$feedback = $DB->get_record('feedback', array('id'=>$item->feedback))) {
+ return false;
+ }
- if (!has_capability('mod/feedback:view', $context)) {
+ $canload = false;
+ //first check whether the user has the complete capability
+ if(has_capability('mod/feedback:complete', $context)) {
+ $canload = true;
+ }
+
+ //now we check whether the user has the view capability
+ if(has_capability('mod/feedback:view', $context)) {
+ $canload = true;
+ }
+
+ //if the feedback is on frontpage and anonymous and the fullanonymous is allowed
+ //so the file can be loaded too.
+ if(isset($CFG->feedback_allowfullanonymous)
+ AND $CFG->feedback_allowfullanonymous
+ AND $course->id == SITEID
+ AND $feedback->anonymous == FEEDBACK_ANONYMOUS_YES ) {
+ $canload = true;
+ }
+
+ if(!$canload) {
+ return false;
+ }
+ }else if($filearea === 'template') { //now we check files in templates
+ if(!$template = $DB->get_record('feedback_template', array('id'=>$item->template))) {
+ return false;
+ }
+
+ //if the file is not public so the capability edititems has to be there
+ if(!$template->ispublic) {
+ if(!has_capability('mod/feedback:edititems', $context)) {
+ return false;
+ }
+ }else { //on public templates, at least the user has to be logged in
+ if(!isloggedin()) {
+ return false;
+ }
+ }
+ }else {
return false;
}
}
}
- if ($context->contextlevel == CONTEXT_COURSE) {
+ if ($context->contextlevel == CONTEXT_COURSE || $context->contextlevel == CONTEXT_SYSTEM) {
if ($filearea !== 'template') {
return false;
}
return false;
}
-
/**
* this will delete a given instance.
* all referenced data also will be deleted
global $DB;
$templ = new stdClass();
- $templ->course = $courseid;
+ $templ->course = ($ispublic ? 0 : $courseid);
$templ->name = $name;
$templ->ispublic = $ispublic;
return false;
}
- //files in the template_item are in the context of the current course
+ //files in the template_item are in the context of the current course or
+ //if the template is public the files are in the system context
//files in the feedback_item are in the feedback_context of the feedback
- $c_context = get_context_instance(CONTEXT_COURSE, $newtempl->course);
+ if($ispublic) {
+ $s_context = get_system_context();
+ }else {
+ $s_context = get_context_instance(CONTEXT_COURSE, $newtempl->course);
+ }
$cm = get_coursemodule_from_instance('feedback', $feedback->id);
$f_context = get_context_instance(CONTEXT_MODULE, $cm->id);
if ($itemfiles = $fs->get_area_files($f_context->id, 'mod_feedback', 'item', $item->id, "id", false)) {
foreach($itemfiles as $ifile) {
$file_record = new stdClass();
- $file_record->contextid = $c_context->id;
+ $file_record->contextid = $s_context->id;
$file_record->component = 'mod_feedback';
$file_record->filearea = 'template';
$file_record->itemid = $t_item->id;
*
* @global object
* @uses CONTEXT_COURSE
- * @param int $id the templateid
+ * @param object $template the template
* @return void
*/
-function feedback_delete_template($id) {
+function feedback_delete_template($template) {
global $DB;
- $template = $DB->get_record("feedback_template", array("id"=>$id));
-
- //deleting the files from the item
- $fs = get_file_storage();
- $context = get_context_instance(CONTEXT_COURSE, $template->course);
-
-
- if($t_items = $DB->get_records("feedback_item", array("template"=>$id))) {
+ //deleting the files from the item is done by feedback_delete_item
+ if($t_items = $DB->get_records("feedback_item", array("template"=>$template->id))) {
foreach($t_items as $t_item) {
- if ($templatefiles = $fs->get_area_files($context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) {
- $fs->delete_area_files($context->id, 'mod_feedback', 'template', $t_item->id);
- }
+ feedback_delete_item($t_item->id, false, $template);
}
}
- $DB->delete_records("feedback_template", array("id"=>$id));
+ $DB->delete_records("feedback_template", array("id"=>$template->id));
}
/**
$fs = get_file_storage();
+ if(!$template = $DB->get_record('feedback_template', array('id'=>$templateid))) {
+ return false;
+ }
//get all templateitems
if(!$templitems = $DB->get_records('feedback_item', array('template'=>$templateid))) {
return false;
//files in the template_item are in the context of the current course
//files in the feedback_item are in the feedback_context of the feedback
- $c_context = get_context_instance(CONTEXT_COURSE, $feedback->course);
+ if($template->ispublic) {
+ $s_context = get_system_context();
+ }else {
+ $s_context = get_context_instance(CONTEXT_COURSE, $feedback->course);
+ }
$course = $DB->get_record('course', array('id'=>$feedback->course));
$cm = get_coursemodule_from_instance('feedback', $feedback->id);
$f_context = get_context_instance(CONTEXT_MODULE, $cm->id);
$item->position = $item->position + $positionoffset;
$item->id = $DB->insert_record('feedback_item', $item);
-
+
//TODO: moving the files to the new items
- if ($templatefiles = $fs->get_area_files($c_context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) {
+ if ($templatefiles = $fs->get_area_files($s_context->id, 'mod_feedback', 'template', $t_item->id, "id", false)) {
foreach($templatefiles as $tfile) {
$file_record = new stdClass();
$file_record->contextid = $f_context->id;
*
* @global object
* @param object $course
- * @param boolean $onlyown
+ * @param string $onlyownorpublic
* @return array the template recordsets
*/
-function feedback_get_template_list($course, $onlyown = false) {
- global $DB;
+function feedback_get_template_list($course, $onlyownorpublic = '') {
+ global $DB, $CFG;
- if ($onlyown) {
- $templates = $DB->get_records('feedback_template', array('course'=>$course->id));
- } else {
- $templates = $DB->get_records_select('feedback_template', 'course = ? OR ispublic = 1', array($course->id));
+ switch($onlyownorpublic) {
+ case '':
+ $templates = $DB->get_records_select('feedback_template', 'course = ? OR ispublic = 1', array($course->id), 'name');
+ break;
+ case 'own':
+ $templates = $DB->get_records('feedback_template', array('course'=>$course->id), 'name');
+ break;
+ case 'public':
+ $templates = $DB->get_records('feedback_template', array('ispublic'=>1), 'name');
+ break;
}
return $templates;
}
* @uses CONTEXT_MODULE
* @param int $itemid
* @param boolean $renumber should the kept items renumbered Yes/No
+ * @param object $template if the template is given so the items are bound to it
* @return void
*/
-function feedback_delete_item($itemid, $renumber = true){
+function feedback_delete_item($itemid, $renumber = true, $template = false){
global $DB;
//deleting the files from the item
$fs = get_file_storage();
- if (!$cm = get_coursemodule_from_instance('feedback', $item->feedback)) {
- return false;
- }
- $context = get_context_instance(CONTEXT_MODULE, $cm->id);
+
+ if($template) {
+ if($template->ispublic) {
+ $context = get_system_context();
+ }else {
+ $context = get_context_instance(CONTEXT_COURSE, $template->course);
+ }
+ if ($templatefiles = $fs->get_area_files($context->id, 'mod_feedback', 'template', $item->id, "id", false)) {
+ $fs->delete_area_files($context->id, 'mod_feedback', 'template', $item->id);
+ }
+ }else {
+ if (!$cm = get_coursemodule_from_instance('feedback', $item->feedback)) {
+ return false;
+ }
+ $context = get_context_instance(CONTEXT_MODULE, $cm->id);
- if ($itemfiles = $fs->get_area_files($context->id, 'mod_feedback', 'item', $item->id, "id", false)) {
- $fs->delete_area_files($context->id, 'mod_feedback', 'item', $item->id);
+ if ($itemfiles = $fs->get_area_files($context->id, 'mod_feedback', 'item', $item->id, "id", false)) {
+ $fs->delete_area_files($context->id, 'mod_feedback', 'item', $item->id);
+ }
}
$DB->delete_records("feedback_value", array("item"=>$itemid));
*/
- $module->version = 2011051600; // The current module version (Date: YYYYMMDDXX)
+ $module->version = 2011100800; // The current module version (Date: YYYYMMDDXX)
$module->requires = 2010080300; // Requires this Moodle version
$feedback_version_intern = 1; //this version is used for restore older backups
$module->cron = 0; // Period for cron to check this module (secs)
'error' => false);
// Remove all grades from gradebook
+ $DB->delete_records_select('quiz_grades',
+ 'quiz IN (SELECT id FROM {quiz} WHERE course = ?)', array($data->courseid));
if (empty($data->reset_gradebook_grades)) {
quiz_reset_gradebook($data->courseid);
}
$status[] = array(
'component' => $componentstr,
- 'item' => get_string('attemptsdeleted', 'quiz'),
+ 'item' => get_string('gradesdeleted', 'quiz'),
'error' => false);
}
<FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="course" NEXT="intro"/>
<FIELD NAME="intro" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="name" NEXT="introformat"/>
<FIELD NAME="introformat" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="intro" NEXT="externalurl"/>
- <FIELD NAME="externalurl" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="introformat" NEXT="display"/>
+ <FIELD NAME="externalurl" TYPE="text" LENGTH="small" NOTNULL="true" SEQUENCE="false" PREVIOUS="introformat" NEXT="display"/>
<FIELD NAME="display" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="externalurl" NEXT="displayoptions"/>
<FIELD NAME="displayoptions" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="display" NEXT="parameters"/>
<FIELD NAME="parameters" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="displayoptions" NEXT="timemodified"/>
// Moodle v2.1.0 release upgrade line
// Put any upgrade step following this
+ if ($oldversion < 2011092800) {
+
+ // Changing nullability of field externalurl on table urls to not-null
+ $table = new xmldb_table('url');
+ $field = new xmldb_field('externalurl', XMLDB_TYPE_TEXT, 'small', null,
+ XMLDB_NOTNULL, null, null, 'introformat');
+
+ $DB->set_field_select('url', 'externalurl', $DB->sql_empty(), 'externalurl IS NULL');
+ // Launch change of nullability for field =externalurl
+ $dbman->change_field_notnull($table, $field);
+
+ // url savepoint reached
+ upgrade_mod_savepoint(true, 2011092800, 'url');
+ }
return true;
}
$string['externalurl'] = 'External URL';
$string['framesize'] = 'Frame height';
$string['chooseavariable'] = 'Choose a variable...';
+$string['invalidurl'] = 'Entered URL is invalid';
$string['modulename'] = 'URL';
$string['modulenameplural'] = 'URLs';
$string['neverseen'] = 'Never seen';
//-------------------------------------------------------
$mform->addElement('header', 'content', get_string('contentheader', 'url'));
$mform->addElement('url', 'externalurl', get_string('externalurl', 'url'), array('size'=>'60'), array('usefilepicker'=>true));
+ $mform->addRule('externalurl', null, 'required', null, 'client');
//-------------------------------------------------------
$mform->addElement('header', 'optionssection', get_string('optionsheader', 'url'));
}
}
+ function validation($data, $files) {
+ $errors = parent::validation($data, $files);
+ //Validating Entered url
+ $data['externalurl'] = clean_param($data['externalurl'], PARAM_URL);
+ if (empty($data['externalurl'])) {
+ $errors['externalurl'] = get_string('invalidurl', 'url');
+ }
+ return $errors;
+ }
+
}
defined('MOODLE_INTERNAL') || die;
-$module->version = 2010101400;
+$module->version = 2011092800;
$module->requires = 2010080300; // Requires this Moodle version
$module->cron = 0;
$creator = wiki_get_user_info($version0page->userid);
$a = new StdClass;
$a->date = userdate($this->page->timecreated, get_string('strftimedaydatetime', 'langconfig'));
- $a->username = $creator->username;
+ $a->username = fullname($creator);
echo $OUTPUT->heading(get_string('createddate', 'wiki', $a), 4, 'wiki_headingtime');
if ($vcount > 0) {
$creator = wiki_get_user_info($version0page->userid);
$a = new stdClass();
$a->date = userdate($this->page->timecreated, get_string('strftimedaydatetime', 'langconfig'));
- $a->username = $creator->username;
+ $a->username = fullname($creator);
echo $OUTPUT->heading(get_string('createddate', 'wiki', $a), 4, 'wiki_headingtime');
if ($versioncount > 0) {
/// If there is only one version, we don't need radios nor forms
return $status;
}
+ $prevstep = $this->qa->get_last_step_with_behaviour_var('_try');
+ $prevresponse = $prevstep->get_qt_data();
$prevtries = $this->qa->get_last_behaviour_var('_try', 0);
$prevbest = $pendingstep->get_fraction();
if (is_null($prevbest)) {
$prevbest = 0;
}
+ if ($this->question->is_same_response($response, $prevresponse)) {
+ return question_attempt::DISCARD;
+ }
+
list($fraction, $state) = $this->question->grade_response($response);
$pendingstep->set_fraction(max($prevbest, $this->adjusted_fraction($fraction, $prevtries)));
- if ($state == question_state::$gradedright) {
+ if ($prevstep->get_state() == question_state::$complete) {
+ $pendingstep->set_state(question_state::$complete);
+ } else if ($state == question_state::$gradedright) {
$pendingstep->set_state(question_state::$complete);
} else {
$pendingstep->set_state(question_state::$todo);
return question_attempt::DISCARD;
}
- $laststep = $this->qa->get_last_step();
- $response = $laststep->get_qt_data();
- if (!$this->question->is_gradable_response($response)) {
- $pendingstep->set_state(question_state::$gaveup);
- return question_attempt::KEEP;
- }
-
$prevtries = $this->qa->get_last_behaviour_var('_try', 0);
- $prevbest = $pendingstep->get_fraction();
+ $prevbest = $this->qa->get_fraction();
if (is_null($prevbest)) {
$prevbest = 0;
}
- if ($laststep->has_behaviour_var('_try')) {
- // Last answer was graded, we want to regrade it. Otherwise the answer
- // has changed, and we are grading a new try.
- $prevtries -= 1;
- }
+ $laststep = $this->qa->get_last_step();
+ $response = $laststep->get_qt_data();
+ if (!$this->question->is_gradable_response($response)) {
+ $state = question_state::$gaveup;
+ $fraction = 0;
+ } else {
- list($fraction, $state) = $this->question->grade_response($response);
+ if ($laststep->has_behaviour_var('_try')) {
+ // Last answer was graded, we want to regrade it. Otherwise the answer
+ // has changed, and we are grading a new try.
+ $prevtries -= 1;
+ }
+
+ list($fraction, $state) = $this->question->grade_response($response);
+
+ $pendingstep->set_behaviour_var('_try', $prevtries + 1);
+ $pendingstep->set_behaviour_var('_rawfraction', $fraction);
+ $pendingstep->set_new_response_summary($this->question->summarise_response($response));
+ }
- $pendingstep->set_fraction(max($prevbest, $this->adjusted_fraction($fraction, $prevtries)));
$pendingstep->set_state($state);
- $pendingstep->set_behaviour_var('_try', $prevtries + 1);
- $pendingstep->set_behaviour_var('_rawfraction', $fraction);
- $pendingstep->set_new_response_summary($this->question->summarise_response($response));
+ $pendingstep->set_fraction(max($prevbest, $this->adjusted_fraction($fraction, $prevtries)));
return question_attempt::KEEP;
}
+
+ /**
+ * Got the most recently graded step. This is mainly intended for use by the
+ * renderer.
+ * @return question_attempt_step the most recently graded step.
+ */
+ public function get_graded_step() {
+ $step = $this->qa->get_last_step_with_behaviour_var('_try');
+ if ($step->has_behaviour_var('_try')) {
+ return $step;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Determine whether a question state represents an "improvable" result,
+ * that is, whether the user can still improve their score.
+ *
+ * @param question_state $state the question state.
+ * @return bool whether the state is improvable
+ */
+ public function is_state_improvable(question_state $state) {
+ return $state == question_state::$todo;
+ }
}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_adaptive_renderer extends qbehaviour_renderer {
- protected function get_graded_step(question_attempt $qa) {
- foreach ($qa->get_reverse_step_iterator() as $step) {
- if ($step->has_behaviour_var('_try')) {
- return $step;
- }
- }
- }
public function controls(question_attempt $qa, question_display_options $options) {
return $this->submit_button($qa, $options);
public function feedback(question_attempt $qa, question_display_options $options) {
// Try to find the last graded step.
- $gradedstep = $this->get_graded_step($qa);
+ $gradedstep = $qa->get_behaviour()->get_graded_step($qa);
if (is_null($gradedstep) || $qa->get_max_mark() == 0 ||
$options->marks < question_display_options::MARK_AND_MAX) {
return '';
}
$output = '';
- // print details of grade adjustment due to penalties
+ // Print details of grade adjustment due to penalties
if ($mark->raw != $mark->cur) {
$output .= ' ' . get_string('gradingdetailsadjustment', 'qbehaviour_adaptive', $mark);
}
- // print info about new penalty
- // penalty is relevant only if the answer is not correct and further attempts are possible
- if (!$qa->get_state()->is_finished()) {
+ // Print information about any new penalty, only relevant if the answer can be improved.
+ if ($qa->get_behaviour()->is_state_improvable($qa->get_state())) {
$output .= ' ' . get_string('gradingdetailspenalty', 'qbehaviour_adaptive',
format_float($qa->get_question()->penalty, $options->markdp));
}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_adaptive_walkthrough_test extends qbehaviour_walkthrough_test_base {
+ protected function get_contains_penalty_info_expectation($penalty) {
+ $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive',
+ format_float($penalty, $this->displayoptions->markdp));
+ return new PatternExpectation('/'.preg_quote($penaltyinfo).'/');
+ }
+
+ protected function get_does_not_contain_penalty_info_expectation() {
+ $penaltyinfo = get_string('gradingdetailspenalty', 'qbehaviour_adaptive', 'XXXXX');
+ $penaltypattern = '/'.str_replace('XXXXX', '\\w*', preg_quote($penaltyinfo)).'/';
+ return new NoPatternExpectation($penaltypattern);
+ }
+
public function test_adaptive_multichoice() {
// Create a multiple choice, single response question.
$this->get_contains_mc_radio_expectation($wrongindex, true, true),
$this->get_contains_mc_radio_expectation(($wrongindex + 1) % 3, true, false),
$this->get_contains_mc_radio_expectation(($wrongindex + 2) % 3, true, false),
- $this->get_contains_incorrect_expectation());
+ $this->get_contains_incorrect_expectation(),
+ $this->get_contains_penalty_info_expectation(0.33));
$this->assertPattern('/B|C/',
$this->quba->get_response_summary($this->slot));
$this->get_contains_mc_radio_expectation(($rightindex + 1) % 3, true, false),
$this->get_contains_mc_radio_expectation(($rightindex + 2) % 3, true, false),
$this->get_contains_correct_expectation(),
- new PatternExpectation('/' . preg_quote(
- get_string('gradingdetailspenalty', 'qbehaviour_adaptive',
- format_float($mc->penalty, $this->displayoptions->markdp))) . '/'));
+ $this->get_does_not_contain_penalty_info_expectation());
$this->assertEqual('A',
$this->quba->get_response_summary($this->slot));
// Now change the correct answer to the question, and regrade.
$mc->answers[13]->fraction = -0.33333333;
- $mc->answers[15]->fraction = 1;
+ $mc->answers[14]->fraction = 1; // We don't know which "wrong" index we chose above!
+ $mc->answers[15]->fraction = 1; // Therefore, treat answers B and C with the same score.
$this->quba->regrade_all_questions();
// Verify.
$this->get_contains_partcorrect_expectation());
$autogradedstep = $this->get_step($this->get_step_count() - 2);
- $this->assertWithinMargin($autogradedstep->get_fraction(), 0, 0.0000001);
+ $this->assertWithinMargin($autogradedstep->get_fraction(), 1, 0.0000001);
}
public function test_adaptive_multichoice2() {
$this->check_current_output(
$this->get_contains_mark_summary(2),
$this->get_contains_submit_button_expectation(true),
- $this->get_contains_correct_expectation());
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation());
- // Save the same correct answer again. Should no do anything.
+ // Save the same correct answer again. Should not do anything.
$numsteps = $this->get_step_count();
$this->process_submission(array('choice0' => 1, 'choice2' => 1));
// Verify.
$this->check_step_count($numsteps);
+ $this->check_current_mark(2);
$this->check_current_state(question_state::$complete);
// Finish the attempt.
$this->get_contains_correct_expectation());
}
+ public function test_adaptive_shortanswer_partially_right() {
+
+ // Create a short answer question
+ $sa = test_question_maker::make_a_shortanswer_question();
+ $this->start_attempt_at_question($sa, 'adaptive');
+
+ // Check the initial state.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation());
+
+ // Submit a partially correct answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'toad'));
+
+ // Verify.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(0.8);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.8),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_partcorrect_expectation(),
+ $this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit an incorrect answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
+
+ // Verify.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(0.8);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.8),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit a correct answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(0.8);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.8),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Finish the attempt.
+ $this->quba->finish_all_questions();
+
+ // Verify.
+ $this->check_current_state(question_state::$gradedright);
+ $this->check_current_mark(0.8);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.8),
+ $this->get_contains_submit_button_expectation(false),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+ }
+
+ public function test_adaptive_shortanswer_wrong_right_wrong() {
+
+ // Create a short answer question
+ $sa = test_question_maker::make_a_shortanswer_question();
+ $this->start_attempt_at_question($sa, 'adaptive');
+
+ // Check the initial state.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation());
+
+ // Submit a wrong answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
+
+ // Verify.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(0);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit the same wrong answer again. Nothing should change.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
+
+ // Verify.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(0);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit a correct answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(0.66666667);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.67),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit another incorrect answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(0.66666667);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.67),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Finish the attempt.
+ $this->quba->finish_all_questions();
+
+ // Verify.
+ $this->check_current_state(question_state::$gradedwrong);
+ $this->check_current_mark(0.66666667);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.67),
+ $this->get_contains_submit_button_expectation(false),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+ }
+
+ public function test_adaptive_shortanswer_invalid_after_complete() {
+
+ // Create a short answer question
+ $sa = test_question_maker::make_a_shortanswer_question();
+ $this->start_attempt_at_question($sa, 'adaptive');
+
+ // Check the initial state.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation());
+
+ // Submit a wrong answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'hippopotamus'));
+
+ // Verify.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(0);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_contains_penalty_info_expectation(0.33),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit a correct answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'frog'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(0.66666667);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.67),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit an empty answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => ''));
+
+ // Verify.
+ $this->check_current_state(question_state::$invalid);
+ $this->check_current_mark(0.66666667);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.67),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_contains_validation_error_expectation());
+
+ // Submit another wrong answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => 'bumblebee'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(0.66666667);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.67),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Finish the attempt.
+ $this->quba->finish_all_questions();
+
+ // Verify.
+ $this->check_current_state(question_state::$gradedwrong);
+ $this->check_current_mark(0.66666667);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(0.67),
+ $this->get_contains_submit_button_expectation(false),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+ }
+
public function test_adaptive_shortanswer_try_to_submit_blank() {
// Create a short answer question with correct answer true.
$this->get_contains_marked_out_of_summary(),
$this->get_contains_submit_button_expectation(true),
$this->get_does_not_contain_correctness_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
$this->get_contains_validation_error_expectation());
$this->assertNull($this->quba->get_response_summary($this->slot));
$this->get_contains_mark_summary(0.8),
$this->get_contains_submit_button_expectation(true),
$this->get_contains_partcorrect_expectation(),
+ $this->get_contains_penalty_info_expectation(0.33),
$this->get_does_not_contain_validation_error_expectation());
// Now submit blank again.
$this->get_contains_mark_summary(0.8),
$this->get_contains_submit_button_expectation(true),
$this->get_contains_partcorrect_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
$this->get_contains_validation_error_expectation());
}
+
+ public function test_adaptive_numerical() {
+
+ // Create a numerical question
+ $sa = test_question_maker::make_question('numerical', 'pi');
+ $this->start_attempt_at_question($sa, 'adaptive');
+
+ // Check the initial state.
+ $this->check_current_state(question_state::$todo);
+ $this->check_current_mark(null);
+ $this->check_current_output(
+ $this->get_contains_marked_out_of_summary(),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_does_not_contain_feedback_expectation());
+
+ // Submit the correct answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => '3.14'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(1);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(1),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_correct_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Submit an incorrect answer.
+ $this->process_submission(array('-submit' => 1, 'answer' => '-5'));
+
+ // Verify.
+ $this->check_current_state(question_state::$complete);
+ $this->check_current_mark(1);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(1),
+ $this->get_contains_submit_button_expectation(true),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_does_not_contain_penalty_info_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+
+ // Finish the attempt.
+ $this->quba->finish_all_questions();
+
+ // Verify.
+ $this->check_current_state(question_state::$gradedwrong);
+ $this->check_current_mark(1);
+ $this->check_current_output(
+ $this->get_contains_mark_summary(1),
+ $this->get_contains_submit_button_expectation(false),
+ $this->get_contains_incorrect_expectation(),
+ $this->get_does_not_contain_validation_error_expectation());
+ }
}
// Now change the correct answer to the question, and regrade.
$mc->answers[13]->fraction = -0.33333333;
- $mc->answers[15]->fraction = 1;
+ $mc->answers[14]->fraction = 1; // We don't know which "wrong" index we chose above!
+ $mc->answers[15]->fraction = 1; // Therefore, treat answers B and C with the same score.
$this->quba->regrade_all_questions();
// Verify.
$this->get_contains_mark_summary(1),
$this->get_contains_partcorrect_expectation());
- $autogradedstep = $this->get_step($this->get_step_count() - 2);
- $this->assertWithinMargin($autogradedstep->get_fraction(), 0, 0.0000001);
+ $autogradedstep = $this->get_step($this->get_step_count() - 3);
+ $this->assertWithinMargin($autogradedstep->get_fraction(), 1, 0.0000001);
}
public function test_multichoice2() {
DELETE qu, qa, qas, qasd
FROM {question_usages} qu
JOIN {question_attempts} qa ON qa.questionusageid = qu.id
- JOIN {question_attempt_steps} qas ON qas.questionattemptid = qa.id
- JOIN {question_attempt_step_data} qasd ON qasd.attemptstepid = qas.id
+ LEFT JOIN {question_attempt_steps} qas ON qas.questionattemptid = qa.id
+ LEFT JOIN {question_attempt_step_data} qasd ON qasd.attemptstepid = qas.id
WHERE qu.id ' . $qubaids->usage_id_in(),
$qubaids->usage_id_in_params());
}
$this->db->execute('
DELETE qas, qasd
FROM {question_attempt_steps} qas
- JOIN {question_attempt_step_data} qasd ON qasd.attemptstepid = qas.id
+ LEFT JOIN {question_attempt_step_data} qasd ON qasd.attemptstepid = qas.id
WHERE qas.questionattemptid ' . $test, $params);
}
return new question_attempt_step_read_only();
}
+ /**
+ * Get the last step with a particular behaviour variable set.
+ * @param string $name the name of the variable to get.
+ * @return question_attempt_step the last step, or a step with no variables
+ * if there was not a real step.
+ */
+ public function get_last_step_with_behaviour_var($name) {
+ foreach ($this->get_reverse_step_iterator() as $step) {
+ if ($step->has_behaviour_var($name)) {
+ return $step;
+ }
+ }
+ return new question_attempt_step_read_only();
+ }
+
/**
* Get the latest value of a particular question type variable. That is, get
* the value from the latest step that has it set. Return null if it is not
<li>change their question type (numerical, shortanswer, multiple choice). </li></ul>
';
$string['questionsless'] = '{$a} question(s) less than in the multianswer question stored in the database';
-$string['questionsmissing'] = 'No valid questions, create at least one question';
+$string['questionsmissing'] = 'The question text must include at least one embedded answer.';
$string['questionsmore'] = '{$a} question(s) more than in the multianswer question stored in the database';
$string['questionnotfound'] = 'Unable to find question of question part #{$a}';
$string['questionsaveasedited'] = 'The question will be saved as edited';
$prevresponse, $newresponse, 'unit');
}
- return false;
+ return true;
}
public function get_correct_response() {
/************************************************************************
- Non sono riuscito a trovare un modo per sovrascrivere la regola
+ I could not find a way to override the rule
border: 2px solid #ddd;
- presente nel file
+ belonging to the file
/theme/canvas/style/core.css
- Per questo questo file è stato creato a partire dalla copia del
- foglio di stile core.css del tema canvas, ed è stato adeguato alle
- esigenze del tema corrente.
+ Therefore this file has been created from the copy of that css file
+ and adapted to the needs of the current theme.
************************************************************************/
.sitetopic {
label {
margin-right: 0.3em;
}
+
.navbutton .singlebutton {
margin: 0;
}
}
.loginbox .loginform .form-label {
- float: none;
- width: 100%;
margin: 0 auto;
- text-align: left;
+ float: left;
+ text-align: right;
+ width: 40%;
}
.dir-rtl .loginbox .loginform .form-label {
}
.loginbox .loginform .form-input {
- float: none;
- width: 100%;
+ float: right;
+ width: 59%;
margin: 0 auto;
}
// if they are requesting a revision that's not -1, and they have supplied an
// If-Modified-Since header, we can send back a 304 Not Modified since the
// content never changes (the rev number is increased any time the content changes)
-if (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
+if (strpos($parts, '/-1/') === false and (!empty($_SERVER['HTTP_IF_NONE_MATCH']) || !empty($_SERVER['HTTP_IF_MODIFIED_SINCE']))) {
$lifetime = 60*60*24*30; // 30 days
header('HTTP/1.1 304 Not Modified');
header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
$course = optional_param('course', SITEID, PARAM_INT); // course id (defaults to Site)
$cancelemailchange = optional_param('cancelemailchange', 0, PARAM_INT); // course id (defaults to Site)
-$PAGE->set_url('/user/edit.php', array('course'=>$course, 'id'=>$userid, 'cancelemailchange'=>$cancelemailchange));
+$PAGE->set_url('/user/edit.php', array('course'=>$course, 'id'=>$userid));
if (!$course = $DB->get_record('course', array('id'=>$course))) {
print_error('invalidcourseid');
}
redirect(get_login_url());
} else {
- $PAGE->set_course($course);
+ $PAGE->set_context(get_system_context());
+ $PAGE->set_pagelayout('standard');
}
// Guest can not edit
-$version = 2011100700.00; // YYYYMMDD = weekly release date of this DEV branch
+$version = 2011101200.00; // YYYYMMDD = weekly release date of this DEV branch
// RR = release increments - 00 in DEV branches
// .XX = incremental changes
-$release = '2.2dev (Build: 20111007)'; // Human-friendly version name
+$release = '2.2dev (Build: 20111012)'; // Human-friendly version name
$maturity = MATURITY_ALPHA; // this version's maturity level
$this->zend_server->setReturnResponse(true);
//TODO: the error handling in Zend Soap server is useless, XML-RPC is much, much better :-(
$this->zend_server->registerFaultException('moodle_exception');
- $this->zend_server->registerFaultException('webservice_parameter_exception'); //deprecated - kept for backward compatibility
+ $this->zend_server->registerFaultException('webservice_parameter_exception'); //deprecated since Moodle 2.2 - kept for backward compatibility
$this->zend_server->registerFaultException('invalid_parameter_exception');
$this->zend_server->registerFaultException('invalid_response_exception');
}