Merge branch 'MDL-33562-23' of git://github.com/danpoltawski/moodle into MOODLE_23_STABLE
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 17 Jun 2013 22:48:50 +0000 (00:48 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 17 Jun 2013 22:48:50 +0000 (00:48 +0200)
Conflicts:
version.php

65 files changed:
auth/ldap/auth.php
backup/converter/moodle1/lib.php
backup/moodle2/restore_final_task.class.php
blocks/navigation/yui/navigation/navigation.js
cohort/index.php
course/edit_form.php
course/externallib.php
course/lib.php
course/view.php
filter/urltolink/filter.php
filter/urltolink/tests/filter_test.php
grade/edit/tree/grade.php
grade/report/grader/ajax_callbacks.php
grade/report/grader/lib.php
index.php
install/lang/ca_valencia/error.php
install/lang/ca_valencia/install.php
install/lang/sr_cr/langconfig.php
install/lang/sr_lt/langconfig.php
lang/en/group.php
lang/en/hub.php
lib/adminlib.php
lib/completionlib.php
lib/csslib.php
lib/db/upgrade.php
lib/jslib.php
lib/moodlelib.php
lib/navigationlib.php
lib/portfolio/exporter.php
lib/rsslib.php
lib/tests/completionlib_advanced_test.php [new file with mode: 0644]
lib/tests/completionlib_test.php
lib/tests/environment_test.php [new file with mode: 0644]
lib/tests/navigationlib_test.php
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/book/edit.php
mod/feedback/mapcourse.php
mod/forum/lib.php
mod/imscp/locallib.php
mod/quiz/startattempt.php
mod/scorm/mod_form.php
mod/scorm/report/basic/lang/en/scormreport_basic.php
notes/edit_form.php
question/behaviour/manualgraded/db/install.php [new file with mode: 0644]
question/behaviour/manualgraded/db/upgrade.php [new file with mode: 0644]
question/behaviour/manualgraded/version.php
question/engine/lib.php
question/type/multichoice/question.php
question/type/multichoice/tests/question_multi_test.php [new file with mode: 0644]
question/type/multichoice/tests/question_single_test.php [moved from question/type/multichoice/tests/question_test.php with 56% similarity]
report/progress/lib.php
repository/lib.php
theme/image.php
theme/sky_high/pix/footer-rtl.png [new file with mode: 0644]
theme/sky_high/pix/footer.png
theme/sky_high/style/admin.css
theme/sky_high/style/pagelayout.css
theme/sky_high/style/report.css
theme/yui_combo.php
theme/yui_image.php
user/index.php
user/profile.php
user/selector/module.js
version.php

index 1a6bac8..fa7c2c5 100644 (file)
@@ -549,6 +549,8 @@ class auth_plugin_ldap extends auth_plugin_base {
                 if ($user->firstaccess == 0) {
                     $DB->set_field('user', 'firstaccess', time(), array('id'=>$user->id));
                 }
+                $euser = $DB->get_record('user', array('id' => $user->id));
+                events_trigger('user_updated', $euser);
                 return AUTH_CONFIRM_OK;
             }
         } else {
@@ -710,6 +712,8 @@ class auth_plugin_ldap extends auth_plugin_base {
                         $updateuser->auth = 'nologin';
                         $DB->update_record('user', $updateuser);
                         echo "\t"; print_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
+                        $euser = $DB->get_record('user', array('id' => $user->id));
+                        events_trigger('user_updated', $euser);
                     }
                 }
             } else {
@@ -735,6 +739,8 @@ class auth_plugin_ldap extends auth_plugin_base {
                     $updateuser->auth = $this->authtype;
                     $DB->update_record('user', $updateuser);
                     echo "\t"; print_string('auth_dbreviveduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
+                    $euser = $DB->get_record('user', array('id' => $user->id));
+                    events_trigger('user_updated', $euser);
                 }
             } else {
                 print_string('nouserentriestorevive', 'auth_ldap');
@@ -848,6 +854,8 @@ class auth_plugin_ldap extends auth_plugin_base {
 
                 $id = $DB->insert_record('user', $user);
                 echo "\t"; print_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)); echo "\n";
+                $euser = $DB->get_record('user', array('id' => $user->id));
+                events_trigger('user_created', $euser);
                 if (!empty($this->config->forcechangepassword)) {
                     set_user_preference('auth_forcepasswordchange', 1, $id);
                 }
@@ -918,6 +926,10 @@ class auth_plugin_ldap extends auth_plugin_base {
                     }
                 }
             }
+            if (!empty($updatekeys)) {
+                $euser = $DB->get_record('user', array('id' => $userid));
+                events_trigger('user_updated', $euser);
+            }
         } else {
             return false;
         }
