</PHP_SETTING>
</PHP_SETTINGS>
</MOODLE>
+ <MOODLE version="2.2" requires="1.9">
+ <UNICODE level="required">
+ <FEEDBACK>
+ <ON_ERROR message="unicoderequired" />
+ </FEEDBACK>
+ </UNICODE>
+ <DATABASE level="required">
+ <VENDOR name="mysql" version="5.0.25">
+ <FEEDBACK>
+ <ON_ERROR message="mysql416required" />
+ </FEEDBACK>
+ </VENDOR>
+ <VENDOR name="postgres" version="8.3" />
+ <VENDOR name="mssql" version="9.0" />
+ <VENDOR name="odbc_mssql" version="9.0" />
+ <VENDOR name="mssql_n" version="9.0" />
+ <VENDOR name="oracle" version="10.2" />
+ <VENDOR name="sqlite" version="2.0" />
+ </DATABASE>
+ <PHP version="5.3.2" level="required">
+ </PHP>
+ <PHP_EXTENSIONS>
+ <PHP_EXTENSION name="iconv" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="iconvrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="mbstring" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="mbstringrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="curl" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="curlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="openssl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="opensslrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="tokenizer" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="tokenizerrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xmlrpc" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="xmlrpcrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="soap" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="soaprecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="ctype" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ctyperequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="zip" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="ziprequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="gd" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="gdrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="simplexml" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="simplexmlrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="spl" level="required">
+ <FEEDBACK>
+ <ON_CHECK message="splrequired" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="pcre" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="dom" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="xml" level="required">
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="intl" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="intlrecommended" />
+ </FEEDBACK>
+ </PHP_EXTENSION>
+ <PHP_EXTENSION name="json" level="required">
+ </PHP_EXTENSION>
+ </PHP_EXTENSIONS>
+ <PHP_SETTINGS>
+ <PHP_SETTING name="memory_limit" value="40M" level="required">
+ <FEEDBACK>
+ <ON_ERROR message="settingmemorylimit" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="safe_mode" value="0" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingsafemode" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ <PHP_SETTING name="file_uploads" value="1" level="optional">
+ <FEEDBACK>
+ <ON_CHECK message="settingfileuploads" />
+ </FEEDBACK>
+ </PHP_SETTING>
+ </PHP_SETTINGS>
+ </MOODLE>
</COMPATIBILITY_MATRIX>
$ADMIN->add('appearance', $temp);
// coursecontact is the person responsible for course - usually manages enrolments, receives notification, etc.
- $temp = new admin_settingpage('coursecontact', get_string('coursecontact', 'admin'));
+ $temp = new admin_settingpage('coursecontact', get_string('courses'));
$temp->add(new admin_setting_special_coursecontact());
+ $temp->add(new admin_setting_configcheckbox('courselistshortnames',
+ get_string('courselistshortnames', 'admin'),
+ get_string('courselistshortnames_desc', 'admin'), 0));
$ADMIN->add('appearance', $temp);
$temp = new admin_settingpage('ajax', get_string('ajaxuse'));
$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);
$linkcss = $acourse->visible ? '' : ' class="dimmed" ';
echo '<tr>';
- echo '<td><a '.$linkcss.' href="view.php?id='.$acourse->id.'">'. format_string($acourse->fullname) .'</a></td>';
+ $coursename = get_course_display_name_for_list($course);
+ echo '<td><a '.$linkcss.' href="view.php?id='.$acourse->id.'">'. format_string($coursename) .'</a></td>';
if ($editingon) {
echo '<td>';
if (has_capability('moodle/course:update', $coursecontext)) {
return $cats;
}
+/**
+ * Gets the name of a course to be displayed when showing a list of courses.
+ * By default this is just $course->fullname but user can configure it. The
+ * result of this function should be passed through print_string.
+ * @param object $course Moodle course object
+ * @return string Display name of course (either fullname or short + fullname)
+ */
+function get_course_display_name_for_list($course) {
+ global $CFG;
+ if (!empty($CFG->courselistshortnames)) {
+ return $course->shortname . ' ' .$course->fullname;
+ } else {
+ return $course->fullname;
+ }
+}
+
/**
* Prints the category info in indented fashion
* This function is only used by print_whole_category_list() above
$linkcss = array('class'=>'dimmed');
}
- $courselink = html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($course->fullname), $linkcss);
+ $coursename = get_course_display_name_for_list($course);
+ $courselink = html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), format_string($coursename), $linkcss);
// print enrol info
$courseicon = '';
echo html_writer::start_tag('h3', array('class'=>'name'));
$linkhref = new moodle_url('/course/view.php', array('id'=>$course->id));
- $linktext = highlight($highlightterms, format_string($course->fullname));
+
+ $coursename = get_course_display_name_for_list($course);
+ $linktext = highlight($highlightterms, format_string($coursename));
$linkparams = array('title'=>get_string('entercourse'));
if (empty($course->visible)) {
$linkparams['class'] = 'dimmed';
if (!empty($CFG->enableavailability)) {
$cm->availablefrom = $fromform->availablefrom;
$cm->availableuntil = $fromform->availableuntil;
- // The form time is midnight, but because we want it to be
- // inclusive, set it to 23:59:59 on that day.
- if ($cm->availableuntil) {
- $cm->availableuntil = strtotime('23:59:59',
- $cm->availableuntil);
- }
$cm->showavailability = $fromform->showavailability;
condition_info::update_cm_from_form($cm,$fromform,true);
}
if(!empty($CFG->enableavailability)) {
$newcm->availablefrom = $fromform->availablefrom;
$newcm->availableuntil = $fromform->availableuntil;
- // The form time is midnight, but because we want it to be
- // inclusive, set it to 23:59:59 on that day.
- if ($newcm->availableuntil) {
- $newcm->availableuntil = strtotime('23:59:59',
- $newcm->availableuntil);
- }
$newcm->showavailability = $fromform->showavailability;
}
if (isset($fromform->showdescription)) {
// Conditions: Don't let them set dates which make no sense
if (array_key_exists('availablefrom', $data) &&
$data['availablefrom'] && $data['availableuntil'] &&
- $data['availablefrom'] > $data['availableuntil']) {
+ $data['availablefrom'] >= $data['availableuntil']) {
$errors['availablefrom'] = get_string('badavailabledates', 'condition');
}
if (!empty($CFG->enableavailability)) {
// Conditional availability
- $mform->addElement('header', 'availabilityconditionsheader', get_string('availabilityconditions', 'condition'));
- $mform->addElement('date_selector', 'availablefrom', get_string('availablefrom', 'condition'), array('optional'=>true));
+
+ // Available from/to defaults to midnight because then the display
+ // will be nicer where it tells users when they can access it (it
+ // shows only the date and not time).
+ $date = usergetdate(time());
+ $midnight = make_timestamp($date['year'], $date['mon'], $date['mday']);
+
+ // From/until controls
+ $mform->addElement('header', 'availabilityconditionsheader',
+ get_string('availabilityconditions', 'condition'));
+ $mform->addElement('date_time_selector', 'availablefrom',
+ get_string('availablefrom', 'condition'),
+ array('optional' => true, 'defaulttime' => $midnight));
$mform->addHelpButton('availablefrom', 'availablefrom', 'condition');
- $mform->addElement('date_selector', 'availableuntil', get_string('availableuntil', 'condition'), array('optional'=>true));
+ $mform->addElement('date_time_selector', 'availableuntil',
+ get_string('availableuntil', 'condition'),
+ array('optional' => true, 'defaulttime' => $midnight));
// Conditions based on grades
$gradeoptions = array();
foreach ($initials as $initial) {
$var = 'si'.$initial;
+ $othervar = $initial == 'first' ? 'silast' : 'sifirst';
+ $othervar = $$othervar != 'all' ? "&{$othervar}={$$othervar}" : '';
+
$pagingbar .= ' <div class="initialbar '.$initial.'initial">';
$pagingbar .= get_string($initial.'name').': ';
$pagingbar .= '<strong>'.get_string('all').'</strong> ';
}
else {
- $pagingbar .= '<a href="'.$link.'">'.get_string('all').'</a> ';
+ $pagingbar .= "<a href=\"{$link}{$othervar}\">".get_string('all').'</a> ';
}
foreach ($alphabet as $letter) {
$pagingbar .= '<strong>'.$letter.'</strong> ';
}
else {
- $pagingbar .= '<a href="'.$link.'&'.$var.'='.$letter.'">'.$letter.'</a> ';
+ $pagingbar .= "<a href=\"$link&$var={$letter}{$othervar}\">$letter</a> ";
}
}
$pagingbar .= '<div class="paging">';
$pagingbar .= get_string('page').': ';
+ $sistrings = array();
+ $sistrings[] = $sifirst != 'all' ? "sifirst={$sifirst}" : null;
+ $sistrings[] = $silast != 'all' ? "silast={$silast}" : null;
+ $sistring = !empty($sistrings) ? implode('&', $sistrings) : '';
+
// Display previous link
if ($start > 0) {
$pstart = max($start - COMPLETION_REPORT_PAGE, 0);
- $pagingbar .= '(<a class="previous" href="'.$link.$pstart.'">'.get_string('previous').'</a>) ';
+ $pagingbar .= "(<a class=\"previous\" href=\"{$link}{$pstart}{$sistring}\">".get_string('previous').'</a>) ';
}
// Create page links
$pagingbar .= ' '.$curpage.' ';
}
else {
- $pagingbar .= ' <a href="'.$link.$curstart.'">'.$curpage.'</a> ';
+ $pagingbar .= " <a href=\"{$link}{$curstart}{$sistring}\">$curpage</a> ";
}
$curstart += COMPLETION_REPORT_PAGE;
// Display next link
$nstart = $start + COMPLETION_REPORT_PAGE;
if ($nstart < $total) {
- $pagingbar .= ' (<a class="next" href="'.$link.$nstart.'">'.get_string('next').'</a>)';
+ $pagingbar .= " (<a class=\"next\" href=\"{$link}{$nstart}{$sistring}\">".get_string('next').'</a>)';
}
$pagingbar .= '</div>';
// User heading / sort option
print '<th scope="col" class="completion-sortchoice" style="clear: both;">';
+
+ $sistring = "&silast={$silast}&sifirst={$sifirst}";
+
if($firstnamesort) {
print
- get_string('firstname').' / <a href="./?course='.$course->id.'">'.
+ get_string('firstname')." / <a href=\"./?course={$course->id}{$sistring}\">".
get_string('lastname').'</a>';
} else {
- print '<a href="./?course='.$course->id.'&sort=firstname">'.
+ print "<a href=\"./?course={$course->id}&sort=firstname{$sistring}\">".
get_string('firstname').'</a> / '.
get_string('lastname');
}
foreach ($initials as $initial) {
$var = 'si'.$initial;
+ $othervar = $initial == 'first' ? 'silast' : 'sifirst';
+ $othervar = $$othervar != 'all' ? "&{$othervar}={$$othervar}" : '';
+
$pagingbar .= ' <div class="initialbar '.$initial.'initial">';
$pagingbar .= get_string($initial.'name').': ';
$pagingbar .= '<strong>'.get_string('all').'</strong> ';
}
else {
- $pagingbar .= '<a href="'.$link.'">'.get_string('all').'</a> ';
+ $pagingbar .= "<a href=\"{$link}{$othervar}\">".get_string('all').'</a> ';
}
foreach ($alphabet as $letter) {
$pagingbar .= '<strong>'.$letter.'</strong> ';
}
else {
- $pagingbar .= '<a href="'.$link.'&'.$var.'='.$letter.'">'.$letter.'</a> ';
+ $pagingbar .= "<a href=\"$link&$var={$letter}{$othervar}\">$letter</a> ";
}
}
$pagingbar .= '<div class="paging">';
$pagingbar .= get_string('page').': ';
+ $sistrings = array();
+ $sistrings[] = $sifirst != 'all' ? "sifirst={$sifirst}" : null;
+ $sistrings[] = $silast != 'all' ? "silast={$silast}" : null;
+ $sistring = !empty($sistrings) ? implode('&', $sistrings) : '';
+
// Display previous link
if ($start > 0) {
$pstart = max($start - COMPLETION_REPORT_PAGE, 0);
- $pagingbar .= '(<a class="previous" href="'.$link.$pstart.'">'.get_string('previous').'</a>) ';
+ $pagingbar .= "(<a class=\"previous\" href=\"{$link}{$pstart}{$sistring}\">".get_string('previous').'</a>) ';
}
// Create page links
$pagingbar .= ' '.$curpage.' ';
}
else {
- $pagingbar .= ' <a href="'.$link.$curstart.'">'.$curpage.'</a> ';
+ $pagingbar .= " <a href=\"{$link}{$curstart}{$sistring}\">$curpage</a> ";
}
$curstart += COMPLETION_REPORT_PAGE;
// Display next link
$nstart = $start + COMPLETION_REPORT_PAGE;
if ($nstart < $total) {
- $pagingbar .= ' (<a class="next" href="'.$link.$nstart.'">'.get_string('next').'</a>)';
+ $pagingbar .= " (<a class=\"next\" href=\"{$link}{$nstart}{$sistring}\">".get_string('next').'</a>)';
}
$pagingbar .= '</div>';
// User heading / sort option
print '<th scope="col" class="completion-sortchoice">';
+
+ $sistring = "&silast={$silast}&sifirst={$sifirst}";
+
if($firstnamesort) {
print
- get_string('firstname').' / <a href="./?course='.$course->id.'">'.
+ get_string('firstname')." / <a href=\"./?course={$course->id}{$sistring}\">".
get_string('lastname').'</a>';
} else {
- print '<a href="./?course='.$course->id.'&sort=firstname">'.
+ print "<a href=\"./?course={$course->id}&sort=firstname{$sistring}\">".
get_string('firstname').'</a> / '.
get_string('lastname');
}
$this->assertEqual(25, next($newsections_flipped));
$this->assertEqual(21, next($newsections_flipped));
}
+
+ function test_get_course_display_name_for_list() {
+ global $CFG;
+
+ $course = (object)array('shortname' => 'FROG101',
+ 'fullname' => 'Introduction to pond life');
+
+ // Store config value in case other tests rely on it
+ $oldcfg = $CFG->courselistshortnames;
+
+ $CFG->courselistshortnames = 0;
+ $this->assertEqual('Introduction to pond life',
+ get_course_display_name_for_list($course));
+
+ $CFG->courselistshortnames = 1;
+ $this->assertEqual('FROG101 Introduction to pond life',
+ get_course_display_name_for_list($course));
+
+ $CFG->courselistshortnames = $oldcfg;
+ }
}
$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['country'] = 'Default country';
$string['coursecontact'] = 'Course contacts';
$string['coursecontact_desc'] = 'This setting allows you to control who appears on the course description. Users need to have at least one of these roles in a course to be shown on the course description for that course.';
+$string['courselistshortnames'] = 'Display short names';
+$string['courselistshortnames_desc'] = 'Show short name as well as full name when displaying lists of courses.';
$string['coursemgmt'] = 'Add/edit courses';
$string['courseoverview'] = 'Course overview';
$string['courserequestnotify'] = 'Course request notification';
$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['requires_date'] = 'Available from {$a}.';
$string['requires_date_before'] = 'Available until {$a}.';
$string['requires_date_both'] = 'Available from {$a->from} to {$a->until}.';
+$string['requires_date_both_single_day'] = 'Available on {$a}.';
$string['requires_grade_any'] = 'Not available until you have a grade in <strong>{$a}</strong>.';
$string['requires_grade_max'] = 'Not available unless you get an appropriate score in <strong>{$a}</strong>.';
$string['requires_grade_min'] = 'Not available until you achieve a required score in <strong>{$a}</strong>.';
$string['erroroccur'] = 'An error has occurred during this process';
$string['invalidarraysize'] = 'Incorrect size of arrays in params of {$a}';
$string['invalideventdata'] = 'Incorrect eventadata submitted: {$a}';
-$string['invalidparameter'] = 'Invalid parameter value detected, execution can not continue.';
-$string['invalidresponse'] = 'Invalid response value detected, execution can not continue.';
+$string['invalidparameter'] = 'Invalid parameter value detected';
+$string['invalidresponse'] = 'Invalid response value detected';
$string['missingconfigversion'] = 'Config table does not contain version, can not continue, sorry.';
$string['modulenotexist'] = '{$a} module doesn\'t exist';
$string['morethanonerecordinfetch'] = 'Found more than one record in fetch() !';
$string['errorcodes'] = 'Error message';
$string['errorcoursecontextnotvalid'] = 'You cannot execute functions in the course context (course id:{$a->courseid}). The context error message was: {$a->message}';
$string['errorinvalidparam'] = 'The param "{$a}" is invalid.';
-$string['errorinvalidparamsapi'] = 'Invalid external api parameter';
-$string['errorinvalidparamsdesc'] = 'Invalid external api description';
-$string['errorinvalidresponseapi'] = 'Invalid external api response';
-$string['errorinvalidresponsedesc'] = 'Invalid external api response description';
-$string['errormissingkey'] = 'Missing required key in single structure: {$a}';
$string['errornotemptydefaultparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. The default can only be empty array. Check web service description.';
-$string['erroronlyarray'] = 'Only arrays accepted.';
$string['erroroptionalparamarray'] = 'The web service description parameter named \'{$a}\' is an single or multiple structure. It can not be set as VALUE_OPTIONAL. Check web service description.';
-$string['errorresponsemissingkey'] = 'Error in response - Missing following required key in a single structure: {$a}';
-$string['errorscalartype'] = 'Scalar type expected, array or object received.';
-$string['errorunexpectedkey'] = 'Unexpected keys ({$a}) detected in parameter array.';
$string['execute'] = 'Execute';
$string['executewarnign'] = 'WARNING: If you press execute your database will be modified and changes can not be reverted automatically!';
$string['externalservice'] = 'External service';
//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 ' .
}
}
- // Dates
+ // The date logic is complicated. The intention of this logic is:
+ // 1) display date without time where possible (whenever the date is
+ // midnight)
+ // 2) when the 'until' date is e.g. 00:00 on the 14th, we display it as
+ // 'until the 13th' (experience at the OU showed that students are
+ // likely to interpret 'until <date>' as 'until the end of <date>').
+ // 3) This behaviour becomes confusing for 'same-day' dates where there
+ // are some exceptions.
+ // Users in different time zones will typically not get the 'abbreviated'
+ // behaviour but it should work OK for them aside from that.
+
+ // The following cases are possible:
+ // a) From 13:05 on 14 Oct until 12:10 on 17 Oct (exact, exact)
+ // b) From 14 Oct until 12:11 on 17 Oct (midnight, exact)
+ // c) From 13:05 on 14 Oct until 17 Oct (exact, midnight 18 Oct)
+ // d) From 14 Oct until 17 Oct (midnight 14 Oct, midnight 18 Oct)
+ // e) On 14 Oct (midnight 14 Oct, midnight 15 Oct)
+ // f) From 13:05 on 14 Oct until 0:00 on 15 Oct (exact, midnight, same day)
+ // g) From 0:00 on 14 Oct until 12:05 on 14 Oct (midnight, exact, same day)
+ // h) From 13:05 on 14 Oct (exact)
+ // i) From 14 Oct (midnight)
+ // j) Until 13:05 on 14 Oct (exact)
+ // k) Until 14 Oct (midnight 15 Oct)
+
+ // Check if start and end dates are 'midnights', if so we show in short form
+ $shortfrom = self::is_midnight($this->cm->availablefrom);
+ $shortuntil = self::is_midnight($this->cm->availableuntil);
+
+ // For some checks and for display, we need the previous day for the 'until'
+ // value, if we are going to display it in short form
+ if ($this->cm->availableuntil) {
+ $daybeforeuntil = strtotime("-1 day", usergetmidnight($this->cm->availableuntil));
+ }
+
+ // Special case for if one but not both are exact and they are within a day
+ if ($this->cm->availablefrom && $this->cm->availableuntil &&
+ $shortfrom != $shortuntil && $daybeforeuntil < $this->cm->availablefrom) {
+ // Don't use abbreviated version (see examples f, g above)
+ $shortfrom = false;
+ $shortuntil = false;
+ }
+
+ // When showing short end date, the display time is the 'day before' one
+ $displayuntil = $shortuntil ? $daybeforeuntil : $this->cm->availableuntil;
+
if ($this->cm->availablefrom && $this->cm->availableuntil) {
- $information .= get_string('requires_date_both', 'condition',
- (object)array(
- 'from' => self::show_time($this->cm->availablefrom, false),
- 'until' => self::show_time($this->cm->availableuntil, true)));
+ if ($shortfrom && $shortuntil && $daybeforeuntil == $this->cm->availablefrom) {
+ $information .= get_string('requires_date_both_single_day', 'condition',
+ self::show_time($this->cm->availablefrom, true));
+ } else {
+ $information .= get_string('requires_date_both', 'condition', (object)array(
+ 'from' => self::show_time($this->cm->availablefrom, $shortfrom),
+ 'until' => self::show_time($displayuntil, $shortuntil)));
+ }
} else if ($this->cm->availablefrom) {
$information .= get_string('requires_date', 'condition',
- self::show_time($this->cm->availablefrom, false));
+ self::show_time($this->cm->availablefrom, $shortfrom));
} else if ($this->cm->availableuntil) {
$information .= get_string('requires_date_before', 'condition',
- self::show_time($this->cm->availableuntil, true));
+ self::show_time($displayuntil, $shortuntil));
}
$information = trim($information);
return $information;
}
+ /**
+ * Checks whether a given time refers exactly to midnight (in current user
+ * timezone).
+ * @param int $time Time
+ * @return bool True if time refers to midnight, false if it's some other
+ * time or if it is set to zero
+ */
+ private static function is_midnight($time) {
+ return $time && usergetmidnight($time) == $time;
+ }
+
/**
* Determines whether this particular course-module is currently available
* according to these criteria.
$available = false;
$information .= get_string('requires_date', 'condition',
- self::show_time($this->cm->availablefrom, false));
+ self::show_time($this->cm->availablefrom,
+ self::is_midnight($this->cm->availablefrom)));
}
}
}
/**
- * Shows a time either as a date (if it falls exactly on the day) or
- * a full date and time, according to user's timezone.
- *
+ * Shows a time either as a date or a full date and time, according to
+ * user's timezone.
* @param int $time Time
- * @param bool $until True if this date should be treated as the second of
- * an inclusive pair - if so the time will be shown unless date is 23:59:59.
- * Without this the date shows for 0:00:00.
+ * @param bool $dateonly If true, uses date only
* @return string Date
*/
- private function show_time($time, $until) {
- // Break down the time into fields
- $userdate = usergetdate($time);
-
- // Handle the 'inclusive' second date
- if($until) {
- $dateonly = $userdate['hours']==23 && $userdate['minutes']==59 &&
- $userdate['seconds']==59;
- } else {
- $dateonly = $userdate['hours']==0 && $userdate['minutes']==0 &&
- $userdate['seconds']==0;
- }
-
- return userdate($time, get_string(
- $dateonly ? 'strftimedate' : 'strftimedatetime', 'langconfig'));
+ private function show_time($time, $dateonly) {
+ return userdate($time,
+ get_string($dateonly ? 'strftimedate' : 'strftimedatetime', 'langconfig'));
}
/**
upgrade_main_savepoint(true, 2011100700.02);
}
+ if ($oldversion < 2011101200.01) {
+ // The conditional availability date system used to rely on dates being
+ // set to 23:59:59 for the end date, but now that exact times are
+ // supported, it uses midnight on the following day.
+
+ // The query is restricted on 'time mod 10 = 9' in order that
+ // it is safe to run this upgrade twice if something goes wrong.
+ $DB->execute('UPDATE {course_modules} SET availableuntil = availableuntil + 1 ' .
+ 'WHERE availableuntil > 0 AND ' . $DB->sql_modulo('availableuntil', 10) . ' = 9');
+
+ // Because availableuntil is stored in modinfo, we need to clear modinfo
+ // for all courses.
+ rebuild_course_cache(0, true);
+
+ // Main savepoint reached
+ upgrade_main_savepoint(true, 2011101200.01);
+ }
+
return true;
}
public static function validate_parameters(external_description $description, $params) {
if ($description instanceof external_value) {
if (is_array($params) or is_object($params)) {
- throw new invalid_parameter_exception(get_string('errorscalartype', 'webservice'));
+ throw new invalid_parameter_exception('Scalar type expected, array or object received.');
}
if ($description->type == PARAM_BOOL) {
return (bool)$params;
}
}
- return validate_param($params, $description->type, $description->allownull, get_string('errorinvalidparamsapi', 'webservice'));
+ $debuginfo = 'Invalid external api parameter: the value is "' . $params .
+ '", the server was expecting "' . $description->type . '" type';
+ return validate_param($params, $description->type, $description->allownull, $debuginfo);
} else if ($description instanceof external_single_structure) {
if (!is_array($params)) {
- throw new invalid_parameter_exception(get_string('erroronlyarray', 'webservice'));
+ throw new invalid_parameter_exception('Only arrays accepted. The bad value is: \''
+ . print_r($params, true) . '\'');
}
$result = array();
foreach ($description->keys as $key=>$subdesc) {
if (!array_key_exists($key, $params)) {
if ($subdesc->required == VALUE_REQUIRED) {
- throw new invalid_parameter_exception(get_string('errormissingkey', 'webservice', $key));
+ throw new invalid_parameter_exception('Missing required key in single structure: '. $key);
}
if ($subdesc->required == VALUE_DEFAULT) {
try {
$result[$key] = self::validate_parameters($subdesc, $subdesc->default);
} catch (invalid_parameter_exception $e) {
- throw new webservice_parameter_exception('invalidextparam',$key);
+ //we are only interested by exceptions returned by validate_param() and validate_parameters()
+ //(in order to build the path to the faulty attribut)
+ throw new invalid_parameter_exception($key." => ".$e->getMessage() . ': ' .$e->debuginfo);
}
}
} else {
try {
$result[$key] = self::validate_parameters($subdesc, $params[$key]);
} catch (invalid_parameter_exception $e) {
- //it's ok to display debug info as here the information is useful for ws client/dev
- throw new webservice_parameter_exception('invalidextparam',$key." (".$e->debuginfo.")");
+ //we are only interested by exceptions returned by validate_param() and validate_parameters()
+ //(in order to build the path to the faulty attribut)
+ throw new invalid_parameter_exception($key." => ".$e->getMessage() . ': ' .$e->debuginfo);
}
}
unset($params[$key]);
}
if (!empty($params)) {
- //list all unexpected keys
- $keys = '';
- foreach($params as $key => $value) {
- $keys .= $key . ',';
- }
- throw new invalid_parameter_exception(get_string('errorunexpectedkey', 'webservice', $keys));
+ throw new invalid_parameter_exception('Unexpected keys (' . implode(', ', array_keys($params)) . ') detected in parameter array.');
}
return $result;
} else if ($description instanceof external_multiple_structure) {
if (!is_array($params)) {
- throw new invalid_parameter_exception(get_string('erroronlyarray', 'webservice'));
+ throw new invalid_parameter_exception('Only arrays accepted. The bad value is: \''
+ . print_r($params, true) . '\'');
}
$result = array();
foreach ($params as $param) {
return $result;
} else {
- throw new invalid_parameter_exception(get_string('errorinvalidparamsdesc', 'webservice'));
+ throw new invalid_parameter_exception('Invalid external api description');
}
}
public static function clean_returnvalue(external_description $description, $response) {
if ($description instanceof external_value) {
if (is_array($response) or is_object($response)) {
- throw new invalid_response_exception(get_string('errorscalartype', 'webservice'));
+ throw new invalid_response_exception('Scalar type expected, array or object received.');
}
if ($description->type == PARAM_BOOL) {
return (bool)$response;
}
}
- return validate_param($response, $description->type, $description->allownull, get_string('errorinvalidresponseapi', 'webservice'));
+ $debuginfo = 'Invalid external api response: the value is "' . $response .
+ '", the server was expecting "' . $description->type . '" type';
+ try {
+ return validate_param($response, $description->type, $description->allownull, $debuginfo);
+ } catch (invalid_parameter_exception $e) {
+ //proper exception name, to be recursively catched to build the path to the faulty attribut
+ throw new invalid_response_exception($e->debuginfo);
+ }
} else if ($description instanceof external_single_structure) {
if (!is_array($response)) {
- throw new invalid_response_exception(get_string('erroronlyarray', 'webservice'));
+ throw new invalid_response_exception('Only arrays accepted. The bad value is: \'' .
+ print_r($response, true) . '\'');
}
$result = array();
foreach ($description->keys as $key=>$subdesc) {
if (!array_key_exists($key, $response)) {
if ($subdesc->required == VALUE_REQUIRED) {
- throw new webservice_parameter_exception('errorresponsemissingkey', $key);
+ throw new invalid_response_exception('Error in response - Missing following required key in a single structure: ' . $key);
}
if ($subdesc instanceof external_value) {
if ($subdesc->required == VALUE_DEFAULT) {
try {
$result[$key] = self::clean_returnvalue($subdesc, $subdesc->default);
- } catch (Exception $e) {
- throw new webservice_parameter_exception('invalidextresponse',$key." (".$e->debuginfo.")");
+ } catch (invalid_response_exception $e) {
+ //build the path to the faulty attribut
+ throw new invalid_response_exception($key." => ".$e->getMessage() . ': ' . $e->debuginfo);
}
}
}
} else {
try {
$result[$key] = self::clean_returnvalue($subdesc, $response[$key]);
- } catch (Exception $e) {
- //it's ok to display debug info as here the information is useful for ws client/dev
- throw new webservice_parameter_exception('invalidextresponse',$key." (".$e->debuginfo.")");
+ } catch (invalid_response_exception $e) {
+ //build the path to the faulty attribut
+ throw new invalid_response_exception($key." => ".$e->getMessage() . ': ' . $e->debuginfo);
}
}
unset($response[$key]);
} else if ($description instanceof external_multiple_structure) {
if (!is_array($response)) {
- throw new invalid_response_exception(get_string('erroronlyarray', 'webservice'));
+ throw new invalid_response_exception('Only arrays accepted. The bad value is: \'' .
+ print_r($response, true) . '\'');
}
$result = array();
foreach ($response as $param) {
return $result;
} else {
- throw new invalid_response_exception(get_string('errorinvalidresponsedesc', 'webservice'));
+ throw new invalid_response_exception('Invalid external api response description');
}
}
}
}
+ 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);
*
* startyear => integer start of range of years that can be selected
* stopyear => integer last year that can be selected
+ * defaulttime => default time value if the field is currently not set
* timezone => float/string timezone
* applydst => apply users daylight savings adjustment?
* step => step to increment minutes by
* optional => if true, show a checkbox beside the date to turn it on (or off)
*/
- var $_options = array('startyear' => 1970, 'stopyear' => 2020,
+ var $_options = array('startyear' => 1970, 'stopyear' => 2020, 'defaulttime' => 0,
'timezone' => 99, 'applydst' => true, 'step' => 5, 'optional' => false);
/**
}
$requestvalue=$value;
if ($value == 0) {
- $value = time();
+ $value = $this->_options['defaulttime'];
+ if (!$value) {
+ $value = time();
+ }
}
if (!is_array($value)) {
$currentdate = usergetdate($value);
* @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;
}
/**
/**
* Web service parameter exception class
- *
+ * @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
*/
* @param string $errorcode The name of the string from webservice.php to print
* @param string $a The name of the parameter
*/
- function __construct($errorcode=null, $a = '') {
- parent::__construct($errorcode, 'webservice', '', $a, null);
+ function __construct($errorcode=null, $a = '', $debuginfo = null) {
+ parent::__construct($errorcode, 'webservice', '', $a, $debuginfo);
}
}
Changes:
* lots of files removed
* small fix to error reporting in reflection (MDL-21460, ZF-8980)
+* SOAP and XMLRPC servers overwrite the fault() functions
$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);
}
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';
if(is_array($feedbackitems)){
$itemnr = 0;
-
+
$align = right_to_left() ? 'right' : 'left';
$helpbutton = $OUTPUT->help_icon('preview', 'feedback');
// 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 (!has_capability('mod/feedback:view', $context)) {
+ //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;
+ }
+
+ $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->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 ($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($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);
+ }
}
$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)
<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() {
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() {
$hasheading = ($PAGE->heading);
$hasnavbar = (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar());
$hasfooter = (empty($PAGE->layout_options['nofooter']));
+
$hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
$hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
+
+$showsidepre = ($hassidepre && !$PAGE->blocks->region_completely_docked('side-pre', $OUTPUT));
+$showsidepost = ($hassidepost && !$PAGE->blocks->region_completely_docked('side-post', $OUTPUT));
+
$custommenu = $OUTPUT->custom_menu();
$hascustommenu = (empty($PAGE->layout_options['nocustommenu']) && !empty($custommenu));
$bodyclasses = array();
-if ($hassidepre && !$hassidepost) {
+if ($showsidepre && !$showsidepost) {
$bodyclasses[] = 'side-pre-only';
-} else if ($hassidepost && !$hassidepre) {
+} else if ($showsidepost && !$showsidepre) {
$bodyclasses[] = 'side-post-only';
-} else if (!$hassidepost && !$hassidepre) {
+} else if (!$showsidepost && !$showsidepre) {
$bodyclasses[] = 'content-only';
}
if ($hascustommenu) {
$bodyclasses[] = 'has_custom_menu';
}
+
echo $OUTPUT->doctype() ?>
<html <?php echo $OUTPUT->htmlattributes() ?>>
<head>
<title><?php echo $PAGE->title ?></title>
<link rel="shortcut icon" href="<?php echo $OUTPUT->pix_url('favicon', 'theme')?>" />
+ <meta name="description" content="<?php p(strip_tags(format_text($SITE->summary, FORMAT_HTML))) ?>" />
<?php echo $OUTPUT->standard_head_html() ?>
</head>
#page-content #region-main-box {
float: left;
- margin-left: -[[setting:regionpostwidth]];;
+ margin-left: -[[setting:regionpostwidth]];
position: relative;
width: 200%;
right: 100%;
#page-content #region-post-box {
float: left;
- margin-left: -[[setting:regionprewidth]];;
+ margin-left: -[[setting:regionprewidth]];
width: 100%;
}
/** Only side pre **/
.side-pre-only #page-content #region-main-box {
- margin-left: 0px;
+ margin-left: 0;
}
.side-pre-only #page-content #region-post-box {
.side-pre-only #page-content #region-pre {
left: [[setting:regionprewidth]];
- width: [[setting:regionprewidth]];
+ width: [[setting:regionprewidth]];
}
.side-pre-only #page-content #region-post {
- width: 0%;
+ left: 0;
+ width: 0;
}
/** Only side post **/
.side-post-only #page-content #region-main-box {
- margin-left: 0px;
+ margin-left: 0;
}
.side-post-only #page-content #region-main {
margin-left: [[setting:regionpostwidth]];
-
}
+.side-post-only #page-content #region-pre {
+ left: 0;
+ width: 0;
+}
.side-post-only #page-content #region-post {
- left: [[setting:regionsumwidth]];
+ left: [[setting:regionpostwidth]];
width: [[setting:regionpostwidth]];
}
margin-left: [[setting:regionprewidth]];
}
+.blocks-moving.side-post-only #page-content #region-main-box {
+ float: left;
+ margin-left: -[[setting:regionpostwidth]];
+ position: relative;
+ width: 200%;
+ right: 100%;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box {
+ float: left;
+ margin-left:
+ -[[setting:regionprewidth]];
+ width: 100%;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap {
+ float: left;
+ width: 50%;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main {
+ overflow: hidden;
+ position: relative;
+ margin-left: [[setting:regionsumwidth]];
+ left: 100%;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-pre {
+ float: right;
+ position: relative;
+ left: [[setting:leftregionwidthmargin]];
+ width: [[setting:regionprewidth]];
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-post {
+ float: right;
+ position: relative;
+ left: [[setting:regiondoublepresumwidth]];
+ width: [[setting:regionpostwidth]];
+}
+
/** No blocks whatsoever **/
.content-only #page-content #region-main-box {
margin-left: 0px;
/** Path: theme pagelayout **/
-body {margin:auto 0px;width:auto; height: 100%}
-#page {width:100%; min-height: 100%;}
+body {margin:auto 0px;
+ width:auto;
+ height: 100%
+}
+#page {
+ width:100%;
+ min-height: 100%;
+}
#page-content {
clear: both;
position: relative;
- width: 100%;min-height: 100%;
+ width: 100%;
+ min-height: 100%;
}
-
#page-content #region-main-box {
float: left;
margin-left: -200px;
position: relative;
width: 200%;
- right: 100%;min-height: 100%;
+ right: 100%;
+ min-height: 100%;
}
-
#page-content #region-post-box {
float: left;
margin-left: -200px;
- width: 100%;min-height: 100%;
+ width: 100%;
+ min-height: 100%;
}
-
#page-content #region-main-wrap {
float: left;
- width: 50%;min-height: 100%;
+ width: 50%;
+ min-height: 100%;
}
-
#page-content #region-main {
position: relative;
margin-left: 400px;
- left: 100%;min-height: 100%;
+ left: 100%;
+ min-height: 100%;
}
-
#page-content #region-pre {
float: right;
position: relative;
left: 200px;
min-height: 100%;
}
-
#page-content #region-post {
float: right;
position: relative;
left: 600px;
- width: 200px;min-height: 100%;
+ width: 200px;
+ min-height: 100%;
}
-
#page-content #region-main .region-content {
overflow: hidden;
- padding: 20px 20px 20px 0;min-height: 100%;
+ padding: 20px 20px 20px 0;
+ min-height: 100%;
}
-
.pagelayout-report #page-content #region-main .region-content {
overflow: auto;
- padding-bottom:0;
- margin-bottom:20px;
+ padding-bottom: 0;
+ margin-bottom: 20px;
}
-
#page-content #region-pre .region-content,
#page-content #region-post .region-content {
overflow: hidden;
padding: 20px 10px;
min-height: 100%;
}
-
#page-footer {
clear: both;
float: left;
/** Only side pre **/
.side-pre-only #page-content #region-main-box {
- margin-left: 0px;
+ margin-left: 0;
}
-
.side-pre-only #page-content #region-main-box #region-post-box {
margin-left: -200px;
}
-
.side-pre-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main {
margin-left: 200px;
}
-
.side-pre-only #page-content #region-main-box #region-post-box #region-pre {
left: 200px;
width: 200px;
}
-
.side-pre-only #page-content #region-main-box #region-post-box #region-post {
- width: 0%;
+ width: 0;
}
/** Only side post **/
.side-post-only #page-content #region-main-box {
- margin-left: 0px;
+ margin-left: 0;
}
-
.side-post-only #page-content #region-main-box #region-post-box {
margin-left: -200px;
}
-
.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main {
margin-left: 200px;
}
-
+.side-post-only #page-content #region-main-box #region-post-box #region-pre {
+ left: 0;
+ width: 0;
+}
.side-post-only #page-content #region-main-box #region-post-box #region-post {
- left: 400px;
+ left: 200px;
width: 200px;
}
-
+.blocks-moving.side-post-only #page-content #region-main-box {
+ float: left;
+ margin-left: -200px;
+ position: relative;
+ width: 200%;
+ right: 100%;min-height: 100%;
+}
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box {
+ float: left;
+ margin-left: -200px;
+ width: 100%;min-height: 100%;
+}
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap {
+ float: left;
+ width: 50%;
+ min-height: 100%;
+}
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main {
+ position: relative;
+ margin-left: 400px;
+ left: 100%;
+ min-height: 100%;
+}
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-pre {
+ float: right;
+ position: relative;
+ width: 200px;
+ left: 200px;
+ min-height: 100%;
+}
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-post {
+ float: right;
+ position: relative;
+ left: 600px;
+ width: 200px;
+ min-height: 100%;
+}
.has_dock.side-post-only .page-middle #region-main-box #region-post-box #region-main-wrap #region-main {
margin-left: 200px;
}
/** No blocks whatsoever **/
.content-only #page-content #region-main-box {
- margin-left: 0px;
+ margin-left: 0;
}
-
.content-only #page-content #region-main-box #region-post-box {
- margin-left: 0px;
+ margin-left: 0;
}
-
.content-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main {
- margin-left: 0px;
+ margin-left: 0;
}
-
.content-only #page-content #region-main-box #region-post-box #region-pre {
- width: 0px;
+ width: 0;
}
-
.content-only #page-content #region-main-box #region-post-box #region-post {
- width: 0px;
+ width: 0;
}
\ No newline at end of file
/** Path: theme pagelayout **/
/*********************************************************************************************
-
- left column: 230px
- right column: 330px
- padding left/right column: 10px
- padding center column: 30px
-
+ column witdh: [[setting:regionwidth]]
**********************************************************************************************/
body {
width: 100%;
}
-
-
/* @end */
/* @group Pre Side Only */
width: 0%;
}
-
-
/* @end */
/* @group Post Side Only */
margin-left: [[setting:regionwidth]];
}
+.side-post-only #page-content #region-main-box #region-post-box #region-pre {
+ left: 0;
+ width: 0;
+}
+
.side-post-only #page-content #region-main-box #region-post-box #region-post {
- left: [[setting:regionwidthdouble]];
+ left: [[setting:regionwidth]];
width: [[setting:regionwidth]];
}
margin-left: 200px;
}
+.blocks-moving.side-post-only #page-content #region-main-box {
+ float: left;
+ margin-left: -[[setting:regionwidth]];
+ position: relative;
+ width: 200%;
+ right: 100%;
+}
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box {
+ float: left;
+ margin-left: -[[setting:regionwidth]];
+ width: 100%;
+ border-right: 2px solid #98bcd6;
+ background: url([[pix:theme|top_bg]]) repeat-x top #fff;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap {
+ float: left;
+ width: 50%;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-main-wrap #region-main {
+ overflow: hidden;
+ position: relative;
+ margin-left: [[setting:regionwidthdouble]];
+ left: 100%;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-pre {
+ float: right;
+ position: relative;
+ left: [[setting:leftregionwidthmargin]];
+ width: [[setting:regionwidth]];
+ background: transparent;
+}
+
+.blocks-moving.side-post-only #page-content #region-main-box #region-post-box #region-post {
+ float: right;
+ position: relative;
+ left: [[setting:rightregionwidthmargin]];
+ width: [[setting:regionwidth]];
+ background: transparent;
+}
/* @end */
width: 0px;
}
+/* @end */
+/* @pagelayout-report - overflow */
-/* @end */
.pagelayout-report #page-content #region-main {
- overflow:auto;
+ overflow: auto;
}
.pagelayout-report #page-content #region-main .region-content {
- overflow:visible;
+ overflow: visible;
}
\ No newline at end of file
-$version = 2011101200.00; // YYYYMMDD = weekly release date of this DEV branch
+$version = 2011101200.01; // YYYYMMDD = weekly release date of this DEV branch
// RR = release increments - 00 in DEV branches
// .XX = incremental changes
*/
require_once("$CFG->dirroot/webservice/lib.php");
+require_once 'Zend/Soap/Server.php';
+
+/**
+ * The Zend XMLRPC server but with a fault that returns debuginfo
+ */
+class moodle_zend_soap_server extends Zend_Soap_Server {
+
+ /**
+ * Generate a server fault
+ *
+ * Note that the arguments are reverse to those of SoapFault.
+ *
+ * Moodle note: the difference with the Zend server is that we throw a SoapFault exception
+ * with the debuginfo integrated to the exception message when DEBUG >= NORMAL
+ *
+ * If an exception is passed as the first argument, its message and code
+ * will be used to create the fault object if it has been registered via
+ * {@Link registerFaultException()}.
+ *
+ * @link http://www.w3.org/TR/soap12-part1/#faultcodes
+ * @param string|Exception $fault
+ * @param string $code SOAP Fault Codes
+ * @return SoapFault
+ */
+ public function fault($fault = null, $code = "Receiver")
+ {
+ //intercept any exceptions with debug info and transform it in Moodle exception
+ if ($fault instanceof Exception) {
+ //add the debuginfo to the exception message if debuginfo must be returned
+ if (debugging() and isset($fault->debuginfo)) {
+ $fault = new SoapFault('Receiver', $fault->getMessage() . ' | DEBUG INFO: ' . $fault->debuginfo);
+ }
+ }
+
+ return parent::fault($fault, $code);
+ }
+}
/**
* SOAP service server implementation.
if (optional_param('wsdl', 0, PARAM_BOOL)) {
parent::__construct($authmethod, 'Zend_Soap_AutoDiscover');
} else {
- parent::__construct($authmethod, 'Zend_Soap_Server');
+ parent::__construct($authmethod, 'moodle_zend_soap_server');
}
$this->wsname = 'soap';
}
$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');
+ $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');
+ //when DEBUG >= NORMAL then the thrown exceptions are "casted" into a PHP SoapFault expception
+ //in order to diplay the $debuginfo (see moodle_zend_soap_server class - MDL-29435)
+ if (debugging()) {
+ $this->zend_server->registerFaultException('SoapFault');
+ }
}
}
*/
require_once("$CFG->dirroot/webservice/lib.php");
+require_once 'Zend/XmlRpc/Server.php';
+
+/**
+ * The Zend XMLRPC server but with a fault that return debuginfo
+ */
+class moodle_zend_xmlrpc_server extends Zend_XmlRpc_Server {
+
+ /**
+ * Raise an xmlrpc server fault
+ *
+ * Moodle note: the difference with the Zend server is that we throw a plain PHP Exception
+ * with the debuginfo integrated to the exception message when DEBUG >= NORMAL
+ *
+ * @param string|Exception $fault
+ * @param int $code
+ * @return Zend_XmlRpc_Server_Fault
+ */
+ public function fault($fault = null, $code = 404)
+ {
+ //intercept any exceptions with debug info and transform it in Moodle exception
+ if ($fault instanceof Exception) {
+ //add the debuginfo to the exception message if debuginfo must be returned
+ if (debugging() and isset($fault->debuginfo)) {
+ $fault = new Exception($fault->getMessage() . ' | DEBUG INFO: ' . $fault->debuginfo, 0);
+ }
+ }
+
+ return parent::fault($fault, $code);
+ }
+}
/**
* XML-RPC service server implementation.
class webservice_xmlrpc_server extends webservice_zend_server {
/**
* Contructor
- * @param integer $authmethod authentication method one of WEBSERVICE_AUTHMETHOD_*
+ * @param integer $authmethod authentication method one of WEBSERVICE_AUTHMETHOD_*
*/
public function __construct($authmethod) {
require_once 'Zend/XmlRpc/Server.php';
- parent::__construct($authmethod, 'Zend_XmlRpc_Server');
+ parent::__construct($authmethod, 'moodle_zend_xmlrpc_server');
$this->wsname = 'xmlrpc';
}
parent::init_zend_server();
// this exception indicates request failed
Zend_XmlRpc_Server_Fault::attachFaultException('moodle_exception');
+ //when DEBUG >= NORMAL then the thrown exceptions are "casted" into a plain PHP Exception class
+ //in order to display the $debuginfo (see moodle_zend_xmlrpc_server class - MDL-29435)
+ if (debugging()) {
+ Zend_XmlRpc_Server_Fault::attachFaultException('Exception');
+ }
}
}
$client = new Zend_XmlRpc_Client($serverurl);
return $client->call($function, $params);
}
-}
\ No newline at end of file
+}