index da6a4ec..8940251 100644 (file)
@@ -1209,12 +1209,16 @@ class moodle1_file_manager implements loggable {
      */
     public function migrate_file($sourcepath, $filepath = '/', $filename = null, $sortorder = 0, $timecreated = null, $timemodified = null) {
 
-        $sourcefullpath = $this->basepath.'/'.$sourcepath;
+        // Normalise Windows paths a bit.
+        $sourcepath = str_replace('\\', '/', $sourcepath);
 
-        if ($sourcefullpath !== clean_param($sourcefullpath, PARAM_PATH)) {
-            throw new moodle1_convert_exception('file_invalid_path', $sourcefullpath);
+        // PARAM_PATH must not be used on full OS path!
+        if ($sourcepath !== clean_param($sourcepath, PARAM_PATH)) {
+            throw new moodle1_convert_exception('file_invalid_path', $sourcepath);
         }
 
+        $sourcefullpath = $this->basepath.'/'.$sourcepath;
+
         if (!is_readable($sourcefullpath)) {
             throw new moodle1_convert_exception('file_not_readable', $sourcefullpath);
         }
index d336631..f4a2d46 100644 (file)
@@ -143,6 +143,9 @@ class restore_final_task extends restore_task {
         $rules[] = new restore_log_rule('course', 'report stats', 'report/stats/index.php?id={course}', '{course}');
         $rules[] = new restore_log_rule('course', 'view section', 'view.php?id={course}&sectionid={course_section}', '{course_section}');
 
+        // module 'grade' rules
+        $rules[] = new restore_log_rule('grade', 'update', 'report/grader/index.php?id={course}', null);
+
         // module 'user' rules
         $rules[] = new restore_log_rule('user', 'view', 'view.php?id={user}&course={course}', '{user}');
         $rules[] = new restore_log_rule('user', 'change password', 'view.php?id={user}&course={course}', '{user}');
index 2189d51..a987317 100644 (file)
@@ -340,11 +340,15 @@ BRANCH.prototype = {
     },
     /**
      * Attaches required events to the branch structure.
+     *
+     * @chainable
+     * @method wire
+     * @return {BRANCH} This function is chainable, it always returns itself.
      */
     wire : function() {
         this.node = this.node || Y.one('#'+this.get('id'));
         if (!this.node) {
-            return false;
+            return this;
         }
         if (this.get('expandable')) {
             this.event_ajaxload = this.node.on('ajaxload|click', this.ajaxLoad, this);
index 9c18bb7..cba516a 100644 (file)
@@ -77,6 +77,7 @@ $search .= html_writer::start_tag('div');
 $search .= html_writer::label(get_string('searchcohort', 'cohort').':', 'cohort_search_q');
 $search .= html_writer::empty_tag('input', array('id'=>'cohort_search_q', 'type'=>'text', 'name'=>'search', 'value'=>$searchquery));
 $search .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('search', 'cohort')));
+$search .= html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'contextid', 'value'=>$contextid));
 $search .= html_writer::end_tag('div');
 $search .= html_writer::end_tag('form');
 echo $search;
@@ -137,4 +138,4 @@ if ($manager) {
     echo $OUTPUT->single_button(new moodle_url('/cohort/edit.php', array('contextid'=>$context->id)), get_string('add'));
 }
 
-echo $OUTPUT->footer();
\ No newline at end of file
+echo $OUTPUT->footer();
index 7d22c94..37d24af 100644 (file)
@@ -130,6 +130,15 @@ class course_edit_form extends moodleform {
         if (!isset($max) || !is_numeric($max)) {
             $max = 52;
         }
+
+        // Increase the number of sections combo box values if the user has increased the number of sections
+        // using the icon on the course page beyond course 'maxsections' or course 'maxsections' has been
+        // reduced below the number of sections already set for the course on the site administration course
+        // defaults page.  This is so that the number of sections is not reduced leaving unintended orphaned
+        // activities / resources.
+        if (isset($course->numsections) && $course->numsections > $max) {
+            $max = $course->numsections;
+        }
         for ($i = 0; $i <= $max; $i++) {
             $sectionmenu[$i] = "$i";
         }
@@ -201,7 +210,7 @@ class course_edit_form extends moodleform {
         enrol_course_edit_form($mform, $course, $context);
 
 //--------------------------------------------------------------------------------
-        $mform->addElement('header','', get_string('groups', 'group'));
+        $mform->addElement('header', '', get_string('groupsettingsheader', 'group'));
 
         $choices = array();
         $choices[NOGROUPS] = get_string('groupsnone', 'group');
index 0eeb13e..1a79201 100644 (file)
@@ -678,9 +678,9 @@ class core_course_external extends external_api {
                                             "users" (int) Include users (default to 0 that is equal to no),
                                             "role_assignments" (int) Include role assignments  (default to 0 that is equal to no),
                                             "comments" (int) Include user comments  (default to 0 that is equal to no),
-                                            "completion_information" (int) Include user course completion information  (default to 0 that is equal to no),
+                                            "userscompletion" (int) Include user course completion information  (default to 0 that is equal to no),
                                             "logs" (int) Include course logs  (default to 0 that is equal to no),
-                                            "histories" (int) Include histories  (default to 0 that is equal to no)'
+                                            "grade_histories" (int) Include histories  (default to 0 that is equal to no)'
                                             ),
                                 'value' => new external_value(PARAM_RAW, 'the value for the option 1 (yes) or 0 (no)'
                             )
@@ -742,9 +742,9 @@ class core_course_external extends external_api {
             'users' => 0,
             'role_assignments' => 0,
             'comments' => 0,
-            'completion_information' => 0,
+            'userscompletion' => 0,
             'logs' => 0,
-            'histories' => 0
+            'grade_histories' => 0
         );
 
         $backupsettings = array();
index 045c9e5..057b179 100644 (file)
@@ -94,6 +94,9 @@ function make_log_url($module, $url) {
         case 'role':
             $url = '/'.$url;
             break;
+        case 'grade':
+            $url = "/grade/$url";
+            break;
         default:
             $url = "/mod/$module/$url";
             break;
index 18f0313..8a74b55 100644 (file)
 
     $PAGE->set_pagelayout('course');
     $PAGE->set_pagetype('course-view-' . $course->format);
+    $PAGE->set_other_editing_capability('moodle/course:update');
     $PAGE->set_other_editing_capability('moodle/course:manageactivities');
+    $PAGE->set_other_editing_capability('moodle/course:activityvisibility');
+    if (course_format_uses_sections($course->format)) {
+        $PAGE->set_other_editing_capability('moodle/course:sectionvisibility');
+    }
 
     if ($reset_user_allowed_editing) {
         // ugly hack
index d7ca920..9d01e8d 100644 (file)
@@ -122,19 +122,25 @@ class filter_urltolink extends moodle_text_filter {
             $unicoderegexp = @preg_match('/\pL/u', 'a'); // This will fail silently, returning false,
         }
 
-        //todo: MDL-21296 - use of unicode modifiers may cause a timeout
-        if ($unicoderegexp) { //We can use unicode modifiers
-            $text = preg_replace('#(?<!=["\'])(((http(s?))://)(((([\pLl0-9]([\pLl0-9]|-)*[\pLl0-9]|[\pLl0-9])\.)+([\pLl]([\pLl0-9]|-)*[\pLl0-9]|[\pLl]))|(([0-9]{1,3}\.){3}[0-9]{1,3}))(:[\pL0-9]*)?(/([\pLl0-9\.!$&\'\(\)*+,;=_~:@-]|%[a-fA-F0-9]{2})*)*(\?([\pLl0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\#[\pLl0-9\.!$&\'\(\)*+,;=_~:@/?-]*)?)(?<![,.;])#iu',
-                                 '<a href="\\1" class="_blanktarget">\\1</a>', $text);
-            $text = preg_replace('#(?<!=["\']|//)((www\.([\pLl0-9]([\pLl0-9]|-)*[\pLl0-9]|[\pLl0-9])\.)+([\pLl]([\pLl0-9]|-)*[\pLl0-9]|[\pLl])(:[\pL0-9]*)?(/([\pLl0-9\.!$&\'\(\)*+,;=_~:@-]|%[a-fA-F0-9]{2})*)*(\?([\pLl0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\#[\pLl0-9\.!$&\'\(\)*+,;=_~:@/?-]*)?)(?<![,.;])#iu',
-                                 '<a href="http://\\1" class="_blanktarget">\\1</a>', $text);
-        } else { //We cannot use unicode modifiers
-            $text = preg_replace('#(?<!=["\'])(((http(s?))://)(((([a-z0-9]([a-z0-9]|-)*[a-z0-9]|[a-z0-9])\.)+([a-z]([a-z0-9]|-)*[a-z0-9]|[a-z]))|(([0-9]{1,3}\.){3}[0-9]{1,3}))(:[a-zA-Z0-9]*)?(/([a-z0-9\.!$&\'\(\)*+,;=_~:@-]|%[a-f0-9]{2})*)*(\?([a-z0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\#[a-z0-9\.!$&\'\(\)*+,;=_~:@/?-]*)?)(?<![,.;])#i',
-                                 '<a href="\\1" class="_blanktarget">\\1</a>', $text);
-            $text = preg_replace('#(?<!=["\']|//)((www\.([a-z0-9]([a-z0-9]|-)*[a-z0-9]|[a-z0-9])\.)+([a-z]([a-z0-9]|-)*[a-z0-9]|[a-z])(:[a-zA-Z0-9]*)?(/([a-z0-9\.!$&\'\(\)*+,;=_~:@-]|%[a-f0-9]{2})*)*(\?([a-z0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)?(\#[a-z0-9\.!$&\'\(\)*+,;=_~:@/?-]*)?)(?<![,.;])#i',
-                                 '<a href="http://\\1" class="_blanktarget">\\1</a>', $text);
+        // TODO MDL-21296 - use of unicode modifiers may cause a timeout
+        $domainsegment = '(?:[\pLl0-9][\pLl0-9-]*[\pLl0-9]|[\pLl0-9])';
+        $numericip = '(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})';
+        $port = '(?::\d*)';
+        $pathchar = '(?:[\pL0-9\.!$&\'\(\)*+,;=_~:@-]|%[a-f0-9]{2})';
+        $path = "(?:/$pathchar*)*";
+        $querystring = '(?:\?(?:[\pL0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)';
+        $fragment = '(?:\#(?:[\pL0-9\.!$&\'\(\)*+,;=_~:@/?-]|%[a-fA-F0-9]{2})*)';
+
+        $regex = "(?<!=[\"'])(?:http(s)?://|(www\.))((?:$domainsegment\.)+$domainsegment|$numericip)" .
+                "($port?$path$querystring?$fragment?)(?<![]),.;])";
+        if ($unicoderegexp) {
+            $regex = '#' . $regex . '#ui';
+        } else {
+            $regex = '#' . preg_replace(array('\pLl', '\PL'), 'a-z', $regex) . '#i';
         }
 
+        $text = preg_replace($regex, '<a href="http$1://$2$3$4" class="_blanktarget">$0</a>', $text);
+
         if (!empty($ignoretags)) {
             $ignoretags = array_reverse($ignoretags); /// Reversed so "progressive" str_replace() will solve some nesting problems.
             $text = str_replace(array_keys($ignoretags),$ignoretags,$text);
index 95109d1..70fb3f1 100644 (file)
@@ -44,7 +44,7 @@ class filter_urltolink_testcase extends basic_testcase {
             '$1<a href="http://www.$2$3" target="_blank">www.$2$3</a>', $text);
     }
 
-    function test_convert_urls_into_links() {
+    function get_convert_urls_into_links_test_cases() {
         $texts = array (
             //just a url
             'http://moodle.org - URL' => '<a href="http://moodle.org" class="_blanktarget">http://moodle.org</a> - URL',
@@ -57,13 +57,26 @@ class filter_urltolink_testcase extends basic_testcase {
             'URL: https://moodle.org/s/i=1&j=2' => 'URL: <a href="https://moodle.org/s/i=1&j=2" class="_blanktarget">https://moodle.org/s/i=1&j=2</a>',
             //url with port and params
             'URL: http://moodle.org:8080/s/i=1' => 'URL: <a href="http://moodle.org:8080/s/i=1" class="_blanktarget">http://moodle.org:8080/s/i=1</a>',
-            //url in brackets
+            // URL with complex fragment.
+            'Most voted issues: https://tracker.moodle.org/browse/MDL#selectedTab=com.atlassian.jira.plugin.system.project%3Apopularissues-panel' => 'Most voted issues: <a href="https://tracker.moodle.org/browse/MDL#selectedTab=com.atlassian.jira.plugin.system.project%3Apopularissues-panel" class="_blanktarget">https://tracker.moodle.org/browse/MDL#selectedTab=com.atlassian.jira.plugin.system.project%3Apopularissues-panel</a>',
+            // Domain with more parts
+            'URL: www.bbc.co.uk.' => 'URL: <a href="http://www.bbc.co.uk" class="_blanktarget">www.bbc.co.uk</a>.',
+            // URL in brackets.
             '(http://moodle.org) - URL' => '(<a href="http://moodle.org" class="_blanktarget">http://moodle.org</a>) - URL',
             '(www.moodle.org) - URL' => '(<a href="http://www.moodle.org" class="_blanktarget">www.moodle.org</a>) - URL',
-            //url in square brackets
+            // URL in brackets with a path.
+            '(http://example.com/index.html) - URL' => '(<a href="http://example.com/index.html" class="_blanktarget">http://example.com/index.html</a>) - URL',
+            '(www.example.com/index.html) - URL' => '(<a href="http://www.example.com/index.html" class="_blanktarget">www.example.com/index.html</a>) - URL',
+            // URL in brackets with anchor.
+            '(http://moodle.org/main#anchor) - URL' => '(<a href="http://moodle.org/main#anchor" class="_blanktarget">http://moodle.org/main#anchor</a>) - URL',
+            '(www.moodle.org/main#anchor) - URL' => '(<a href="http://www.moodle.org/main#anchor" class="_blanktarget">www.moodle.org/main#anchor</a>) - URL',
+            // URL in square brackets.
             '[http://moodle.org] - URL' => '[<a href="http://moodle.org" class="_blanktarget">http://moodle.org</a>] - URL',
             '[www.moodle.org] - URL' => '[<a href="http://www.moodle.org" class="_blanktarget">www.moodle.org</a>] - URL',
-            //url in brackets with anchor
+            // URL in square brackets with a path.
+            '[http://example.com/index.html] - URL' => '[<a href="http://example.com/index.html" class="_blanktarget">http://example.com/index.html</a>] - URL',
+            '[www.example.com/index.html] - URL' => '[<a href="http://www.example.com/index.html" class="_blanktarget">www.example.com/index.html</a>] - URL',
+            // URL in square brackets with anchor.
             '[http://moodle.org/main#anchor] - URL' => '[<a href="http://moodle.org/main#anchor" class="_blanktarget">http://moodle.org/main#anchor</a>] - URL',
             '[www.moodle.org/main#anchor] - URL' => '[<a href="http://www.moodle.org/main#anchor" class="_blanktarget">www.moodle.org/main#anchor</a>] - URL',
             //brackets within the url
@@ -71,7 +84,8 @@ class filter_urltolink_testcase extends basic_testcase {
             'URL: www.cc.org/url_(withpar)_go/?i=2' => 'URL: <a href="http://www.cc.org/url_(withpar)_go/?i=2" class="_blanktarget">www.cc.org/url_(withpar)_go/?i=2</a>',
             'URL: http://cc.org/url_(with)_(par)_go/?i=2' => 'URL: <a href="http://cc.org/url_(with)_(par)_go/?i=2" class="_blanktarget">http://cc.org/url_(with)_(par)_go/?i=2</a>',
             'URL: www.cc.org/url_(with)_(par)_go/?i=2' => 'URL: <a href="http://www.cc.org/url_(with)_(par)_go/?i=2" class="_blanktarget">www.cc.org/url_(with)_(par)_go/?i=2</a>',
-            'http://en.wikipedia.org/wiki/Slash_(punctuation)'=>'<a href="http://en.wikipedia.org/wiki/Slash_(punctuation)" class="_blanktarget">http://en.wikipedia.org/wiki/Slash_(punctuation)</a>',
+            // URL legitimately ending in a bracket. Commented out as part of MDL-22390. See next tests for work-arounds.
+            // 'http://en.wikipedia.org/wiki/Slash_(punctuation)'=>'<a href="http://en.wikipedia.org/wiki/Slash_(punctuation)" class="_blanktarget">http://en.wikipedia.org/wiki/Slash_(punctuation)</a>',
             'http://en.wikipedia.org/wiki/%28#Parentheses_.28_.29 - URL' => '<a href="http://en.wikipedia.org/wiki/%28#Parentheses_.28_.29" class="_blanktarget">http://en.wikipedia.org/wiki/%28#Parentheses_.28_.29</a> - URL',
             'http://en.wikipedia.org/wiki/(#Parentheses_.28_.29 - URL' => '<a href="http://en.wikipedia.org/wiki/(#Parentheses_.28_.29" class="_blanktarget">http://en.wikipedia.org/wiki/(#Parentheses_.28_.29</a> - URL',
             //escaped brackets in url
@@ -94,8 +108,6 @@ class filter_urltolink_testcase extends basic_testcase {
             'URL: www.moodle.org?u=1.23' => 'URL: <a href="http://www.moodle.org?u=1.23" class="_blanktarget">www.moodle.org?u=1.23</a>',
             //escaped space in url
             'URL: www.moodle.org?u=test+param&' => 'URL: <a href="http://www.moodle.org?u=test+param&" class="_blanktarget">www.moodle.org?u=test+param&</a>',
-            //odd characters in url param
-            'URL: www.moodle.org?param=:)' => 'URL: <a href="http://www.moodle.org?param=:)" class="_blanktarget">www.moodle.org?param=:)</a>',
             //multiple urls
             'URL: http://moodle.org www.moodle.org'
             => 'URL: <a href="http://moodle.org" class="_blanktarget">http://moodle.org</a> <a href="http://www.moodle.org" class="_blanktarget">www.moodle.org</a>',
@@ -151,17 +163,25 @@ class filter_urltolink_testcase extends basic_testcase {
             //'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN http://www.w3.org/TR/html4/strict.dtd">'=>'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN http://www.w3.org/TR/html4/strict.dtd">'
         );
 
-        $testablefilter = new testable_filter_urltolink();
-
+        $data = array();
         foreach ($texts as $text => $correctresult) {
-            $msg = "Testing text: ". str_replace('%', '%%', $text) . ": %s"; // Escape original '%' so sprintf() wont get confused
+            $data[] = array($text, $correctresult);
+        }
+        return $data;
+    }
 
-            $testablefilter->convert_urls_into_links($text);
+    /**
+     * @dataProvider get_convert_urls_into_links_test_cases
+     */
+    function test_convert_urls_into_links($text, $correctresult) {
+        $testablefilter = new testable_filter_urltolink();
+        $testablefilter->convert_urls_into_links($text);
+        $this->assertEquals($correctresult, $text);
+    }
 
-            $this->assertEquals($text, $correctresult, $msg);
-        }
+    function test_convert_urls_into_links_performance() {
+        $testablefilter = new testable_filter_urltolink();
 
-        //performance testing
         $reps = 1000;
         $text = file_get_contents(__DIR__ . '/fixtures/sample.txt');
         $time_start = microtime(true);
index 2d61e68..14de14b 100644 (file)
@@ -200,6 +200,18 @@ if ($mform->is_cancelled()) {
         $data->feedback       = $old_grade_grade->feedback;
         $data->feedbackformat = $old_grade_grade->feedbackformat;
     }
+
+    // Only log a grade override if they actually changed the student grade.
+    if ($data->finalgrade != $old_grade_grade->finalgrade) {
+        $url = '/report/grader/index.php?id=' . $course->id;
+
+        $user = $DB->get_record('user', array('id'=>$data->userid), '*', MUST_EXIST);
+        $fullname = fullname($user);
+
+        $info = "{$grade_item->itemname}: $fullname";
+        add_to_log($course->id, 'grade', 'update', $url, $info);
+    }
+
     // update final grade or feedback
     // when we set override grade the first time, it happens here
     $grade_item->update_final_grade($data->userid, $data->finalgrade, 'editgrade', $data->feedback, $data->feedbackformat);
@@ -213,6 +225,19 @@ if ($mform->is_cancelled()) {
             $data->overridden = 0; // checkbox unticked
         }
         $grade_grade->set_overridden($data->overridden);
+
+        if ($data->overridden == 0 && $data->overridden != $old_grade_grade->overridden) {
+            // Log removing an override.
+            // The addition of an override is logged above.
+            // One or the other will happen but never both.
+            $url = '/report/grader/index.php?id=' . $course->id;
+
+            $user = $DB->get_record('user', array('id'=>$data->userid), '*', MUST_EXIST);
+            $fullname = fullname($user);
+
+            $info = "{$grade_item->itemname}: $fullname";
+            add_to_log($course->id, 'grade', 'update', $url, $info);
+        }
     }
 
     if (has_capability('moodle/grade:manage', $context) or has_capability('moodle/grade:hide', $context)) {
index 6c12063..c84e2be 100644 (file)
@@ -118,6 +118,14 @@ switch ($action) {
                 echo json_encode($json_object);
                 die();
             } else {
+                $url = '/report/grader/index.php?id=' . $course->id;
+
+                $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+                $fullname = fullname($user);
+
+                $info = "{$grade_item->itemname}: $fullname";
+                add_to_log($course->id, 'grade', 'update', $url, $info);
+
                 $json_object->gradevalue = $finalvalue;
 
                 if ($grade_item->update_final_grade($userid, $finalgrade, 'gradebook', $feedback, FORMAT_MOODLE)) {
index 2c653a8..7b09cde 100644 (file)
@@ -297,6 +297,12 @@ class grade_report_grader extends grade_report {
                         }
                     }
 
+                    $url = '/report/grader/index.php?id=' . $this->course->id;
+                    $fullname = fullname($this->users[$userid]);
+
+                    $info = "{$gradeitem->itemname}: $fullname";
+                    add_to_log($this->course->id, 'grade', 'update', $url, $info);
+
                     $gradeitem->update_final_grade($userid, $finalgrade, 'gradebook', $feedback, FORMAT_MOODLE);
 
                     // We can update feedback without reloading the grade item as it doesn't affect grade calculations
index 087a73a..c765322 100644 (file)
--- a/index.php
+++ b/index.php
@@ -40,6 +40,9 @@
     }
     $PAGE->set_url('/', $urlparams);
     $PAGE->set_course($SITE);
+    $PAGE->set_other_editing_capability('moodle/course:update');
+    $PAGE->set_other_editing_capability('moodle/course:manageactivities');
+    $PAGE->set_other_editing_capability('moodle/course:activityvisibility');
 
     // Prevent caching of this page to stop confusion when changing page after making AJAX changes
     $PAGE->set_cacheable(false);
@@ -89,7 +92,6 @@
     }
 
     $PAGE->set_pagetype('site-index');
-    $PAGE->set_other_editing_capability('moodle/course:manageactivities');
     $PAGE->set_docs_path('');
     $PAGE->set_pagelayout('frontpage');
     $editing = $PAGE->user_is_editing();
 
             echo format_text($summarytext, $section->summaryformat, $summaryformatoptions);
 
-            if ($editing) {
+            if ($editing && has_capability('moodle/course:update', $context)) {
                 $streditsummary = get_string('editsummary');
                 echo "<a title=\"$streditsummary\" ".
                      " href=\"course/editsection.php?id=$section->id\"><img src=\"" . $OUTPUT->pix_url('t/edit') . "\" ".
index 9df0f04..3301110 100644 (file)
@@ -30,5 +30,5 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['cannotsavemd5file'] = 'No s\'ha pogut alçar el fitxer md5';
-$string['cannotsavezipfile'] = 'No s\'ha pogut alçar el fitxer zip';
+$string['cannotsavemd5file'] = 'No s\'ha pogut guardar el fitxer md5';
+$string['cannotsavezipfile'] = 'No s\'ha pogut guardar el fitxer zip';
index d8be051..ec61cf9 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$string['langdownloaderror'] = 'Dissortadament l\'idioma "{$a}" no està instal·lat. La instal·lació prosseguirà en anglés.';
+$string['clialreadyconfigured'] = 'El fitxer config.php ja existeix, feu servir dmin/cli/install_database.php si voleu instal·lar este lloc web.';
+$string['clialreadyinstalled'] = 'El fitxer config.php ja existeix, feu servir admin/cli/upgrade.php si voleu actualitzar este lloc web.';
+$string['cliinstallheader'] = 'Programa d\'instal·lació de línia d\'ordes de Moodle {$a}';
+$string['langdownloaderror'] = 'Dissortadament l\'idioma "{$a}" no es pot baixar. La instal·lació prosseguirà en anglés.';
 $string['memorylimithelp'] = '<p>El límit de memòria del PHP del vostre servidor actualment està definit en {$a}.</p>
 
 <p>Això pot causar que Moodle tinga problemes de memòria més avant, especialment si teniu molts mòduls habilitats i/o molts usuaris.</p>
@@ -41,14 +44,20 @@ $string['memorylimithelp'] = '<p>El límit de memòria del PHP del vostre servid
 <li>Si teniu accés al fitxer php.ini, podeu canviar el paràmetre <b>memory_limit</b> a 40 MB. Si no hi teniu accés podeu demanar al vostre administrador que ho faça ell.</li>
 <li>En alguns servidors PHP podeu crear un fitxer .htaccess dins del directori de Moodle amb esta línia:
 <p><blockquote>php_value memory_limit 40M</blockquote></p>
-<p>Tanmateix, en alguns servidors això farà que no funcioni <b>cap</b> pàgina PHP (es visualitzaran errors) en el qual cas hauríeu de suprimir el fitxer .htaccess.</p></li>
+<p>Tanmateix, en alguns servidors això farà que no funcione <b>cap</b> pàgina PHP (es visualitzaran errors) en el qual cas hauríeu de suprimir el fitxer .htaccess.</p></li>
 </ol>';
-$string['pathssubdataroot'] = 'Necessiteu un espai on Moodle puga alçar els fitxers penjats. Este directori hauria de tindre permisos de lectura I ESCRIPTURA per a l\'usuari del servidor web (normalment \'nobody\' o \'apache\'), però no cal que siga accessible directament via web. L\'instal·lador provarà de crear-lo si no existeix.';
-$string['phpversionhelp'] = '<p>Moodle necessita la versió de PHP 4.1.0 o posterior.</p>
+$string['pathssubadmindir'] = 'Alguns serveis d\'allotjament web (pocs) utilitzen un URL especial /admin p. ex. per a accedir a un tauler de control o quelcom paregut. Malauradament això entra en conflicte amb la ubicació estàndard de les pàgines d\'administració de Moodle. Podeu arreglar este problema canviant el nom del directori d\'administració de Moodle en la vostra instal·lació i posant el nou nom ací. Per exemple <em>moodleadmin</em>. Això modificarà els enllaços d\'administració de Moodle.';
+$string['pathssubdataroot'] = 'Necessiteu un espai on Moodle puga guardar els fitxers penjats. Este directori hauria de tindre permisos de lectura I ESCRIPTURA per a l\'usuari del servidor web (normalment \'nobody\' o \'apache\'), però no cal que siga accessible directament via web. L\'instal·lador provarà de crear-lo si no existeix.';
+$string['pathssubwwwroot'] = 'L\'adreça web completa on s\'accedirà a Moodle.
+No és possible accedir a Moodle en diferents adreces.
+Si el vostre lloc té múltiples adreces públiques haureu de configurar redireccions permanents per a totes excepte esta.
+Si el vostre lloc és accessible tant des d\'Internet com des d\'una intranet, utilitzeu ací l\'adreça pública i configureu el DNS de manera que els usuaris de la intranet puguen utilitzar també l\'adreça pública.
+Si l\'adreça no és correcta, canvieu l\'URL en el vostre navegador per reiniciar la instal·lació amb un altre valor.';
+$string['phpversionhelp'] = '<p>Moodle necessita una versió de PHP 4.3.0 o 5.1.0 (les versions 5.0.x tenien uns quants problemes coneguts).</p>
 <p>A hores d\'ara esteu utilitzant la versió {$a}.</p>
-<p>Vos caldrà actualitzar el PHP o traslladar Moodle a un ordinador amb una versió de PHP més recent.</p>';
-$string['welcomep20'] = 'Esteu veient esta pàgina perquè heu instal·lat amb èxit i heu executat el paquet <strong>{$a->packname} {$a->packversion}</strong>. Felicitacions!';
-$string['welcomep30'] = 'Esta versió de <strong>{$a->installername}</strong> inclou les aplicacions necessàries per crear un entorn en el qual funcioni <strong>Moodle</strong>:';
+<p>Vos cal actualitzar el PHP o traslladar Moodle a un ordinador amb una versió de PHP més recent.<br />(Si esteu utilitzant la versió 5.0.x, alternativament també podríeu tornar arrere a la 4.4.x)</p>';
+$string['welcomep20'] = 'Esteu veient esta pàgina perquè heu instal·lat amb èxit i heu executat el paquet <strong>{$a->packname} {$a->packversion}</strong>. Felicitacions.';
+$string['welcomep30'] = 'Esta versió de <strong>{$a->installername}</strong> inclou les aplicacions necessàries per crear un entorn en el qual funcione <strong>Moodle</strong>:';
 $string['welcomep50'] = 'L\'ús de totes les aplicacions d\'este paquet és governat per les seues llicències respectives. El paquet <strong>{$a->installername}</strong> complet és
 <a href="http://www.opensource.org/docs/definition_plain.html">codi font obert</a> i es distribueix
 sota llicència <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a>.';
index 9e1d4de..1ede2d0 100644 (file)
@@ -30,5 +30,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['parentlanguage'] = 'en';
 $string['thisdirection'] = 'ltr';
 $string['thislanguage'] = 'Српски';
index c11ac67..01b82fc 100644 (file)
@@ -30,5 +30,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
+$string['parentlanguage'] = 'en';
 $string['thisdirection'] = 'ltr';
 $string['thislanguage'] = 'Srpski';
index 1505986..1c12d4f 100644 (file)
@@ -107,6 +107,7 @@ $string['groupnameexists'] = 'The group name \'{$a}\' already exists in this cou
 $string['groupnotamember'] = 'Sorry, you are not a member of that group';
 $string['groups'] = 'Groups';
 $string['groupscount'] = 'Groups ({$a})';
+$string['groupsettingsheader'] = 'Groups';
 $string['groupsgroupings'] = 'Groups &amp; groupings';
 $string['groupsinselectedgrouping'] = 'Groups in:';
 $string['groupsnone'] = 'No groups';
index 81e0c1e..9578f10 100644 (file)
@@ -152,7 +152,6 @@ $string['publisheremail_help'] = 'The publisher email address allows the hub adm
 $string['publishername'] = 'Publisher';
 $string['publishername_help'] = 'The publisher is the person or organisation that is the official publisher of the course.  Unless you are publishing it on behalf of someone else, it will usually be you.';
 $string['publishon'] = 'Publish on';
-$string['publishonmoodleorg'] = 'Publish on MOOCH';
 $string['publishonspecifichub'] = 'Publish on another Hub';
 $string['questionsnumber'] = 'Number of questions ({$a})';
 $string['registeredcourses'] = 'Registered courses';
@@ -190,7 +189,6 @@ $string['share'] = 'Share this course for people to download';
 $string['shared'] = 'Shared';
 $string['shareon'] = 'Upload this course to {$a}';
 $string['shareonhub'] = 'Upload this course to a hub';
-$string['shareonmoodleorg'] = 'Upload this course to MOOCH';
 $string['sharepublication_help'] = 'Uploading this course to a community hub server will enable people to download it and install it on their own Moodle sites.';
 $string['siteadmin'] = 'Administrator';
 $string['siteadmin_help'] = 'The full name of the site administrator.';
index fa7adbe..cdfca8f 100644 (file)
@@ -3842,7 +3842,7 @@ class admin_setting_question_behaviour extends admin_setting_configselect {
     public function load_choices() {
         global $CFG;
         require_once($CFG->dirroot . '/question/engine/lib.php');
-        $this->choices = question_engine::get_archetypal_behaviours();
+        $this->choices = question_engine::get_behaviour_options('');
         return true;
     }
 }
index b293c5c..55acc20 100644 (file)
@@ -976,40 +976,36 @@ class completion_info {
         }
     }
 
+     /**
+     * Return whether or not the course has activities with completion enabled.
+     *
+     * @return boolean true when there is at least one activity with completion enabled.
+     */
+    public function has_activities() {
+        $modinfo = get_fast_modinfo($this->course);
+        foreach ($modinfo->get_cms() as $cm) {
+            if ($cm->completion != COMPLETION_TRACKING_NONE) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Obtains a list of activities for which completion is enabled on the
      * course. The list is ordered by the section order of those activities.
      *
-     * @param array $modinfo For unit testing only, supply the value
-     *   here. Otherwise the method calls get_fast_modinfo
      * @return array Array from $cmid => $cm of all activities with completion enabled,
      *   empty array if none
      */
-    public function get_activities($modinfo=null) {
-        global $DB;
-
-        // Obtain those activities which have completion turned on
-        $withcompletion = $DB->get_records_select('course_modules', 'course='.$this->course->id.
-          ' AND completion<>'.COMPLETION_TRACKING_NONE);
-        if (!$withcompletion) {
-            return array();
-        }
-
-        // Use modinfo to get section order and also add in names
-        if (empty($modinfo)) {
-            $modinfo = get_fast_modinfo($this->course);
-        }
+    public function get_activities() {
+        $modinfo = get_fast_modinfo($this->course);
         $result = array();
-        foreach ($modinfo->sections as $sectioncms) {
-            foreach ($sectioncms as $cmid) {
-                if (array_key_exists($cmid, $withcompletion)) {
-                    $result[$cmid] = $withcompletion[$cmid];
-                    $result[$cmid]->modname = $modinfo->cms[$cmid]->modname;
-                    $result[$cmid]->name    = $modinfo->cms[$cmid]->name;
-                }
+        foreach ($modinfo->get_cms() as $cm) {
+            if ($cm->completion != COMPLETION_TRACKING_NONE) {
+                $result[$cm->id] = $cm;
             }
         }
-
         return $result;
     }
 
index 79f10f6..8d6de0f 100644 (file)
@@ -124,7 +124,7 @@ function css_send_ie_css($themename, $rev, $etag, $slasharguments) {
         $css .= "\n@import url($relroot/styles.php?theme=$themename&rev=$rev&type=theme);";
     }
 
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     header('Content-Disposition: inline; filename="styles.php"');
     header('Last-Modified: '. gmdate('D, d M Y H:i:s', time()) .' GMT');
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
@@ -150,7 +150,7 @@ function css_send_ie_css($themename, $rev, $etag, $slasharguments) {
 function css_send_cached_css($csspath, $etag) {
     $lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
 
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     header('Content-Disposition: inline; filename="styles.php"');
     header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($csspath)) .' GMT');
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
@@ -208,7 +208,7 @@ function css_send_unmodified($lastmodified, $etag) {
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
     header('Cache-Control: public, max-age='.$lifetime);
     header('Content-Type: text/css; charset=utf-8');
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     if ($lastmodified) {
         header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
     }
index ae90511..73d48f8 100644 (file)
@@ -1187,13 +1187,13 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012062507.01);
     }
 
-    if ($oldversion < 2012062507.04) {
+    if ($oldversion < 2012062507.06) {
         // Clean up old tokens which haven't been deleted.
         $DB->execute("DELETE FROM {user_private_key} WHERE NOT EXISTS
                          (SELECT 'x' FROM {user} WHERE deleted = 0 AND id = userid)");
 
         // Main savepoint reached.
-        upgrade_main_savepoint(true, 2012062507.04);
+        upgrade_main_savepoint(true, 2012062507.06);
     }
 
     return true;
index ee905f1..cef5c98 100644 (file)
@@ -36,7 +36,7 @@ function js_send_cached($jspath, $etag, $filename = 'javascript.php') {
 
     $lifetime = 60*60*24*60; // 60 days only - the revision may get incremented quite often
 
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     header('Content-Disposition: inline; filename="'.$filename.'"');
     header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($jspath)) .' GMT');
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
@@ -86,7 +86,7 @@ function js_send_unmodified($lastmodified, $etag) {
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
     header('Cache-Control: public, max-age='.$lifetime);
     header('Content-Type: application/javascript; charset=utf-8');
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     if ($lastmodified) {
         header('Last-Modified: '. gmdate('D, d M Y H:i:s', $lastmodified) .' GMT');
     }
index 62f5d26..a1f8a0a 100644 (file)
@@ -10346,6 +10346,11 @@ function get_performance_info() {
     $info['html'] .= '<span class="included">Included '.$info['includecount'].' files</span> ';
     $info['txt']  .= 'includecount: '.$info['includecount'].' ';
 
+    if (!empty($CFG->early_install_lang) or empty($PAGE)) {
+        // We can not track more performance before installation or before PAGE init, sorry.
+        return $info;
+    }
+
     $filtermanager = filter_manager::instance();
     if (method_exists($filtermanager, 'get_performance_summary')) {
         list($filterinfo, $nicenames) = $filtermanager->get_performance_summary();
index 72ab369..37b6669 100644 (file)
@@ -889,8 +889,9 @@ class navigation_node_collection implements IteratorAggregate {
         $child = $this->get($key, $type);
         if ($child !== false) {
             foreach ($this->collection as $colkey => $node) {
-                if ($node->key == $key && $node->type == $type) {
+                if ($node->key === $key && $node->type == $type) {
                     unset($this->collection[$colkey]);
+                    $this->collection = array_values($this->collection);
                     break;
                 }
             }
@@ -3582,7 +3583,7 @@ class settings_navigation extends navigation_node {
             $coursenode->force_open();
         }
 
-        if (has_capability('moodle/course:update', $coursecontext)) {
+        if ($this->page->user_allowed_editing()) {
             // Add the turn on/off settings
 
             if ($this->page->url->compare(new moodle_url('/course/view.php'), URL_MATCH_BASE)) {
@@ -3605,8 +3606,9 @@ class settings_navigation extends navigation_node {
             $coursenode->add($editstring, $editurl, self::TYPE_SETTING, null, null, new pix_icon('i/edit', ''));
 
             // Add the module chooser toggle
-            $modchoosertoggleurl = clone($baseurl);
-            if ($this->page->user_is_editing() && course_ajax_enabled($course)) {
+            if ($this->page->user_is_editing() && has_capability('moodle/course:manageactivities', $coursecontext)
+                    && course_ajax_enabled($course)) {
+                $modchoosertoggleurl = clone($baseurl);
                 if ($usemodchooser = get_user_preferences('usemodchooser', $CFG->modchooserdefault)) {
                     $modchoosertogglestring = get_string('modchooserdisable', 'moodle');
                     $modchoosertoggleurl->param('modchooser', 'off');
@@ -3619,7 +3621,9 @@ class settings_navigation extends navigation_node {
                 $modchoosertoggle->add_class('visibleifjs');
                 user_preference_allow_ajax_update('usemodchooser', PARAM_BOOL);
             }
+        }
 
+        if (has_capability('moodle/course:update', $coursecontext)) {
             if ($this->page->user_is_editing()) {
                 // Removed as per MDL-22732
                 // $this->add_course_editing_links($course);
@@ -4381,7 +4385,7 @@ class settings_navigation extends navigation_node {
         }
         $frontpage->id = 'frontpagesettings';
 
-        if (has_capability('moodle/course:update', $coursecontext)) {
+        if ($this->page->user_allowed_editing()) {
 
             // Add the turn on/off settings
             $url = new moodle_url('/course/view.php', array('id'=>$course->id, 'sesskey'=>sesskey()));
@@ -4393,7 +4397,9 @@ class settings_navigation extends navigation_node {
                 $editstring = get_string('turneditingon');
             }
             $frontpage->add($editstring, $url, self::TYPE_SETTING, null, null, new pix_icon('i/edit', ''));
+        }
 
+        if (has_capability('moodle/course:update', $coursecontext)) {
             // Add the course settings link
             $url = new moodle_url('/admin/settings.php', array('section'=>'frontpagesettings'));
             $frontpage->add(get_string('editsettings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', ''));
index 79bfc8f..9e3c46d 100644 (file)
@@ -821,7 +821,7 @@ class portfolio_exporter {
             if ($f->get_filename() == $skipfile) {
                 continue;
             }
-            $returnfiles[$f->get_filepath() . '/' . $f->get_filename()] = $f;
+            $returnfiles[$f->get_filepath() . $f->get_filename()] = $f;
         }
         return $returnfiles;
     }
index 6046e66..17157bb 100644 (file)
@@ -269,7 +269,7 @@ function rss_standard_header($title = NULL, $link = NULL, $description = NULL) {
             $result .= rss_full_tag('language', 2, false, substr($USER->lang,0,2));
         }
         $today = getdate();
-        $result .= rss_full_tag('copyright', 2, false, '&#169; '. $today['year'] .' '. format_string($site->fullname));
+        $result .= rss_full_tag('copyright', 2, false, '(c) '. $today['year'] .' '. format_string($site->fullname));
         /*
        if (!empty($USER->email)) {
             $result .= rss_full_tag('managingEditor', 2, false, fullname($USER));
diff --git a/lib/tests/completionlib_advanced_test.php b/lib/tests/completionlib_advanced_test.php
new file mode 100644 (file)
index 0000000..e259554
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Completion lib advanced test case.
+ *
+ * This file contains the advanced test suite for completion lib.
+ *
+ * @package    core_completion
+ * @category   phpunit
+ * @copyright  2013 Frédéric Massart
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir.'/completionlib.php');
+
+class completionlib_advanced_testcase extends advanced_testcase {
+
+    function test_get_activities() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create a course with mixed auto completion data.
+        $course = $this->getDataGenerator()->create_course();
+        $completionauto = array('completion' => COMPLETION_TRACKING_AUTOMATIC);
+        $completionmanual = array('completion' => COMPLETION_TRACKING_MANUAL);
+        $completionnone = array('completion' => COMPLETION_TRACKING_NONE);
+        $forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), $completionauto);
+        $page = $this->getDataGenerator()->create_module('page', array('course' => $course->id), $completionauto);
+        $data = $this->getDataGenerator()->create_module('data', array('course' => $course->id), $completionmanual);
+
+        $forum2 = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), $completionnone);
+        $page2 = $this->getDataGenerator()->create_module('page', array('course' => $course->id), $completionnone);
+        $data2 = $this->getDataGenerator()->create_module('data', array('course' => $course->id), $completionnone);
+
+        // Create data in another course to make sure it's not considered.
+        $course2 = $this->getDataGenerator()->create_course();
+        $c2forum = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id), $completionauto);
+        $c2page = $this->getDataGenerator()->create_module('page', array('course' => $course2->id), $completionmanual);
+        $c2data = $this->getDataGenerator()->create_module('data', array('course' => $course2->id), $completionnone);
+
+        $c = new completion_info($course);
+        $activities = $c->get_activities();
+        $this->assertEquals(3, count($activities));
+        $this->assertTrue(isset($activities[$forum->cmid]));
+        $this->assertEquals($activities[$forum->cmid]->name, $forum->name);
+        $this->assertTrue(isset($activities[$page->cmid]));
+        $this->assertEquals($activities[$page->cmid]->name, $page->name);
+        $this->assertTrue(isset($activities[$data->cmid]));
+        $this->assertEquals($activities[$data->cmid]->name, $data->name);
+
+        $this->assertFalse(isset($activities[$forum2->cmid]));
+        $this->assertFalse(isset($activities[$page2->cmid]));
+        $this->assertFalse(isset($activities[$data2->cmid]));
+    }
+
+    function test_has_activities() {
+        global $DB;
+        $this->resetAfterTest(true);
+
+        // Create a course with mixed auto completion data.
+        $course = $this->getDataGenerator()->create_course();
+        $course2 = $this->getDataGenerator()->create_course();
+        $completionauto = array('completion' => COMPLETION_TRACKING_AUTOMATIC);
+        $completionnone = array('completion' => COMPLETION_TRACKING_NONE);
+        $c1forum = $this->getDataGenerator()->create_module('forum', array('course' => $course->id), $completionauto);
+        $c2forum = $this->getDataGenerator()->create_module('forum', array('course' => $course2->id), $completionnone);
+
+        $c1 = new completion_info($course);
+        $c2 = new completion_info($course2);
+
+        $this->assertTrue($c1->has_activities());
+        $this->assertFalse($c2->has_activities());
+    }
+}
index 5092831..2298460 100644 (file)
@@ -514,31 +514,6 @@ WHERE
         $c->internal_set_data($cm, $data);
     }
 
-    function test_get_activities() {
-        global $DB;
-
-        $c = new completion_info((object)array('id'=>42));
-
-        // Try with no activities
-        $DB->expects($this->at(0))
-            ->method('get_records_select')
-            ->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE)
-            ->will($this->returnValue(array()));
-        $result = $c->get_activities();
-        $this->assertEquals(array(), $result);
-
-        // Try with an activity (need to fake up modinfo for it as well)
-        $DB->expects($this->at(0))
-            ->method('get_records_select')
-            ->with('course_modules', 'course=42 AND completion<>'.COMPLETION_TRACKING_NONE)
-            ->will($this->returnValue(array(13=>(object)array('id'=>13))));
-        $modinfo = new stdClass;
-        $modinfo->sections = array(array(1, 2, 3), array(12, 13, 14));
-        $modinfo->cms[13] = (object)array('modname'=>'frog', 'name'=>'kermit');
-        $result = $c->get_activities($modinfo);
-        $this->assertEquals(array(13=>(object)array('id'=>13, 'modname'=>'frog', 'name'=>'kermit')), $result);
-    }
-
     // get_tracked_users() cannot easily be tested because it uses
     // get_role_users, so skipping that
 
diff --git a/lib/tests/environment_test.php b/lib/tests/environment_test.php
new file mode 100644 (file)
index 0000000..9dea45d
--- /dev/null
@@ -0,0 +1,45 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Moodle environment test.
+ *
+ * @package    core
+ * @category   phpunit
+ * @copyright  2013 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Do standard environment.xml tests.
+ */
+class environment_testcase extends advanced_testcase {
+
+    public function test_environment() {
+        global $CFG;
+
+        require_once($CFG->libdir.'/environmentlib.php');
+        list($envstatus, $environment_results) = check_moodle_environment(normalize_version($CFG->release), ENV_SELECT_RELEASE);
+
+        $this->assertNotEmpty($envstatus);
+        foreach ($environment_results as $environment_result) {
+            $this->assertTrue($environment_result->getStatus(), "Problem detected in environment ($environment_result->part:$environment_result->info), fix all warnings and errors!");
+        }
+    }
+}
index 63804f7..473b659 100644 (file)
@@ -60,6 +60,8 @@ class navigation_node_testcase extends basic_testcase {
 
         $this->node = new navigation_node('Test Node');
         $this->node->type = navigation_node::TYPE_SYSTEM;
+        // We add the first child without key. This way we make sure all keys search by comparision is performed using ===
+        $this->node->add('first child without key', null, navigation_node::TYPE_CUSTOM);
         $demo1 = $this->node->add('demo1', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo1', new pix_icon('i/course', ''));
         $demo2 = $this->node->add('demo2', $this->inactiveurl, navigation_node::TYPE_COURSE, null, 'demo2', new pix_icon('i/course', ''));
         $demo3 = $this->node->add('demo3', $this->inactiveurl, navigation_node::TYPE_CATEGORY, null, 'demo3',new pix_icon('i/course', ''));
@@ -251,8 +253,24 @@ class navigation_node_testcase extends basic_testcase {
         $this->assertInstanceOf('navigation_node', $this->node->get('remove2'));
         $this->assertInstanceOf('navigation_node', $remove2->get('remove3'));
 
+        // Remove element and make sure this is no longer a child.
         $this->assertTrue($remove1->remove());
+        $this->assertFalse($this->node->get('remove1'));
+        $this->assertFalse(in_array('remove1', $this->node->get_children_key_list(), true));
+
+        // Make sure that we can insert element after removal
+        $insertelement = navigation_node::create('extra element 4', null, navigation_node::TYPE_CUSTOM, null, 'element4');
+        $this->node->add_node($insertelement, 'remove2');
+        $this->assertNotEmpty($this->node->get('element4'));
+
+        // Remove more elements
         $this->assertTrue($this->node->get('remove2')->remove());
+        $this->assertFalse($this->node->get('remove2'));
+
+        // Make sure that we can add element after removal
+        $this->node->add('extra element 5', null, navigation_node::TYPE_CUSTOM, null, 'element5');
+        $this->assertNotEmpty($this->node->get('element5'));
+
         $this->assertTrue($remove2->get('remove3')->remove());
 
         $this->assertFalse($this->node->get('remove1'));
index a7da827..39b2e64 100644 (file)
@@ -106,15 +106,17 @@ class assign_grading_table extends table_sql implements renderable {
         // The filters do not make sense when there are no submissions, so do not apply them.
         if ($this->assignment->is_any_submission_plugin_enabled()) {
             if ($filter == ASSIGN_FILTER_SUBMITTED) {
-                $where .= ' AND s.timecreated > 0 ';
-            }
-            if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
+                $where .= ' AND (s.timemodified IS NOT NULL AND
+                                 s.status = :submitted) ';
+                $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+
+            } else if ($filter == ASSIGN_FILTER_REQUIRE_GRADING) {
                 $where .= ' AND (s.timemodified IS NOT NULL AND
                                  s.status = :submitted AND
                                  (s.timemodified > g.timemodified OR g.timemodified IS NULL))';
                 $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
-            }
-            if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
+
+            } else if (strpos($filter, ASSIGN_FILTER_SINGLE_USER) === 0) {
                 $userfilter = (int) array_pop(explode('=', $filter));
                 $where .= ' AND (u.id = :userid)';
                 $params['userid'] = $userfilter;
index e97c2e5..4890e7a 100644 (file)
@@ -2352,7 +2352,7 @@ class assign {
             return false;
         }
         $assign = clone $this->get_instance();
-        $assign->cmidnumber = $this->get_course_module()->id;
+        $assign->cmidnumber = $this->get_course_module()->idnumber;
 
         return assign_grade_item_update($assign, $gradebookgrade);
     }
@@ -2662,7 +2662,9 @@ class assign {
             // Give each submission plugin a chance to process the submission
             $plugins = $this->get_submission_plugins();
             foreach ($plugins as $plugin) {
-                $plugin->submit_for_grading();
+                if ($plugin->is_enabled() && $plugin->is_visible()) {
+                    $plugin->submit_for_grading();
+                }
             }
 
             $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
@@ -2943,7 +2945,7 @@ class assign {
             $allempty = true;
             $pluginerror = false;
             foreach ($this->submissionplugins as $plugin) {
-                if ($plugin->is_enabled()) {
+                if ($plugin->is_enabled() && $plugin->is_visible()) {
                     if (!$plugin->save($submission, $data)) {
                         $notices[] = $plugin->get_error();
                         $pluginerror = true;
index 541233d..e0f7003 100644 (file)
@@ -72,6 +72,7 @@ if ($mform->is_cancelled()) {
         // store the files
         $data = file_postupdate_standard_editor($data, 'content', $options, $context, 'mod_book', 'chapter', $data->id);
         $DB->update_record('book_chapters', $data);
+        $DB->set_field('book', 'revision', $book->revision+1, array('id'=>$book->id));
 
         add_to_log($course->id, 'course', 'update mod', '../mod/book/view.php?id='.$cm->id, 'book '.$book->id);
         add_to_log($course->id, 'book', 'update', 'view.php?id='.$cm->id.'&chapterid='.$data->id, $book->id, $cm->id);
index be060c0..4f07f8e 100644 (file)
@@ -70,6 +70,7 @@ require_login($course, true, $cm);
 require_capability('mod/feedback:mapcourse', $context);
 
 if ($coursefilter) {
+    $map = new stdClass;
     $map->feedbackid = $feedback->id;
     $map->courseid = $coursefilter;
     // insert a map only if it does exists yet
@@ -125,6 +126,7 @@ echo '</form>';
 
 if ($coursemap = feedback_get_courses_from_sitecourse_map($feedback->id)) {
     $table = new flexible_table('coursemaps');
+    $table->baseurl = $url;
     $table->define_columns( array('course'));
     $table->define_headers( array(get_string('mappedcourses', 'feedback')));
 
@@ -132,9 +134,8 @@ if ($coursemap = feedback_get_courses_from_sitecourse_map($feedback->id)) {
 
     $unmapurl = new moodle_url('/mod/feedback/unmapcourse.php');
     foreach ($coursemap as $cmap) {
-        $cmapcontext = get_context_instance(CONTEXT_COURSE, $cmap->id);
-        $cmapshortname = format_string($cmap->shortname, true, array('context' => $cmapcontext));
-        $coursecontext = get_context_instance(CONTEXT_COURSE, $cmap->courseid);
+        $coursecontext = context_course::instance($cmap->courseid);
+        $cmapshortname = format_string($cmap->shortname, true, array('context' => $coursecontext));
         $cmapfullname = format_string($cmap->fullname, true, array('context' => $coursecontext));
         $unmapurl->params(array('id'=>$id, 'cmapid'=>$cmap->id));
         $anker = '<a href="'.$unmapurl->out().'">';
index d9f28be..f1c07ef 100644 (file)
@@ -6717,7 +6717,11 @@ function forum_tp_count_forum_unread_posts($cm, $course) {
         $modinfo->groups = groups_get_user_groups($course->id, $USER->id);
     }
 
-    $mygroups = $modinfo->groups[$cm->groupingid];
+    if (array_key_exists($cm->groupingid, $modinfo->groups)) {
+        $mygroups = $modinfo->groups[$cm->groupingid];
+    } else {
+        $mygroups = false; // Will be set below
+    }
 
     // add all groups posts
     if (empty($mygroups)) {
index 04b16fb..71a1813 100644 (file)
@@ -61,10 +61,14 @@ function imscp_print_content($imscp, $cm, $course) {
 function imscp_htmllize_item($item, $imscp, $cm) {
     global $CFG;
 
-    $context = get_context_instance(CONTEXT_MODULE, $cm->id);
-    $urlbase = "$CFG->wwwroot/pluginfile.php";
-    $path = '/'.$context->id.'/mod_imscp/content/'.$imscp->revision.'/'.$item['href'];
-    $url = file_encode_url($urlbase, $path, false);
+    if (preg_match('|^https?://|', $item['href'])) {
+        $url = $item['href'];
+    } else {
+        $context = context_module::instance($cm->id);
+        $urlbase = "$CFG->wwwroot/pluginfile.php";
+        $path = '/'.$context->id.'/mod_imscp/content/'.$imscp->revision.'/'.$item['href'];
+        $url = file_encode_url($urlbase, $path, false);
+    }
     $result = "<li><a href=\"$url\">".$item['title'].'</a>';
     if ($item['subitems']) {
         $result .= '<ul>';
index f1f08ca..4a47194 100644 (file)
@@ -93,7 +93,7 @@ if ($lastattempt && ($lastattempt->state == quiz_attempt::IN_PROGRESS ||
 
     // And, if the attempt is now no longer in progress, redirect to the appropriate place.
     if ($lastattempt->state == quiz_attempt::OVERDUE) {
-         redirect($quizobj->summary_url($lastattempt->id));
+        redirect($quizobj->summary_url($lastattempt->id));
     } else if ($lastattempt->state != quiz_attempt::IN_PROGRESS) {
         redirect($quizobj->review_url($lastattempt->id));
     }
@@ -104,6 +104,10 @@ if ($lastattempt && ($lastattempt->state == quiz_attempt::IN_PROGRESS ||
     }
 
 } else {
+    while ($lastattempt && $lastattempt->preview) {
+        $lastattempt = array_pop($attempts);
+    }
+
     // Get number for the next or unfinished attempt.
     if ($lastattempt) {
         $attemptnumber = $lastattempt->attempt + 1;
index e3ffb34..a57f2dd 100644 (file)
@@ -35,7 +35,7 @@ class mod_scorm_mod_form extends moodleform_mod {
         //-------------------------------------------------------------------------------
         $mform->addElement('header', 'general', get_string('general', 'form'));
 
-        // Name
+        // Name.
         $mform->addElement('text', 'name', get_string('name'));
         if (!empty($CFG->formatstringstriptags)) {
             $mform->setType('name', PARAM_TEXT);
@@ -45,10 +45,10 @@ class mod_scorm_mod_form extends moodleform_mod {
         $mform->addRule('name', null, 'required', null, 'client');
         $mform->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client');
 
-        // Summary
+        // Summary.
         $this->add_intro_editor(true);
 
-        // Scorm types
+        // Scorm types.
         $scormtypes = array(SCORM_TYPE_LOCAL => get_string('typelocal', 'scorm'));
 
         if ($cfg_scorm->allowtypeexternal) {
@@ -67,7 +67,7 @@ class mod_scorm_mod_form extends moodleform_mod {
             $scormtypes[SCORM_TYPE_AICCURL] = get_string('typeaiccurl', 'scorm');
         }
 
-        // Reference
+        // Reference.
         if (count($scormtypes) > 1) {
             $mform->addElement('select', 'scormtype', get_string('scormtype', 'scorm'), $scormtypes);
             $mform->addHelpButton('scormtype', 'scormtype', 'scorm');
@@ -79,7 +79,7 @@ class mod_scorm_mod_form extends moodleform_mod {
             $mform->addElement('hidden', 'scormtype', SCORM_TYPE_LOCAL);
         }
 
-        // New local package upload
+        // New local package upload.
         $maxbytes = get_max_upload_file_size($CFG->maxbytes, $COURSE->maxbytes);
         $mform->setMaxFileSize($maxbytes);
         $mform->addElement('filepicker', 'packagefile', get_string('package', 'scorm'));
@@ -87,34 +87,34 @@ class mod_scorm_mod_form extends moodleform_mod {
         $mform->disabledIf('packagefile', 'scormtype', 'noteq', SCORM_TYPE_LOCAL);
 
         //-------------------------------------------------------------------------------
-        // Time restrictions
+        // Time restrictions.
         $mform->addElement('header', 'timerestricthdr', get_string('timerestrict', 'scorm'));
 
         $mform->addElement('date_time_selector', 'timeopen', get_string("scormopen", "scorm"), array('optional' => true));
         $mform->addElement('date_time_selector', 'timeclose', get_string("scormclose", "scorm"), array('optional' => true));
         //-------------------------------------------------------------------------------
-        // display Settings
+        // Display Settings.
         $mform->addElement('header', 'displaysettings', get_string('displaysettings', 'scorm'));
-        // Framed / Popup Window
+        // Framed / Popup Window.
         $mform->addElement('select', 'popup', get_string('display', 'scorm'), scorm_get_popup_display_array());
         $mform->setDefault('popup', $cfg_scorm->popup);
         $mform->setAdvanced('popup', $cfg_scorm->popup_adv);
 
-        // Width
+        // Width.
         $mform->addElement('text', 'width', get_string('width', 'scorm'), 'maxlength="5" size="5"');
         $mform->setDefault('width', $cfg_scorm->framewidth);
         $mform->setType('width', PARAM_INT);
         $mform->setAdvanced('width', $cfg_scorm->framewidth_adv);
         $mform->disabledIf('width', 'popup', 'eq', 0);
 
-        // Height
+        // Height.
         $mform->addElement('text', 'height', get_string('height', 'scorm'), 'maxlength="5" size="5"');
         $mform->setDefault('height', $cfg_scorm->frameheight);
         $mform->setType('height', PARAM_INT);
         $mform->setAdvanced('height', $cfg_scorm->frameheight_adv);
         $mform->disabledIf('height', 'popup', 'eq', 0);
 
-        // Window Options
+        // Window Options.
         $winoptgrp = array();
         foreach (scorm_get_popup_options_array() as $key => $value) {
             $winoptgrp[] = &$mform->createElement('checkbox', $key, '', get_string($key, 'scorm'));
@@ -124,7 +124,7 @@ class mod_scorm_mod_form extends moodleform_mod {
         $mform->disabledIf('winoptgrp', 'popup', 'eq', 0);
         $mform->setAdvanced('winoptgrp', $cfg_scorm->winoptgrp_adv);
 
-        // Skip view page
+        // Skip view page.
         $skipviewoptions = scorm_get_skip_view_array();
         if ($COURSE->format == 'scorm') { // Remove option that would cause a constant redirect.
             unset($skipviewoptions[SCORM_SKIPVIEW_ALWAYS]);
@@ -137,41 +137,41 @@ class mod_scorm_mod_form extends moodleform_mod {
         $mform->setDefault('skipview', $cfg_scorm->skipview);
         $mform->setAdvanced('skipview', $cfg_scorm->skipview_adv);
 
-        // Hide Browse
+        // Hide Browse.
         $mform->addElement('selectyesno', 'hidebrowse', get_string('hidebrowse', 'scorm'));
         $mform->addHelpButton('hidebrowse', 'hidebrowse', 'scorm');
         $mform->setDefault('hidebrowse', $cfg_scorm->hidebrowse);
         $mform->setAdvanced('hidebrowse', $cfg_scorm->hidebrowse_adv);
 
-        // Display course structure
+        // Display course structure.
         $mform->addElement('selectyesno', 'displaycoursestructure', get_string('displaycoursestructure', 'scorm'));
         $mform->addHelpButton('displaycoursestructure', 'displaycoursestructure', 'scorm');
         $mform->setDefault('displaycoursestructure', $cfg_scorm->displaycoursestructure);
         $mform->setAdvanced('displaycoursestructure', $cfg_scorm->displaycoursestructure_adv);
 
-        // Toc display
+        // Toc display.
         $mform->addElement('select', 'hidetoc', get_string('hidetoc', 'scorm'), scorm_get_hidetoc_array());
         $mform->addHelpButton('hidetoc', 'hidetoc', 'scorm');
         $mform->setDefault('hidetoc', $cfg_scorm->hidetoc);
         $mform->setAdvanced('hidetoc', $cfg_scorm->hidetoc_adv);
 
-        // Hide Navigation panel
+        // Hide Navigation panel.
         $mform->addElement('selectyesno', 'hidenav', get_string('hidenav', 'scorm'));
         $mform->setDefault('hidenav', $cfg_scorm->hidenav);
         $mform->setAdvanced('hidenav', $cfg_scorm->hidenav_adv);
         $mform->disabledIf('hidenav', 'hidetoc', 'noteq', 0);
 
         //-------------------------------------------------------------------------------
-        // grade Settings
+        // Grade Settings.
         $mform->addElement('header', 'gradesettings', get_string('gradesettings', 'scorm'));
 
-        // Grade Method
+        // Grade Method.
         $mform->addElement('select', 'grademethod', get_string('grademethod', 'scorm'), scorm_get_grade_method_array());
         $mform->addHelpButton('grademethod', 'grademethod', 'scorm');
         $mform->setDefault('grademethod', $cfg_scorm->grademethod);
         $mform->setAdvanced('grademethod', $cfg_scorm->grademethod_adv);
 
-        // Maximum Grade
+        // Maximum Grade.
         for ($i=0; $i<=100; $i++) {
             $grades[$i] = "$i";
         }
@@ -182,20 +182,20 @@ class mod_scorm_mod_form extends moodleform_mod {
 
         $mform->addElement('header', 'othersettings', get_string('othersettings', 'scorm'));
 
-        // Max Attempts
+        // Max Attempts.
         $mform->addElement('select', 'maxattempt', get_string('maximumattempts', 'scorm'), scorm_get_attempts_array());
         $mform->addHelpButton('maxattempt', 'maximumattempts', 'scorm');
         $mform->setDefault('maxattempt', $cfg_scorm->maxattempt);
         $mform->setAdvanced('maxattempt', $cfg_scorm->maxattempt_adv);
 
-        // What Grade
+        // What Grade.
         $mform->addElement('select', 'whatgrade', get_string('whatgrade', 'scorm'),  scorm_get_what_grade_array());
         $mform->disabledIf('whatgrade', 'maxattempt', 'eq', 1);
         $mform->addHelpButton('whatgrade', 'whatgrade', 'scorm');
         $mform->setDefault('whatgrade', $cfg_scorm->whatgrade);
         $mform->setAdvanced('whatgrade', $cfg_scorm->whatgrade_adv);
 
-        // Display attempt status
+        // Display attempt status.
         $mform->addElement('selectyesno', 'displayattemptstatus', get_string('displayattemptstatus', 'scorm'));
         $mform->addHelpButton('displayattemptstatus', 'displayattemptstatus', 'scorm');
         $mform->setDefault('displayattemptstatus', $cfg_scorm->displayattemptstatus);
@@ -207,19 +207,19 @@ class mod_scorm_mod_form extends moodleform_mod {
         $mform->setDefault('forcecompleted', $cfg_scorm->forcecompleted);
         $mform->setAdvanced('forcecompleted', $cfg_scorm->forcecompleted_adv);
 
-        // Force new attempt
+        // Force new attempt.
         $mform->addElement('selectyesno', 'forcenewattempt', get_string('forcenewattempt', 'scorm'));
         $mform->addHelpButton('forcenewattempt', 'forcenewattempt', 'scorm');
         $mform->setDefault('forcenewattempt', $cfg_scorm->forcenewattempt);
         $mform->setAdvanced('forcenewattempt', $cfg_scorm->forcenewattempt_adv);
 
-        // Last attempt lock - lock the enter button after the last available attempt has been made
+        // Last attempt lock - lock the enter button after the last available attempt has been made.
         $mform->addElement('selectyesno', 'lastattemptlock', get_string('lastattemptlock', 'scorm'));
         $mform->addHelpButton('lastattemptlock', 'lastattemptlock', 'scorm');
         $mform->setDefault('lastattemptlock', $cfg_scorm->lastattemptlock);
         $mform->setAdvanced('lastattemptlock', $cfg_scorm->lastattemptlock_adv);
 
-        // Activation period
+        // Activation period.
 /*        $mform->addElement('static', '', '' ,'<hr />');
         $mform->addElement('static', 'activation', get_string('activation','scorm'));
         $datestartgrp = array();
@@ -239,14 +239,14 @@ class mod_scorm_mod_form extends moodleform_mod {
         $mform->disabledIf('dateendgrp', 'enddisabled', 'checked');
 */
 
-        // Autocontinue
+        // Autocontinue.
         $mform->addElement('selectyesno', 'auto', get_string('autocontinue', 'scorm'));
         $mform->addHelpButton('auto', 'autocontinue', 'scorm');
         $mform->setDefault('auto', $cfg_scorm->auto);
         $mform->setAdvanced('auto', $cfg_scorm->auto_adv);
 
         if (count($scormtypes) > 1) {
-            // Update packages timing
+            // Update packages timing.
             $mform->addElement('select', 'updatefreq', get_string('updatefreq', 'scorm'), scorm_get_updatefreq_array());
             $mform->setDefault('updatefreq', $cfg_scorm->updatefreq);
             $mform->setAdvanced('updatefreq', $cfg_scorm->updatefreq_adv);
@@ -256,7 +256,7 @@ class mod_scorm_mod_form extends moodleform_mod {
             $mform->addElement('hidden', 'updatefreq', 0);
         }
         //-------------------------------------------------------------------------------
-        // Hidden Settings
+        // Hidden Settings.
         $mform->addElement('hidden', 'datadir', null);
         $mform->setType('datadir', PARAM_RAW);
         $mform->addElement('hidden', 'pkgtype', null);
@@ -271,7 +271,7 @@ class mod_scorm_mod_form extends moodleform_mod {
         //-------------------------------------------------------------------------------
         $this->standard_coursemodule_elements();
         //-------------------------------------------------------------------------------
-        // buttons
+        // Buttons.
         $this->add_action_buttons();
     }
 
@@ -324,7 +324,7 @@ class mod_scorm_mod_form extends moodleform_mod {
             $default_values['timeclose'] = 0;
         }
 
-        // Set some completion default data
+        // Set some completion default data.
         if (!empty($default_values['completionstatusrequired']) && !is_array($default_values['completionstatusrequired'])) {
             // Unpack values
             $cvalues = array();
@@ -351,7 +351,7 @@ class mod_scorm_mod_form extends moodleform_mod {
 
         if ($type === SCORM_TYPE_LOCAL) {
             if (!empty($data['update'])) {
-                //ok, not required
+                // OK, not required.
 
             } else if (empty($data['packagefile'])) {
                 $errors['packagefile'] = get_string('required');
@@ -419,7 +419,7 @@ class mod_scorm_mod_form extends moodleform_mod {
         return $errors;
     }
 
-    //need to translate the "options" and "reference" field.
+    // Need to translate the "options" and "reference" field.
     function set_data($default_values) {
         $default_values = (array)$default_values;
 
@@ -452,7 +452,7 @@ class mod_scorm_mod_form extends moodleform_mod {
         $mform =& $this->_form;
         $items = array();
 
-        // Require score
+        // Require score.
         $group = array();
         $group[] =& $mform->createElement('text', 'completionscorerequired', '', array('size' => 5));
         $group[] =& $mform->createElement('checkbox', 'completionscoredisabled', null, get_string('disable'));
@@ -465,7 +465,7 @@ class mod_scorm_mod_form extends moodleform_mod {
         $items[] = 'completionscoregroup';
 
 
-        // Require status
+        // Require status.
         $first = true;
         $firstkey = null;
         foreach (scorm_status_options(true) as $key => $value) {
@@ -499,18 +499,22 @@ class mod_scorm_mod_form extends moodleform_mod {
             return false;
         }
 
+        // Convert completionstatusrequired to a proper integer, if any.
+        $total = 0;
+        if (isset($data->completionstatusrequired) && is_array($data->completionstatusrequired)) {
+            foreach (array_keys($data->completionstatusrequired) as $state) {
+                $total |= $state;
+            }
+            $data->completionstatusrequired = $total;
+        }
+
         if (!empty($data->completionunlocked)) {
-            // Turn off completion settings if the checkboxes aren't ticked
+            // Turn off completion settings if the checkboxes aren't ticked.
             $autocompletion = isset($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC;
 
-            if (isset($data->completionstatusrequired) &&
-                    is_array($data->completionstatusrequired) && $autocompletion) {
-                $total = 0;
-                foreach (array_keys($data->completionstatusrequired) as $state) {
-                    $total |= $state;
-                }
-
-                $data->completionstatusrequired = $total;
+            if (isset($data->completionstatusrequired) && $autocompletion) {
+                // Do nothing: completionstatusrequired has been already converted
+                //             into a correct integer representation.
             } else {
                 $data->completionstatusrequired = null;
             }
index 6286973..7a03152 100644 (file)
@@ -23,5 +23,4 @@
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['pluginname'] = 'Graph report';
-
+$string['pluginname'] = 'Basic report';
index 979b432..e66c434 100644 (file)
@@ -24,10 +24,10 @@ class note_edit_form extends moodleform {
         $this->add_action_buttons();
 
         $mform->addElement('hidden', 'courseid');
-        $mform->setType('course', PARAM_INT);
+        $mform->setType('courseid', PARAM_INT);
 
         $mform->addElement('hidden', 'userid');
-        $mform->setType('user', PARAM_INT);
+        $mform->setType('userid', PARAM_INT);
 
         $mform->addElement('hidden', 'id');
         $mform->setType('id', PARAM_INT);
diff --git a/question/behaviour/manualgraded/db/install.php b/question/behaviour/manualgraded/db/install.php
new file mode 100644 (file)
index 0000000..7818769
--- /dev/null
@@ -0,0 +1,49 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Post-install script for manual graded question behaviour.
+ * @package   qbehaviour_manualgraded
+ * @copyright 2013 The Open Universtiy
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Post-install script
+ */
+function xmldb_qbehaviour_manualgraded_install() {
+
+    // Hide the manualgraded behaviour from the list of behaviours that users
+    // can select in the user-interface. If a user accidentally chooses manual
+    // graded behaviour for a quiz, there is no way to get the questions automatically
+    // graded after the student has answered them. If teachers really want to do
+    // this they can ask their admin to enable it on the manage behaviours
+    // screen in the UI.
+    $disabledbehaviours = get_config('question', 'disabledbehaviours');
+    if (!empty($disabledbehaviours)) {
+        $disabledbehaviours = explode(',', $disabledbehaviours);
+    } else {
+        $disabledbehaviours = array();
+    }
+    if (array_search('manualgraded', $disabledbehaviours) === false) {
+        $disabledbehaviours[] = 'manualgraded';
+        set_config('disabledbehaviours', implode(',', $disabledbehaviours), 'question');
+    }
+}
diff --git a/question/behaviour/manualgraded/db/upgrade.php b/question/behaviour/manualgraded/db/upgrade.php
new file mode 100644 (file)
index 0000000..635a8e7
--- /dev/null
@@ -0,0 +1,91 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Post-install script for the manual graded question behaviour.
+ *
+ * @package   qbehaviour_manualgraded
+ * @copyright 2013 The Open Universtiy
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+defined('MOODLE_INTERNAL') || die();
+
+
+/**
+ * Manual graded question behaviour upgrade code.
+ */
+function xmldb_qbehaviour_manualgraded_upgrade($oldversion) {
+    global $CFG, $DB;
+
+    $dbman = $DB->get_manager();
+
+    // Moodle v2.3.0 release upgrade line
+    // Put any upgrade step following this
+
+    if ($oldversion < 2012061701) {
+        // Hide the manualgraded behaviour from the list of behaviours that users
+        // can select in the user-interface. If a user accidentally chooses manual
+        // graded behaviour for a quiz, there is no way to get the questions automatically
+        // graded after the student has answered them. If teachers really want to do
+        // this they can ask their admin to enable it on the manage behaviours
+        // screen in the UI.
+        $disabledbehaviours = get_config('question', 'disabledbehaviours');
+        if (!empty($disabledbehaviours)) {
+            $disabledbehaviours = explode(',', $disabledbehaviours);
+        } else {
+            $disabledbehaviours = array();
+        }
+        if (array_search('manualgraded', $disabledbehaviours) === false) {
+            $disabledbehaviours[] = 'manualgraded';
+            set_config('disabledbehaviours', implode(',', $disabledbehaviours), 'question');
+        }
+
+        // Manual graded question behaviour savepoint reached.
+        upgrade_plugin_savepoint(true, 2012061701, 'qbehaviour', 'manualgraded');
+    }
+
+    if ($oldversion < 2012061702) {
+        // Also, fix any other admin settings that currently select manualgraded behaviour.
+
+        // Work out a sensible default alternative to manualgraded.
+        require_once($CFG->libdir . '/questionlib.php');
+        $behaviours = question_engine::get_behaviour_options('');
+        if (array_key_exists('deferredfeedback', $behaviours)) {
+             $defaultbehaviour = 'deferredfeedback';
+        } else {
+            reset($behaviours);
+            $defaultbehaviour = key($behaviours);
+        }
+
+        // Fix the question preview default.
+        if (get_config('question_preview', 'behaviour') == 'manualgraded') {
+            set_config('behaviour', $defaultbehaviour, 'question_preview');
+        }
+
+        // Fix the quiz settings default.
+        if (get_config('quiz', 'preferredbehaviour') == 'manualgraded') {
+            set_config('preferredbehaviour', $defaultbehaviour, 'quiz');
+        }
+
+        // Manual graded question behaviour savepoint reached.
+        upgrade_plugin_savepoint(true, 2012061702, 'qbehaviour', 'manualgraded');
+    }
+
+    return true;
+}
+
index 897af13..4c8ffc4 100644 (file)
@@ -26,7 +26,7 @@
 defined('MOODLE_INTERNAL') || die();
 
 $plugin->component = 'qbehaviour_manualgraded';
-$plugin->version   = 2012061700;
+$plugin->version   = 2012061702;
 
 $plugin->requires  = 2012061700;
 
index 9fdfc03..c9fb2ca 100644 (file)
@@ -757,16 +757,16 @@ abstract class question_utils {
     public static function arrays_same_at_key_integer(
             array $array1, array $array2, $key) {
         if (array_key_exists($key, $array1)) {
-            $value1 = $array1[$key];
+            $value1 = (int) $array1[$key];
         } else {
             $value1 = 0;
         }
         if (array_key_exists($key, $array2)) {
-            $value2 = $array2[$key];
+            $value2 = (int) $array2[$key];
         } else {
             $value2 = 0;
         }
-        return ((integer) $value1) === ((integer) $value2);
+        return $value1 === $value2;
     }
 
     private static $units     = array('', 'i', 'ii', 'iii', 'iv', 'v', 'vi', 'vii', 'viii', 'ix');
index 8dd7086..67388b6 100644 (file)
@@ -338,7 +338,7 @@ class qtype_multichoice_multi_question extends qtype_multichoice_base {
     public function is_same_response(array $prevresponse, array $newresponse) {
         foreach ($this->order as $key => $notused) {
             $fieldname = $this->field($key);
-            if (!question_utils::arrays_same_at_key($prevresponse, $newresponse, $fieldname)) {
+            if (!question_utils::arrays_same_at_key_integer($prevresponse, $newresponse, $fieldname)) {
                 return false;
             }
         }
diff --git a/question/type/multichoice/tests/question_multi_test.php b/question/type/multichoice/tests/question_multi_test.php
new file mode 100644 (file)
index 0000000..11fe16f
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit tests for the multiple choice, multi-response question definition classes.
+ *
+ * @package   qtype_multichoice
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
+
+
+/**
+ * Unit tests for the multiple choice, multi-response question definition class.
+ *
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class qtype_multichoice_multi_question_test extends advanced_testcase {
+
+    public function test_get_expected_data() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array('choice0' => PARAM_BOOL, 'choice1' => PARAM_BOOL,
+                'choice2' => PARAM_BOOL, 'choice3' => PARAM_BOOL), $question->get_expected_data());
+    }
+
+    public function test_is_complete_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertFalse($question->is_complete_response(array()));
+        $this->assertFalse($question->is_complete_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+        $this->assertTrue($question->is_complete_response(array('choice1' => '1')));
+        $this->assertTrue($question->is_complete_response(
+                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
+    }
+
+    public function test_is_gradable_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertFalse($question->is_gradable_response(array()));
+        $this->assertFalse($question->is_gradable_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+        $this->assertTrue($question->is_gradable_response(array('choice1' => '1')));
+        $this->assertTrue($question->is_gradable_response(
+                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
+    }
+
+    public function test_is_same_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertTrue($question->is_same_response(
+                array(),
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+
+        $this->assertTrue($question->is_same_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0'),
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+
+        $this->assertFalse($question->is_same_response(
+                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0'),
+                array('choice0' => '1', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
+
+        $this->assertTrue($question->is_same_response(
+                array('choice0' => '1', 'choice1' => '0', 'choice2' => '1', 'choice3' => '0'),
+                array('choice0' => '1', 'choice1' => '0', 'choice2' => '1', 'choice3' => '0')));
+    }
+
+    public function test_grading() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->shuffleanswers = false;
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array(1, question_state::$gradedright),
+                $question->grade_response(array('choice0' => '1', 'choice2' => '1')));
+        $this->assertEquals(array(0.5, question_state::$gradedpartial),
+                $question->grade_response(array('choice0' => '1')));
+        $this->assertEquals(array(0, question_state::$gradedwrong),
+                $question->grade_response(
+                        array('choice0' => '1', 'choice1' => '1', 'choice2' => '1')));
+        $this->assertEquals(array(0, question_state::$gradedwrong),
+                $question->grade_response(array('choice1' => '1')));
+    }
+
+    public function test_get_correct_response() {
+        $question = test_question_maker::make_a_multichoice_multi_question();
+        $question->shuffleanswers = false;
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array('choice0' => '1', 'choice2' => '1'),
+                $question->get_correct_response());
+    }
+
+    public function test_get_question_summary() {
+        $mc = test_question_maker::make_a_multichoice_single_question();
+        $mc->start_attempt(new question_attempt_step(), 1);
+
+        $qsummary = $mc->get_question_summary();
+
+        $this->assertRegExp('/' . preg_quote($mc->questiontext, '/') . '/', $qsummary);
+        foreach ($mc->answers as $answer) {
+            $this->assertRegExp('/' . preg_quote($answer->answer, '/') . '/', $qsummary);
+        }
+    }
+
+    public function test_summarise_response() {
+        $mc = test_question_maker::make_a_multichoice_multi_question();
+        $mc->shuffleanswers = false;
+        $mc->start_attempt(new question_attempt_step(), 1);
+
+        $summary = $mc->summarise_response(array('choice1' => 1, 'choice2' => 1),
+                test_question_maker::get_a_qa($mc));
+
+        $this->assertEquals('B; C', $summary);
+    }
+
+    public function test_classify_response() {
+        $mc = test_question_maker::make_a_multichoice_multi_question();
+        $mc->shuffleanswers = false;
+        $mc->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertEquals(array(
+                    13 => new question_classified_response(13, 'A', 0.5),
+                    14 => new question_classified_response(14, 'B', -1.0),
+                ), $mc->classify_response(array('choice0' => 1, 'choice1' => 1)));
+
+        $this->assertEquals(array(), $mc->classify_response(array()));
+    }
+}
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Unit tests for the multiple choice question definition classes.
+ * Unit tests for the multiple choice, single response question definition classes.
  *
- * @package    qtype
- * @subpackage multichoice
- * @copyright  2009 The Open University
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @package   qtype_multichoice
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-
 defined('MOODLE_INTERNAL') || die();
 
 global $CFG;
@@ -31,10 +29,10 @@ require_once($CFG->dirroot . '/question/engine/tests/helpers.php');
 
 
 /**
- * Unit tests for the multiple choice, multiple response question definition class.
+ * Unit tests for the multiple choice, single response question definition class.
  *
- * @copyright  2009 The Open University
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ * @copyright 2009 The Open University
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 class qtype_multichoice_single_question_test extends advanced_testcase {
 
@@ -59,6 +57,31 @@ class qtype_multichoice_single_question_test extends advanced_testcase {
         $this->assertTrue($question->is_gradable_response(array('answer' => '2')));
     }
 
+    public function test_is_same_response() {
+        $question = test_question_maker::make_a_multichoice_single_question();
+        $question->start_attempt(new question_attempt_step(), 1);
+
+        $this->assertTrue($question->is_same_response(
+                array(),
+                array()));
+
+        $this->assertFalse($question->is_same_response(
+                array(),
+                array('answer' => '0')));
+
+        $this->assertTrue($question->is_same_response(
+                array('answer' => '0'),
+                array('answer' => '0')));
+
+        $this->assertFalse($question->is_same_response(
+                array('answer' => '0'),
+                array('answer' => '1')));
+
+        $this->assertTrue($question->is_same_response(
+                array('answer' => '2'),
+                array('answer' => '2')));
+    }
+
     public function test_grading() {
         $question = test_question_maker::make_a_multichoice_single_question();
         $question->shuffleanswers = false;
@@ -151,106 +174,3 @@ class qtype_multichoice_single_question_test extends advanced_testcase {
         $this->assertEquals('Frog<br />†', $mc->make_html_inline('<p>Frog</p><p>†</p>'));
     }
 }
-
-
-/**
- * Unit tests for the multiple choice, single response question definition class.
- *
- * @copyright  2009 The Open University
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class qtype_multichoice_multi_question_test extends advanced_testcase {
-
-    public function test_get_expected_data() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array('choice0' => PARAM_BOOL, 'choice1' => PARAM_BOOL,
-                'choice2' => PARAM_BOOL, 'choice3' => PARAM_BOOL), $question->get_expected_data());
-    }
-
-    public function test_is_complete_response() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertFalse($question->is_complete_response(array()));
-        $this->assertFalse($question->is_complete_response(
-                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
-        $this->assertTrue($question->is_complete_response(array('choice1' => '1')));
-        $this->assertTrue($question->is_complete_response(
-                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
-    }
-
-    public function test_is_gradable_response() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertFalse($question->is_gradable_response(array()));
-        $this->assertFalse($question->is_gradable_response(
-                array('choice0' => '0', 'choice1' => '0', 'choice2' => '0', 'choice3' => '0')));
-        $this->assertTrue($question->is_gradable_response(array('choice1' => '1')));
-        $this->assertTrue($question->is_gradable_response(
-                array('choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1')));
-    }
-
-    public function test_grading() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->shuffleanswers = false;
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array(1, question_state::$gradedright),
-                $question->grade_response(array('choice0' => '1', 'choice2' => '1')));
-        $this->assertEquals(array(0.5, question_state::$gradedpartial),
-                $question->grade_response(array('choice0' => '1')));
-        $this->assertEquals(array(0, question_state::$gradedwrong),
-                $question->grade_response(
-                        array('choice0' => '1', 'choice1' => '1', 'choice2' => '1')));
-        $this->assertEquals(array(0, question_state::$gradedwrong),
-                $question->grade_response(array('choice1' => '1')));
-    }
-
-    public function test_get_correct_response() {
-        $question = test_question_maker::make_a_multichoice_multi_question();
-        $question->shuffleanswers = false;
-        $question->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array('choice0' => '1', 'choice2' => '1'),
-                $question->get_correct_response());
-    }
-
-    public function test_get_question_summary() {
-        $mc = test_question_maker::make_a_multichoice_single_question();
-        $mc->start_attempt(new question_attempt_step(), 1);
-
-        $qsummary = $mc->get_question_summary();
-
-        $this->assertRegExp('/' . preg_quote($mc->questiontext) . '/', $qsummary);
-        foreach ($mc->answers as $answer) {
-            $this->assertRegExp('/' . preg_quote($answer->answer) . '/', $qsummary);
-        }
-    }
-
-    public function test_summarise_response() {
-        $mc = test_question_maker::make_a_multichoice_multi_question();
-        $mc->shuffleanswers = false;
-        $mc->start_attempt(new question_attempt_step(), 1);
-
-        $summary = $mc->summarise_response(array('choice1' => 1, 'choice2' => 1),
-                test_question_maker::get_a_qa($mc));
-
-        $this->assertEquals('B; C', $summary);
-    }
-
-    public function test_classify_response() {
-        $mc = test_question_maker::make_a_multichoice_multi_question();
-        $mc->shuffleanswers = false;
-        $mc->start_attempt(new question_attempt_step(), 1);
-
-        $this->assertEquals(array(
-                    13 => new question_classified_response(13, 'A', 0.5),
-                    14 => new question_classified_response(14, 'B', -1.0),
-                ), $mc->classify_response(array('choice0' => 1, 'choice1' => 1)));
-
-        $this->assertEquals(array(), $mc->classify_response(array()));
-    }
-}
index c8ec1e4..3931e71 100644 (file)
@@ -44,7 +44,7 @@ function report_progress_extend_navigation_course($navigation, $course, $context
     }
 
     $completion = new completion_info($course);
-    $showonnavigation = ($showonnavigation && $completion->is_enabled() && count($completion->get_activities())>0);
+    $showonnavigation = ($showonnavigation && $completion->is_enabled() && $completion->has_activities());
     if ($showonnavigation) {
         $url = new moodle_url('/report/progress/index.php', array('course'=>$course->id));
         $navigation->add(get_string('pluginname','report_progress'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
index c98e0ec..0a151a0 100644 (file)
@@ -2796,6 +2796,7 @@ final class repository_instance_form extends moodleform {
         $instance = (isset($this->_customdata['instance'])
                 && is_subclass_of($this->_customdata['instance'], 'repository'))
             ? $this->_customdata['instance'] : null;
+
         if (!$instance) {
             $errors = repository::static_function($plugin, 'instance_form_validation', $this, $data, $errors);
         } else {
@@ -2806,6 +2807,10 @@ final class repository_instance_form extends moodleform {
                   FROM {repository_instances} i, {repository} r
                  WHERE r.type=:plugin AND r.id=i.typeid AND i.name=:name AND i.contextid=:contextid";
         $params = array('name' => $data['name'], 'plugin' => $this->plugin, 'contextid' => $this->contextid);
+        if ($instance) {
+            $sql .= ' AND i.id != :instanceid';
+            $params['instanceid'] = $instance->id;
+        }
         if ($DB->count_records_sql($sql, $params) > 0) {
             $errors['name'] = get_string('erroruniquename', 'repository');
         }
index 1ddeb26..e9baecf 100644 (file)
@@ -103,7 +103,7 @@ if ($rev > -1) {
             header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
             header('Cache-Control: public, max-age='.$lifetime);
             header('Content-Type: '.$mimetype);
-            header('Etag: '.$etag);
+            header('Etag: "'.$etag.'"');
             die;
         }
         send_cached_image($cacheimage, $etag);
@@ -184,7 +184,7 @@ function send_cached_image($imagepath, $etag) {
 
     $mimetype = get_contenttype_from_ext($pathinfo['extension']);
 
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     header('Content-Disposition: inline; filename="'.$imagename.'"');
     header('Last-Modified: '. gmdate('D, d M Y H:i:s', filemtime($imagepath)) .' GMT');
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
diff --git a/theme/sky_high/pix/footer-rtl.png b/theme/sky_high/pix/footer-rtl.png
new file mode 100644 (file)
index 0000000..7ba3703
Binary files /dev/null and b/theme/sky_high/pix/footer-rtl.png differ
index 34365a8..f2d649c 100644 (file)
Binary files a/theme/sky_high/pix/footer.png and b/theme/sky_high/pix/footer.png differ
index 949de96..92264ae 100644 (file)
@@ -7,10 +7,10 @@ body.pagelayout-admin.has_dock {
     width: 100%;
     margin: 0 auto;
     position: relative;
+    max-width: 1664px;
 }
 .pagelayout-admin #page-footer {
     float: none;
-    background: #f3f3f3 none;
     width: 100%;
     margin: 0;
     padding: 0;
index d243e8e..644fb49 100644 (file)
@@ -10,9 +10,9 @@ body {
 }
 
 #page {
-    width:100%;
-    max-width: 1600px;
-    margin:0 auto;
+    width: 100%;
+    max-width: 1760px;
+    margin: 0 auto;
 }
 
 #page-content {
index a9bdbb3..0859374 100644 (file)
@@ -1,5 +1,8 @@
 /** Report layout **/
 
+.pagelayout-report #page {
+    max-width: 1664px;
+}
 #report-wrapper {
     background: #fff;
     margin: 20px 2% 0;
index 6f4f49e..60971e9 100644 (file)
@@ -61,7 +61,7 @@ if (strpos($parts, '/-1/') === false and (!empty($_SERVER['HTTP_IF_NONE_MATCH'])
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
     header('Cache-Control: public, max-age='.$lifetime);
     header('Content-Type: '.$mimetype);
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     die;
 }
 
@@ -170,7 +170,7 @@ function combo_send_cached($content, $mimetype, $etag, $lastmodified) {
     header('Cache-Control: public, max-age='.$lifetime);
     header('Accept-Ranges: none');
     header('Content-Type: '.$mimetype);
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     if (!min_enable_zlib_compression()) {
         header('Content-Length: '.strlen($content));
     }
index dd092d6..4955936 100644 (file)
@@ -92,7 +92,7 @@ if (strpos($path, '/-1/') === false and (!empty($_SERVER['HTTP_IF_NONE_MATCH'])
     header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT');
     header('Cache-Control: public, max-age='.$lifetime);
     header('Content-Type: '.$mimetype);
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
     die;
 }
 
@@ -113,7 +113,7 @@ function yui_image_cached($imagepath, $imagename, $mimetype, $etag) {
     header('Accept-Ranges: none');
     header('Content-Type: '.$mimetype);
     header('Content-Length: '.filesize($imagepath));
-    header('Etag: '.$etag);
+    header('Etag: "'.$etag.'"');
 
     if (xsendfile($imagepath)) {
         die;
index bfa603d..7aa5e2d 100644 (file)
         $PAGE->requires->js_init_call('M.core_user.init_participation', null, false, $module);
     }
 
-    if (has_capability('moodle/site:viewparticipants', $context) && $totalcount > ($perpage*3)) {
+    // Show a search box if all participants don't fit on a single screen
+    if ($totalcount > $perpage) {
         echo '<form action="index.php" class="searchform"><div><input type="hidden" name="id" value="'.$course->id.'" />';
         echo '<label for="search">' . get_string('search', 'search') . ' </label>';
         echo '<input type="text" id="search" name="search" value="'.s($search).'" />&nbsp;<input type="submit" value="'.get_string('search').'" /></div></form>'."\n";
index d6f502f..9162a43 100644 (file)
@@ -97,7 +97,7 @@ if (!$currentpage->userid) {
 }
 
 $PAGE->set_context($context);
-$PAGE->set_pagelayout('mydashboard');
+$PAGE->set_pagelayout('mypublic');
 $PAGE->set_pagetype('user-profile');
 
 // Set up block editing capabilities
index 120fb78..f1bd9d3 100644 (file)
@@ -270,6 +270,7 @@ M.core_user.init_user_selector = function (Y, name, hash, extrafields, lastsearc
                     option.set('selected', true);
                 }
             } else {
+                optgroup.set('label', groupname);
                 optgroup.append(Y.Node.create('<option disabled="disabled">\u00A0</option>'));
             }
             this.listbox.append(optgroup);
@@ -374,4 +375,4 @@ M.core_user.init_user_selector_options_tracker = function(Y) {
     user_selector_options_tracker.init();
     // Return it just incase it is ever wanted
     return user_selector_options_tracker;
-};
\ No newline at end of file
+};
index 37d66a9..bc166b2 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
-$version  = 2012062507.04;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012062507.06;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes
 
-$release  = '2.3.7+ (Build: 20130530)';  // Human-friendly version name
+$release  = '2.3.7+ (Build: 20130614)';  // Human-friendly version name
 
 $branch   = '23';                       // this version's branch
 $maturity = MATURITY_STABLE;            // this version's maturity level