Merge branch 'MDL-35593-master' of git://github.com/mouneyrac/moodle
authorSam Hemelryk <sam@moodle.com>
Tue, 11 Dec 2012 20:38:43 +0000 (09:38 +1300)
committerSam Hemelryk <sam@moodle.com>
Tue, 11 Dec 2012 20:38:43 +0000 (09:38 +1300)
272 files changed:
admin/renderer.php
admin/tool/customlang/db/upgrade.php
admin/tool/uploaduser/index.php
auth/ldap/auth.php
auth/manual/db/upgrade.php
auth/mnet/db/upgrade.php
auth/shibboleth/index.php
backup/backup.class.php
backup/moodle2/backup_activity_task.class.php
backup/moodle2/restore_activity_task.class.php
backup/moodle2/restore_stepslib.php
backup/util/loggers/file_logger.class.php
backup/util/loggers/output_indented_logger.class.php
backup/util/loggers/output_text_logger.class.php
backup/util/ui/backup_ui_setting.class.php
backup/util/ui/renderer.php
blocks/community/block_community.php
blocks/community/db/upgrade.php
blocks/course_overview/db/access.php
blocks/course_overview/lang/en/block_course_overview.php
blocks/course_overview/renderer.php
blocks/course_overview/version.php
blocks/dock.js
blocks/glossary_random/block_glossary_random.php
blocks/html/db/upgrade.php
blocks/navigation/db/upgrade.php
blocks/online_users/block_online_users.php
blocks/settings/db/upgrade.php
cache/classes/definition.php
cache/forms.php
cache/testperformance.php
calendar/yui/eventmanager/eventmanager.js
composer.json
course/dndupload.js
course/externallib.php
course/lib.php
course/recent_form.php
course/rest.php
enrol/README.txt
enrol/authorize/db/upgrade.php
enrol/database/db/upgrade.php
enrol/flatfile/db/upgrade.php
enrol/guest/db/upgrade.php
enrol/imsenterprise/db/upgrade.php
enrol/manual/db/upgrade.php
enrol/mnet/db/upgrade.php
enrol/paypal/db/upgrade.php
enrol/paypal/ipn.php
enrol/paypal/lib.php
enrol/self/db/upgrade.php
filter/mediaplugin/db/upgrade.php
filter/tex/db/upgrade.php
grade/grading/form/rubric/db/upgrade.php
grade/report/grader/styles.css
grade/report/lib.php
grade/tests/reportlib_test.php [new file with mode: 0644]
install/lang/es/install.php
install/lang/es_mx/langconfig.php
lang/en/message.php
lang/en/plugin.php
lib/accesslib.php
lib/ajax/blocks.php
lib/datalib.php
lib/db/upgrade.php
lib/ddl/oracle_sql_generator.php
lib/dml/pdo_moodle_database.php
lib/dml/tests/dml_test.php
lib/editor/tinymce/db/upgrade.php
lib/editor/tinymce/plugins/spellchecker/db/upgrade.php
lib/excellib.class.php
lib/filelib.php
lib/filestorage/tests/file_storage_test.php
lib/formslib.php
lib/grade/grade_category.php
lib/javascript-static.js
lib/moodlelib.php
lib/navigationlib.php
lib/outputlib.php
lib/outputrenderers.php
lib/phpunit/readme.md
lib/pluginlib.php
lib/sessionlib.php
lib/tablelib.php
lib/tests/moodlelib_test.php
lib/webdavlib.php
lib/yui/blocks/blocks.js
mdeploy.php
mdeploytest.php
message/edit.php
message/lib.php
message/output/email/db/upgrade.php
message/output/jabber/db/upgrade.php
message/output/popup/db/upgrade.php
mod/assign/assignmentplugin.php
mod/assign/db/upgrade.php
mod/assign/feedback/comments/db/upgrade.php
mod/assign/feedback/file/db/upgrade.php
mod/assign/feedback/file/importzipform.php
mod/assign/feedback/offline/importgradesform.php
mod/assign/gradingbatchoperationsform.php
mod/assign/gradingoptionsform.php
mod/assign/gradingtable.php
mod/assign/lang/en/assign.php
mod/assign/locallib.php
mod/assign/pix/gradefeedback.png [new file with mode: 0644]
mod/assign/pix/gradefeedback.svg [new file with mode: 0644]
mod/assign/renderer.php
mod/assign/styles.css
mod/assign/submission/comments/db/upgrade.php
mod/assign/submission/comments/locallib.php
mod/assign/submission/file/db/upgrade.php
mod/assign/submission/onlinetext/db/upgrade.php
mod/assign/submission/onlinetext/locallib.php
mod/assign/upgradelib.php
mod/assignment/db/upgrade.php
mod/assignment/lib.php
mod/book/db/upgrade.php
mod/chat/db/upgrade.php
mod/choice/db/upgrade.php
mod/data/db/upgrade.php
mod/data/field/latlong/kml.php
mod/data/view.php
mod/feedback/db/upgrade.php
mod/feedback/item/captcha/captcha_form.php
mod/feedback/item/multichoice/multichoice_form.php
mod/feedback/item/multichoicerated/multichoicerated_form.php
mod/feedback/item/numeric/numeric_form.php
mod/feedback/item/textarea/textarea_form.php
mod/feedback/item/textfield/textfield_form.php
mod/folder/db/upgrade.php
mod/forum/db/upgrade.php
mod/glossary/db/upgrade.php
mod/imscp/db/upgrade.php
mod/label/db/upgrade.php
mod/lesson/db/upgrade.php
mod/lesson/format.php
mod/lesson/view.php
mod/lti/backup/moodle2/backup_lti_stepslib.php
mod/lti/db/upgrade.php
mod/page/db/upgrade.php
mod/quiz/accessrule/upgrade.txt
mod/quiz/db/upgrade.php
mod/quiz/locallib.php
mod/quiz/report/attemptsreport_table.php
mod/quiz/report/overview/db/upgrade.php
mod/quiz/report/statistics/db/upgrade.php
mod/quiz/summary.php
mod/resource/db/upgrade.php
mod/scorm/db/upgrade.php
mod/scorm/lib.php
mod/survey/db/upgrade.php
mod/url/db/upgrade.php
mod/wiki/db/upgrade.php
mod/wiki/pagelib.php
mod/wiki/parser/utils.php
mod/workshop/db/upgrade.php
mod/workshop/form/accumulative/db/upgrade.php
mod/workshop/form/comments/db/upgrade.php
mod/workshop/form/numerrors/db/upgrade.php
mod/workshop/form/rubric/db/upgrade.php
mod/workshop/lib.php
pix/c/course.png [new file with mode: 0644]
pix/c/course.svg [new file with mode: 0644]
pix/c/group.png [new file with mode: 0644]
pix/c/group.svg [new file with mode: 0644]
pix/c/site.png [new file with mode: 0644]
pix/c/site.svg [new file with mode: 0644]
pix/c/user.png [new file with mode: 0644]
pix/c/user.svg [new file with mode: 0644]
pix/i/approve.png [new file with mode: 0644]
pix/i/approve.svg [new file with mode: 0644]
pix/i/calendar.png [new file with mode: 0644]
pix/i/calendar.svg [new file with mode: 0644]
pix/i/configlock.png [new file with mode: 0644]
pix/i/configlock.svg [new file with mode: 0644]
pix/i/cross_red_big.png [new file with mode: 0644]
pix/i/cross_red_big.svg [new file with mode: 0644]
pix/i/cross_red_small.png [new file with mode: 0644]
pix/i/cross_red_small.svg [new file with mode: 0644]
pix/i/db.png [new file with mode: 0644]
pix/i/db.svg [new file with mode: 0644]
pix/i/files.png [new file with mode: 0644]
pix/i/files.svg [new file with mode: 0644]
pix/i/hierarchylock.png [new file with mode: 0644]
pix/i/hierarchylock.svg [new file with mode: 0644]
pix/i/permissionlock.png [new file with mode: 0644]
pix/i/permissionlock.svg [new file with mode: 0644]
pix/i/reload.png [new file with mode: 0644]
pix/i/reload.svg [new file with mode: 0644]
pix/i/risk_config.png [new file with mode: 0644]
pix/i/risk_config.svg [new file with mode: 0644]
pix/i/risk_dataloss.png [new file with mode: 0644]
pix/i/risk_dataloss.svg [new file with mode: 0644]
pix/i/risk_managetrust.png [new file with mode: 0644]
pix/i/risk_managetrust.svg [new file with mode: 0644]
pix/i/risk_personal.png [new file with mode: 0644]
pix/i/risk_personal.svg [new file with mode: 0644]
pix/i/risk_spam.png [new file with mode: 0644]
pix/i/risk_spam.svg [new file with mode: 0644]
pix/i/risk_xss.png [new file with mode: 0644]
pix/i/risk_xss.svg [new file with mode: 0644]
pix/i/roles.png [new file with mode: 0644]
pix/i/roles.svg [new file with mode: 0644]
pix/i/search.png [new file with mode: 0644]
pix/i/search.svg [new file with mode: 0644]
pix/i/switchrole.png
pix/i/switchrole.svg
pix/i/tick_amber_big.png [new file with mode: 0644]
pix/i/tick_amber_big.svg [new file with mode: 0644]
pix/i/tick_amber_small.png [new file with mode: 0644]
pix/i/tick_amber_small.svg [new file with mode: 0644]
pix/i/tick_green_big.png [new file with mode: 0644]
pix/i/tick_green_big.svg [new file with mode: 0644]
pix/i/tick_green_small.png [new file with mode: 0644]
pix/i/tick_green_small.svg [new file with mode: 0644]
pix/t/addgreen.png [new file with mode: 0644]
pix/t/addgreen.svg [new file with mode: 0644]
pix/t/clear.png [new file with mode: 0644]
pix/t/clear.svg [new file with mode: 0644]
pix/t/contextmenu.png [new file with mode: 0644]
pix/t/contextmenu.svg [new file with mode: 0644]
pix/t/manual_item.png [new file with mode: 0644]
pix/t/manual_item.svg [new file with mode: 0644]
pix/t/unlock_gray.png [new file with mode: 0644]
pix/t/unlock_gray.svg [new file with mode: 0644]
pix/t/user.png [new file with mode: 0644]
pix/t/user.svg [new file with mode: 0644]
pix/t/userblue.png [new file with mode: 0644]
pix/t/userblue.svg [new file with mode: 0644]
portfolio/googledocs/db/upgrade.php
portfolio/picasa/db/upgrade.php
question/behaviour/upgrade.txt
question/export.php
question/format.php
question/format/blackboard_six/formatbase.php
question/format/blackboard_six/formatpool.php
question/format/blackboard_six/formatqti.php
question/format/blackboard_six/tests/blackboardformatpool_test.php
question/format/blackboard_six/tests/blackboardsixformatqti_test.php
question/format/upgrade.txt
question/format/xml/format.php
question/type/calculated/db/upgrade.php
question/type/calculated/questiontype.php
question/type/essay/db/upgrade.php
question/type/match/db/upgrade.php
question/type/multianswer/db/upgrade.php
question/type/multianswer/renderer.php
question/type/multichoice/db/upgrade.php
question/type/numerical/db/upgrade.php
question/type/rendererbase.php
report/log/locallib.php
repository/dropbox/db/upgrade.php
repository/googledocs/db/upgrade.php
repository/picasa/db/upgrade.php
repository/webdav/lib.php
theme/base/style/core.css
theme/base/style/dock.css
theme/base/style/question.css
theme/base/style/user.css
theme/formal_white/db/upgrade.php
theme/magazine/style/core.css
theme/mymobile/javascript/custom.js
theme/mymobile/javascript/jquery.mobile-1.1.1.js
theme/mymobile/readme_moodle.txt [new file with mode: 0644]
theme/mymobile/style/core.css
theme/sky_high/style/admin.css
theme/standardold/layout/frontpage.php
theme/styles.php
theme/styles_debug.php
theme/upgrade.txt
user/index.php
version.php

index de78470..8d49636 100644 (file)
@@ -691,7 +691,7 @@ class core_admin_renderer extends plugin_renderer_base {
     function upgrade_reload($url) {
         return html_writer::empty_tag('br') .
                 html_writer::tag('div',
-                    html_writer::link($url, $this->pix_icon('i/reload', '') .
+                    html_writer::link($url, $this->pix_icon('i/reload', '', '', array('class' => 'icon icon-pre')) .
                             get_string('reload'), array('title' => get_string('reload'))),
                 array('class' => 'continuebutton')) . html_writer::empty_tag('br');
     }
@@ -1211,8 +1211,13 @@ class core_admin_renderer extends plugin_renderer_base {
             if (empty($impediments)) {
                 $widget = $deployer->make_confirm_widget($updateinfo);
                 $box .= $this->output->render($widget);
-            } else if (isset($impediments['notwritable'])) {
-                $box .= $this->output->help_icon('notwritable', 'core_plugin', get_string('notwritable', 'core_plugin'));
+            } else {
+                if (isset($impediments['notwritable'])) {
+                    $box .= $this->output->help_icon('notwritable', 'core_plugin', get_string('notwritable', 'core_plugin'));
+                }
+                if (isset($impediments['notdownloadable'])) {
+                    $box .= $this->output->help_icon('notdownloadable', 'core_plugin', get_string('notdownloadable', 'core_plugin'));
+                }
             }
         }
 
index 07c7e3b..c8bc6b0 100644 (file)
@@ -34,5 +34,9 @@ function xmldb_tool_customlang_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 8db37d0..632cfe1 100644 (file)
@@ -900,7 +900,11 @@ if ($formdata = $mform2->is_cancelled()) {
                     $newgroupdata = new stdClass();
                     $newgroupdata->name = $addgroup;
                     $newgroupdata->courseid = $ccache[$shortname]->id;
-                    if ($ccache[$shortname]->groups[$addgroup]->id = groups_create_group($newgroupdata)){
+                    $newgroupdata->description = '';
+                    $gid = groups_create_group($newgroupdata);
+                    if ($gid){
+                        $ccache[$shortname]->groups[$addgroup] = new stdClass();
+                        $ccache[$shortname]->groups[$addgroup]->id   = $gid;
                         $ccache[$shortname]->groups[$addgroup]->name = $newgroupdata->name;
                     } else {
                         $upt->track('enrolments', get_string('unknowngroup', 'error', s($addgroup)), 'error');
index fed0bae..e8abd12 100644 (file)
@@ -711,7 +711,7 @@ class auth_plugin_ldap extends auth_plugin_base {
 /// User removal
         // Find users in DB that aren't in ldap -- to be removed!
         // this is still not as scalable (but how often do we mass delete?)
-        if ($this->config->removeuser !== AUTH_REMOVEUSER_KEEP) {
+        if ($this->config->removeuser != AUTH_REMOVEUSER_KEEP) {
             $sql = 'SELECT u.*
                       FROM {user} u
                       LEFT JOIN {tmp_extuser} e ON (u.username = e.username AND u.mnethostid = e.mnethostid)
index 46fd720..0c37b61 100644 (file)
@@ -37,5 +37,9 @@ function xmldb_auth_manual_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index d10cda5..2f65822 100644 (file)
@@ -38,5 +38,9 @@ function xmldb_auth_mnet_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 88caf30..61f6ba8 100644 (file)
@@ -7,7 +7,10 @@
     $PAGE->set_url('/auth/shibboleth/index.php');
 
     // Support for WAYFless URLs.
-    $SESSION->wantsurl = optional_param('target', $SESSION->wantsurl, PARAM_LOCALURL);
+    $target = optional_param('target', '', PARAM_LOCALURL);
+    if (!empty($target)) {
+        $SESSION->wantsurl = $target;
+    }
 
     if (isloggedin() && !isguestuser()) {      // Nothing to do
         if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0)) {
index 40249d0..d60142f 100644 (file)
@@ -110,7 +110,7 @@ abstract class backup implements checksumable {
 
     // Version (to keep CFG->backup_version (and release) updated automatically)
     const VERSION = 2012112900;
-    const RELEASE = '2.4';
+    const RELEASE = '2.5';
 }
 
 /*
index a597425..a1ada38 100644 (file)
@@ -258,7 +258,8 @@ abstract class backup_activity_task extends backup_task {
         // - section_included setting (if exists)
         $settingname = $settingprefix . 'included';
         $activity_included = new backup_activity_generic_setting($settingname, base_setting::IS_BOOLEAN, true);
-        $activity_included->get_ui()->set_icon(new pix_icon('icon', get_string('pluginname', $this->modulename), $this->modulename));
+        $activity_included->get_ui()->set_icon(new pix_icon('icon', get_string('pluginname', $this->modulename),
+            $this->modulename, array('class' => 'iconlarge icon-post')));
         $this->add_setting($activity_included);
         // Look for "activities" root setting
         $activities = $this->plan->get_setting('activities');
index 5838a8e..cac6824 100644 (file)
@@ -278,7 +278,8 @@ abstract class restore_activity_task extends restore_task {
         // - section_included setting (if exists)
         $settingname = $settingprefix . 'included';
         $activity_included = new restore_activity_generic_setting($settingname, base_setting::IS_BOOLEAN, true);
-        $activity_included->get_ui()->set_icon(new pix_icon('icon', get_string('pluginname', $this->modulename), $this->modulename));
+        $activity_included->get_ui()->set_icon(new pix_icon('icon', get_string('pluginname', $this->modulename),
+            $this->modulename, array('class' => 'iconlarge icon-post')));
         $this->add_setting($activity_included);
         // Look for "activities" root setting
         $activities = $this->plan->get_setting('activities');
index fe4f504..1a5604e 100644 (file)
@@ -190,6 +190,15 @@ class restore_gradebook_structure_step extends restore_structure_step {
                 $data->id = $newitemid = $existinggradeitem->id;
                 $DB->update_record('grade_items', $data);
             }
+        } else if ($data->itemtype == 'manual') {
+            // Manual items aren't assigned to a cm, so don't go duplicating them in the target if one exists.
+            $gi = array(
+                'itemtype' => $data->itemtype,
+                'courseid' => $data->courseid,
+                'itemname' => $data->itemname,
+                'categoryid' => $data->categoryid,
+            );
+            $newitemid = $DB->get_field('grade_items', 'id', $gi);
         }
 
         if (empty($newitemid)) {
@@ -3072,9 +3081,6 @@ class restore_create_categories_and_questions extends restore_structure_step {
             $data->penalty = 1;
         }
 
-        $data->timecreated  = $this->apply_date_offset($data->timecreated);
-        $data->timemodified = $this->apply_date_offset($data->timemodified);
-
         $userid = $this->get_mappingid('user', $data->createdby);
         $data->createdby = $userid ? $userid : $this->task->get_userid();
 
index e4ab4b1..5c05380 100644 (file)
@@ -75,7 +75,7 @@ class file_logger extends base_logger {
         if (substr($this->fullpath, -5) !== '.html') {
             $content = $prefix . str_repeat('  ', $depth) . $message . PHP_EOL;
         } else {
-            $content = $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES) . '<br/>' . PHP_EOL;
+            $content = $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
         }
         if (false === fwrite($this->fhandle, $content)) {
             throw new base_logger_exception('error_writing_file', $this->fullpath);
index 8bb6811..2eaea5b 100644 (file)
@@ -38,7 +38,7 @@ class output_indented_logger extends base_logger {
         if (defined('STDOUT')) {
             echo $prefix . str_repeat('  ', $depth) . $message . PHP_EOL;
         } else {
-            echo $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES) . '<br/>' . PHP_EOL;
+            echo $prefix . str_repeat('&nbsp;&nbsp;', $depth) . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
         }
         flush();
         return true;
index fe61536..9d13dfd 100644 (file)
@@ -37,7 +37,7 @@ class output_text_logger extends base_logger {
         if (defined('STDOUT')) {
             echo $prefix . $message . PHP_EOL;
         } else {
-            echo $prefix . htmlentities($message, ENT_QUOTES) . '<br/>' . PHP_EOL;
+            echo $prefix . htmlentities($message, ENT_QUOTES, 'UTF-8') . '<br/>' . PHP_EOL;
         }
         flush();
         return true;
index c778fb7..e660c4a 100644 (file)
@@ -329,7 +329,7 @@ class backup_setting_ui_text extends backup_setting_ui {
         $icon = $this->get_icon();
         $label = $this->get_label($task);
         if (!empty($icon)) {
-            $label .= '&nbsp;'.$output->render($icon);
+            $label .= $output->render($icon);
         }
         // name, label, attributes
         return $this->apply_options(array('element'=>'text','name'=>self::NAME_PREFIX.$this->name, 'label'=>$label, 'attributes'=>$this->attributes));
@@ -380,7 +380,7 @@ class backup_setting_ui_checkbox extends backup_setting_ui {
         $icon = $this->get_icon();
         $label = $this->get_label($task);
         if (!empty($icon)) {
-            $label .= '&nbsp;'.$output->render($icon);
+            $label .= $output->render($icon);
         }
         return $this->apply_options(array('element'=>'checkbox','name'=>self::NAME_PREFIX.$this->name, 'label'=>$label, 'text'=>$this->text, 'attributes'=>$this->attributes));
     }
@@ -473,7 +473,7 @@ class backup_setting_ui_radio extends backup_setting_ui {
         $icon = $this->get_icon();
         $label = $this->get_label($task);
         if (!empty($icon)) {
-            $label .= '&nbsp;'.$output->render($icon);
+            $label .= $output->render($icon);
         }
         // name, label, text, value, attributes
         return $this->apply_options(array('element'=>'radio','name'=>self::NAME_PREFIX.$this->name, 'label'=>$label, 'text'=>$this->text, 'value'=>$this->value, 'attributes'=>$this->attributes));
@@ -538,7 +538,7 @@ class backup_setting_ui_select extends backup_setting_ui {
         $icon = $this->get_icon();
         $label = $this->get_label($task);
         if (!empty($icon)) {
-            $label .= '&nbsp;'.$output->render($icon);
+            $label .= $output->render($icon);
         }
         // name, label, options, attributes
         return $this->apply_options(array('element'=>'select','name'=>self::NAME_PREFIX.$this->name, 'label'=>$label, 'options'=>$this->values, 'attributes'=>$this->attributes));
index c58f367..7b8dfe2 100644 (file)
@@ -147,9 +147,9 @@ class core_backup_renderer extends plugin_renderer_base {
                         $table->data = array();
                     }
                     $name = get_string('pluginname', $activity->modulename);
-                    $icon = new pix_icon('icon', $name, $activity->modulename);
+                    $icon = new pix_icon('icon', $name, $activity->modulename, array('class' => 'iconlarge icon-pre'));
                     $table->data[] = array(
-                        $this->output->render($icon).'&nbsp;'.$name,
+                        $this->output->render($icon).$name,
                         $activity->title,
                         ($activity->settings[$activitykey.'_userinfo'])?$yestick:$notick,
                     );
index 3702f11..5609581 100644 (file)
@@ -69,7 +69,7 @@ class block_community extends block_list {
         }
 
         $icon = html_writer::empty_tag('img', array('src' => $OUTPUT->pix_url('i/group'),
-                    'class' => 'icon', 'alt' => get_string('addcourse', 'block_community')));
+                    'class' => 'icon', 'alt' => ""));
         $addcourseurl = new moodle_url('/blocks/community/communitycourse.php',
                         array('add' => true, 'courseid' => $this->page->course->id));
         $searchlink = html_writer::tag('a', $icon . get_string('addcourse', 'block_community'),
index 53e2222..c0fb9f9 100644 (file)
@@ -50,5 +50,9 @@ function xmldb_block_community_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 828135c..00752d3 100644 (file)
@@ -34,18 +34,5 @@ $capabilities = array(
         ),
 
         'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
-    'block/course_overview:addinstance' => array(
-        'riskbitmask' => RISK_SPAM | RISK_XSS,
-
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_BLOCK,
-        'archetypes' => array(
-            'editingteacher' => CAP_ALLOW,
-            'manager' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/site:manageblocks'
-    ),
+    )
 );
index d7be855..3535110 100644 (file)
@@ -27,7 +27,6 @@ $string['alwaysshowall'] = 'Always Show All';
 $string['collapseall'] = 'Collapse All Course Lists';
 $string['configotherexpanded'] = 'If enabled, Other Courses will be expanded by default unless overriden by user preferences.';
 $string['configpreservestates'] = 'If enabled, the collapsed/expanded states set by the user are stored and used on each load.';
-$string['course_overview:addinstance'] = 'Add a new course overview block';
 $string['course_overview:myaddinstance'] = 'Add a new course overview block to the My Moodle page';
 $string['defaultmaxcourses'] = 'Default maximum courses';
 $string['defaultmaxcoursesdesc'] = 'Maximum courses which should be displayed on course overview block, 0 will show all courses';
index 9e5a93a..8c42428 100644 (file)
@@ -106,7 +106,9 @@ class block_course_overview_renderer extends plugin_renderer_base {
 
             $attributes = array('title' => s($course->fullname));
             if ($course->id > 0) {
-                $link = html_writer::link(new moodle_url('/course/view.php', array('id' => $course->id)), format_string($course->shortname, true, $course->id), $attributes);
+                $courseurl = new moodle_url('/course/view.php', array('id' => $course->id));
+                $coursefullname = format_string($course->fullname, true, $course->id);
+                $link = html_writer::link($courseurl, $coursefullname, $attributes);
                 $html .= $this->output->heading($link, 2, 'title');
             } else {
                 $html .= $this->output->heading(html_writer::link(
index 51a734f..40abe52 100644 (file)
@@ -24,6 +24,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2012121000;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012112900;        // Requires this Moodle version
-$plugin->component = 'block_course_overview'; // Full name of the plugin (used for diagnostics)
\ No newline at end of file
+$plugin->component = 'block_course_overview'; // Full name of the plugin (used for diagnostics)
index 6e56390..73249e9 100644 (file)
@@ -516,7 +516,7 @@ M.core_dock.fixTitleOrientation = function(item, title, text) {
     // We need to fix a font-size - sorry theme designers.
     var fontsize = '11px';
     var transform = (clockwise) ? 'rotate(90deg)' : 'rotate(270deg)';
-    var test = Y.Node.create('<h2><span style="font-size:'+fontsize+';position:absolute;">'+text+'</span></h2>');
+    var test = Y.Node.create('<h2><span class="transform-test-node" style="font-size:'+fontsize+';">'+text+'</span></h2>');
     this.nodes.body.insert(test, 0);
     var width = test.one('span').get('offsetWidth') * 1.2;
     var height = test.one('span').get('offsetHeight');
index 916780d..1eccd11 100644 (file)
@@ -137,6 +137,7 @@ class block_glossary_random extends block_base {
             $this->config->cache = '';
             $this->instance_config_commit();
 
+            $this->content = new stdClass();
             $this->content->text   = get_string('notyetconfigured','block_glossary_random');
             $this->content->footer = '';
             return $this->content;
index 54bb53d..c7ca26b 100644 (file)
@@ -36,5 +36,9 @@ function xmldb_block_html_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 32c5d8d..7476af8 100644 (file)
@@ -62,5 +62,9 @@ function xmldb_block_navigation_upgrade($oldversion, $block) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
\ No newline at end of file
index 2efe9a2..77f8a69 100644 (file)
@@ -151,12 +151,13 @@ class block_online_users extends block_base {
                 $timeago = format_time($now - $user->lastaccess); //bruno to calculate correctly on frontpage
 
                 if (isguestuser($user)) {
-                    $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16));
+                    $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16, 'alttext'=>false));
                     $this->content->text .= get_string('guestuser').'</div>';
 
                 } else {
-                    $this->content->text .= '<div class="user">'.$OUTPUT->user_picture($user, array('size'=>16));
-                    $this->content->text .= '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$this->page->course->id.'" title="'.$timeago.'">'.$user->fullname.'</a></div>';
+                    $this->content->text .= '<div class="user">';
+                    $this->content->text .= '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$this->page->course->id.'" title="'.$timeago.'">';
+                    $this->content->text .= $OUTPUT->user_picture($user, array('size'=>16, 'alttext'=>false, 'link'=>false)) .$user->fullname.'</a></div>';
                 }
                 if ($canshowicon and ($USER->id != $user->id) and !isguestuser($user)) {  // Only when logged in and messaging active etc
                     $anchortagcontents = '<img class="iconsmall" src="'.$OUTPUT->pix_url('t/message') . '" alt="'. get_string('messageselectadd') .'" />';
index feb241a..ca5980f 100644 (file)
@@ -62,5 +62,9 @@ function xmldb_block_settings_upgrade($oldversion, $block) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
\ No newline at end of file
index 607f074..e278bbb 100644 (file)
@@ -750,7 +750,7 @@ class cache_definition {
             if (!empty($this->identifiers)) {
                 $identifiers = array();
                 foreach ($this->identifiers as $key => $value) {
-                    $identifiers[] = htmlentities($key).'='.htmlentities($value);
+                    $identifiers[] = htmlentities($key, ENT_QUOTES, 'UTF-8').'='.htmlentities($value, ENT_QUOTES, 'UTF-8');
                 }
                 $this->keyprefixmulti['identifiers'] = join('&', $identifiers);
             }
index 533f1f8..542e7a0 100644 (file)
@@ -101,7 +101,13 @@ class cachestore_addinstance_form extends moodleform {
         }
 
         if (method_exists($this, 'configuration_validation')) {
-            $errors = $this->configuration_validation($data, $files);
+            $newerrors = $this->configuration_validation($data, $files, $errors);
+            // We need to selectiviliy merge here
+            foreach ($newerrors as $element => $error) {
+                if (!array_key_exists($element, $errors)) {
+                    $errors[$element] = $error;
+                }
+            }
         }
 
         return $errors;
index 2d22751..26796c1 100644 (file)
@@ -61,7 +61,7 @@ foreach (get_plugin_list_with_file('cachestore', 'lib.php', true) as $plugin =>
     $class = 'cachestore_'.$plugin;
     $plugin = get_string('pluginname', 'cachestore_'.$plugin);
 
-    if (!class_exists($class) || !method_exists($class, 'initialise_test_instance')) {
+    if (!class_exists($class) || !method_exists($class, 'initialise_test_instance') || !$class::are_requirements_met()) {
         $applicationtable->data[] = array($plugin, $strinvalidplugin, '-', '-', '-', '-');
         $sessiontable->data[] = array($plugin, $strinvalidplugin, '-', '-', '-', '-');
         $requesttable->data[] = array($plugin, $strinvalidplugin, '-', '-', '-', '-');
index d07b94b..4aedc2e 100644 (file)
@@ -101,8 +101,10 @@ YUI.add('moodle-calendar-eventmanager', function(Y) {
             },
             node : {
                 setter : function(node) {
-                    var n = Y.one('#'+node);
-                    return n;
+                    if (typeof(node) === 'string') {
+                        node = Y.one('#'+node);
+                    }
+                    return node;
                 }
             },
             title : {
index 94bf886..5f618de 100644 (file)
@@ -1,5 +1,5 @@
 {
-    "require": {
+    "require-dev": {
         "phpunit/phpunit": "3.7.*",
         "phpunit/dbUnit": "1.2.*"
     }
index e1d1b11..d41bfe9 100644 (file)
@@ -442,6 +442,7 @@ M.course_dndupload = {
             parent: modsel,
             li: document.createElement('li'),
             div: document.createElement('div'),
+            indentdiv: document.createElement('div'),
             a: document.createElement('a'),
             icon: document.createElement('img'),
             namespan: document.createElement('span'),
@@ -452,24 +453,23 @@ M.course_dndupload = {
 
         resel.li.className = 'activity resource modtype_resource';
 
-        resel.div.className = 'mod-indent';
-        resel.li.appendChild(resel.div);
+        resel.indentdiv.className = 'mod-indent';
+        resel.li.appendChild(resel.indentdiv);
+
+        resel.div.className = 'activityinstance';
+        resel.indentdiv.appendChild(resel.div);
 
         resel.a.href = '#';
         resel.div.appendChild(resel.a);
 
         resel.icon.src = M.util.image_url('i/ajaxloader');
-        resel.icon.className = 'activityicon';
+        resel.icon.className = 'activityicon iconlarge';
         resel.a.appendChild(resel.icon);
 
-        resel.a.appendChild(document.createTextNode(' '));
-
         resel.namespan.className = 'instancename';
         resel.namespan.innerHTML = name;
         resel.a.appendChild(resel.namespan);
 
-        resel.div.appendChild(document.createTextNode(' '));
-
         resel.groupingspan.className = 'groupinglabel';
         resel.div.appendChild(resel.groupingspan);
 
@@ -738,7 +738,7 @@ M.course_dndupload = {
 
                             resel.div.removeChild(resel.progressouter);
                             resel.li.id = result.elementid;
-                            resel.div.innerHTML += result.commands;
+                            resel.indentdiv.innerHTML += result.commands;
                             if (result.onclick) {
                                 resel.a.onclick = result.onclick;
                             }
index 659228c..b5c82f4 100644 (file)
@@ -211,7 +211,7 @@ class core_course_external extends external_api {
                                 array(
                                     'id' => new external_value(PARAM_INT, 'activity id'),
                                     'url' => new external_value(PARAM_URL, 'activity url', VALUE_OPTIONAL),
-                                    'name' => new external_value(PARAM_TEXT, 'activity module name'),
+                                    'name' => new external_value(PARAM_RAW, 'activity module name'),
                                     'description' => new external_value(PARAM_RAW, 'activity description', VALUE_OPTIONAL),
                                     'visible' => new external_value(PARAM_INT, 'is the module visible', VALUE_OPTIONAL),
                                     'modicon' => new external_value(PARAM_URL, 'activity icon url'),
index 358de6b..1e81de9 100644 (file)
@@ -594,7 +594,7 @@ function print_log_csv($course, $user, $date, $order='l.time DESC', $modname,
             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
             $ldcache[$log->module][$log->action] = $ld;
         }
-        if ($ld && !empty($log->info)) {
+        if ($ld && is_numeric($log->info)) {
             // ugly hack to make sure fullname is shown correctly
             if (($ld->mtable == 'user') and ($ld->field ==  $DB->sql_concat('firstname', "' '" , 'lastname'))) {
                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
@@ -694,7 +694,7 @@ function print_log_xls($course, $user, $date, $order='l.time DESC', $modname,
             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
             $ldcache[$log->module][$log->action] = $ld;
         }
-        if ($ld && !empty($log->info)) {
+        if ($ld && is_numeric($log->info)) {
             // ugly hack to make sure fullname is shown correctly
             if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
@@ -808,7 +808,7 @@ function print_log_ods($course, $user, $date, $order='l.time DESC', $modname,
             $ld = $DB->get_record('log_display', array('module'=>$log->module, 'action'=>$log->action));
             $ldcache[$log->module][$log->action] = $ld;
         }
-        if ($ld && !empty($log->info)) {
+        if ($ld && is_numeric($log->info)) {
             // ugly hack to make sure fullname is shown correctly
             if (($ld->mtable == 'user') and ($ld->field == $DB->sql_concat('firstname', "' '" , 'lastname'))) {
                 $log->info = fullname($DB->get_record($ld->mtable, array('id'=>$log->info)), true);
@@ -1656,14 +1656,25 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                         } else {
                             $extraclass = '';
                         }
-                        echo "
-<form class='togglecompletion$extraclass' method='post' action='".$CFG->wwwroot."/course/togglecompletion.php'><div>
-<input type='hidden' name='id' value='{$mod->id}' />
-<input type='hidden' name='modulename' value='".s($mod->name)."' />
-<input type='hidden' name='sesskey' value='".sesskey()."' />
-<input type='hidden' name='completionstate' value='$newstate' />
-<input type='image' src='$imgsrc' alt='$imgalt' title='$imgtitle' />
-</div></form>";
+                        echo html_writer::start_tag('form', array(
+                                'class' => 'togglecompletion' . $extraclass,
+                                'method' => 'post',
+                                'action' => $CFG->wwwroot . '/course/togglecompletion.php'));
+                        echo html_writer::start_tag('div');
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'id', 'value' => $mod->id));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'modulename',
+                                'value' => $mod->name));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey()));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'hidden', 'name' => 'completionstate',
+                                'value' => $newstate));
+                        echo html_writer::empty_tag('input', array(
+                                'type' => 'image', 'src' => $imgsrc, 'alt' => $imgalt, 'title' => $imgtitle));
+                        echo html_writer::end_tag('div');
+                        echo html_writer::end_tag('form');
                     } else {
                         // In auto mode, or when editing, the icon is just an image
                         echo "<span class='autocompletion'>";
@@ -1830,7 +1841,7 @@ function print_section_add_menus($course, $section, $modnames = null, $vertical=
         // The module chooser link
         $modchooser = html_writer::start_tag('div', array('class' => 'mdl-right'));
         $modchooser.= html_writer::start_tag('div', array('class' => 'section-modchooser'));
-        $icon = $OUTPUT->pix_icon('t/add', $straddeither);
+        $icon = $OUTPUT->pix_icon('t/add', '');
         $span = html_writer::tag('span', $straddeither, array('class' => 'section-modchooser-text'));
         $modchooser .= html_writer::tag('span', $icon . $span, array('class' => 'section-modchooser-link'));
         $modchooser.= html_writer::end_tag('div');
@@ -4493,6 +4504,7 @@ function include_course_ajax($course, $usedmodules = array(), $enabledmodules =
         'courseid' => $course->id,
         'pagetype' => $PAGE->pagetype,
         'pagelayout' => $PAGE->pagelayout,
+        'subpage' => $PAGE->subpage,
         'regions' => $PAGE->blocks->get_regions(),
     );
     $PAGE->requires->yui_module('moodle-core-blocks', 'M.core_blocks.init_dragdrop', array($params), null, true);
index 349fd58..cfdbffa 100644 (file)
@@ -98,6 +98,9 @@ class recent_form extends moodleform {
 
             $mform->addElement('select', 'user', get_string('participants'), $options);
             $mform->setAdvanced('user');
+        } else {
+            // Default to no user.
+            $mform->addElement('hidden', 'user', 0);
         }
 
         $options = array(''=>get_string('allactivities'));
index 12371f8..8056c57 100644 (file)
@@ -146,6 +146,7 @@ switch($requestmethod) {
                         break;
                     case 'updatetitle':
                         require_capability('moodle/course:manageactivities', $modcontext);
+                        require_once($CFG->libdir . '/gradelib.php');
                         $cm = get_coursemodule_from_id('', $id, 0, false, MUST_EXIST);
                         $module = new stdClass();
                         $module->id = $cm->instance;
@@ -164,10 +165,16 @@ switch($requestmethod) {
                             $module->name = $cm->name;
                         }
 
+                        // Attempt to update the grade item if relevant
+                        $grademodule = $DB->get_record($cm->modname, array('id' => $cm->instance));
+                        $grademodule->cmidnumber = $cm->idnumber;
+                        $grademodule->modname = $cm->modname;
+                        grade_update_mod_grades($grademodule);
+
                         // We need to return strings after they've been through filters for multilang
                         $stringoptions = new stdClass;
                         $stringoptions->context = $coursecontext;
-                        echo json_encode(array('instancename' => format_string($module->name, true,  $stringoptions)));
+                        echo json_encode(array('instancename' => html_entity_decode(format_string($module->name, true,  $stringoptions))));
                         break;
                 }
                 break;
index 70ce970..d6a1887 100644 (file)
@@ -3,102 +3,7 @@ ENROLMENT MODULES
 
 (Yes, that's the correct English spelling  ;-) )
 
-enrol.class.php contains a simple 'factory' method that
-will instantiate your class when called. For an example
-of a complete class, take a look at the 'manual' class.
-
-Each plugin is in a subfolder here.
-
-Except for the configuration methods, most methods
-defined in the API are optional -- callers will use
-method_exists() to determine whether your plugin offers
-the functionality they are after.
-
-
-Mandatory methods
-=================
-
-  config_form()
-  process_config()
-
-
-Login-time methods
-==================
-
-  Before Moodle 1.7
-  -----------------
-
-      get_student_courses()
-      get_teacher_courses()
-
-  You probably will want to offer at least get_student_courses().
-
-  These methods are triggered when a user logs in successfully,
-  and they are expected to populate $USER->student and
-  $USER->teacher arrays and maintain (add/delete) entries from
-  user_students and user_teachers.
-
-  These methods are relevant for most plugins, and are the main
-  interest for plugins that work with a read-only backend such
-  as LDAP or a database.
-
-  Note that with the multi-enrol infrastructure two things have
-  changed. We now have an 'enrol' field in those tables, and
-  each plugin must maintain only its own enrolment records.
-  Conversely, the $USER->student and ->teacher arrays have the
-  enrolment type as value, like
-
-     $USER->student = array ( $courseid => $plugintype );
-
-
-  Moodle 1.7 and later
-  --------------------
-
-      setup_enrolments()
-
-  With the advent of roles, there could well not be students and
-  teachers any more, so enrolment plugins have to be more flexible
-  about how they map outside data to the internal roles.
-
-  This one method should do everything, calling functions from
-  lib/accesslib.php as necessary to set up relationships.
-
-
-Interactive enrolment methods
-=============================
-
-  print_entry()
-  check_entry()
-  check_group_entry()
-  get_access_icons()
-
-These methods are for enrolment plugins that allow for user
-driven enrolment. These methods are relevant for plugins
-that implement payment gateways (credit card, paypal),
-as well as "magic password" schemes.
-
-Only one interactive enrolment method can be active for
-a given course. The site default can be set from
-Admin->Enrolment, and then individual courses can be
-set to specific interactive enrolment methods.
-
-
-Cron
-====
-
-If your class offers a cron() method, it will be invoked by
-the standard Moodle cron every time it is called. Note that if the
-tasks are not lightweight you must control how frequently they
-execute, perhaps offering a config option.
-
-For really heavy cron processing, an alternative is to have
-a separate script to be called separately. Currently the LDAP
-and DB plugins have external scripts.
-
-
-Guilty Parties
---------------
-
-Martin Dougiamas and Shane Elliott, Moodle.com
-Martin Langhoff and Patrick Li, Catalyst IT
+All enrolment modules must extend base class enrol_plugin
+which is defined in lib/enrollib.php. You can find documentation
+of each method in the base class.
 
index ad71ff7..c369c8f 100644 (file)
@@ -30,5 +30,9 @@ function xmldb_enrol_authorize_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 66d9592..623ae22 100644 (file)
@@ -33,5 +33,9 @@ function xmldb_enrol_database_upgrade($oldversion) {
     // Put any upgrade step following this.
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 67785c2..0b1295c 100644 (file)
@@ -36,5 +36,9 @@ function xmldb_enrol_flatfile_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index b14e8f8..9769069 100644 (file)
@@ -44,6 +44,10 @@ function xmldb_enrol_guest_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 39b8fdc..384a74d 100644 (file)
@@ -35,6 +35,10 @@ function xmldb_enrol_imsenterprise_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 5a9eb1e..766160d 100644 (file)
@@ -46,6 +46,10 @@ function xmldb_enrol_manual_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 8c70236..76a1a32 100644 (file)
@@ -36,5 +36,9 @@ function xmldb_enrol_mnet_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 8bcb41f..279dc97 100644 (file)
@@ -51,5 +51,9 @@ function xmldb_enrol_paypal_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 67fb06f..77ed062 100644 (file)
@@ -90,13 +90,14 @@ if (! $plugin_instance = $DB->get_record("enrol", array("id"=>$data->instanceid,
 $plugin = enrol_get_plugin('paypal');
 
 /// Open a connection back to PayPal to validate the data
+$paypaladdr = empty($CFG->usepaypalsandbox) ? 'www.paypal.com' : 'www.sandbox.paypal.com';
 $c = new curl();
 $options = array(
     'returntransfer' => true,
-    'httpheader' => array('application/x-www-form-urlencoded'),
+    'httpheader' => array('application/x-www-form-urlencoded', "Host: $paypaladdr"),
     'timeout' => 30,
+    'CURLOPT_HTTP_VERSION' => CURL_HTTP_VERSION_1_1,
 );
-$paypaladdr = empty($CFG->usepaypalsandbox) ? 'www.paypal.com' : 'www.sandbox.paypal.com';
 $location = "https://$paypaladdr/cgi-bin/webscr";
 $result = $c->post($location, $req, $options);
 
@@ -171,7 +172,7 @@ if (strlen($result) > 0) {
 
         }
 
-        if ($data->business != $plugin->get_config('paypalbusiness')) {   // Check that the email is the one we want it to be
+        if (textlib::strtolower($data->business) !== textlib::strtolower($plugin->get_config('paypalbusiness'))) {   // Check that the email is the one we want it to be
             message_paypal_error_to_admin("Business email is {$data->business} (not ".
                     $plugin->get_config('paypalbusiness').")", $data);
             die;
index 69b9b39..1f354c1 100644 (file)
@@ -207,4 +207,23 @@ class enrol_paypal_plugin extends enrol_plugin {
         return $OUTPUT->box(ob_get_clean());
     }
 
+    /**
+     * Gets an array of the user enrolment actions
+     *
+     * @param course_enrolment_manager $manager
+     * @param stdClass $ue A user enrolment object
+     * @return array An array of user_enrolment_actions
+     */
+    public function get_user_enrolment_actions(course_enrolment_manager $manager, $ue) {
+        $actions = array();
+        $context = $manager->get_context();
+        $instance = $ue->enrolmentinstance;
+        $params = $manager->get_moodlepage()->url->params();
+        $params['ue'] = $ue->id;
+        if ($this->allow_unenrol($instance) && has_capability("enrol/paypal:unenrol", $context)) {
+            $url = new moodle_url('/enrol/unenroluser.php', $params);
+            $actions[] = new user_enrolment_action(new pix_icon('t/delete', ''), get_string('unenrol', 'enrol'), $url, array('class'=>'unenrollink', 'rel'=>$ue->id));
+        }
+        return $actions;
+    }
 }
index 235f12a..fda0d85 100644 (file)
@@ -39,6 +39,10 @@ function xmldb_enrol_self_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index a9c7964..d66224d 100644 (file)
@@ -58,5 +58,9 @@ function xmldb_filter_mediaplugin_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 74b0ef5..aa2e470 100644 (file)
@@ -37,5 +37,9 @@ function xmldb_filter_tex_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 7c1d214..b460fb4 100644 (file)
@@ -42,5 +42,9 @@ function xmldb_gradingform_rubric_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 62c5ad6..9b937c0 100644 (file)
@@ -223,7 +223,7 @@ border-color:#cecece;
 }
 
 .path-grade-report-grader th {
-padding:2px 10px;
+padding:1px 10px;
 }
 
 .path-grade-report-grader span.inclusion-links {
index 90ef60f..4b4e805 100644 (file)
@@ -352,17 +352,21 @@ abstract class grade_report {
         global $CFG, $DB;
         static $hiding_affected = null;//array of items in this course affected by hiding
 
-        //if we're dealing with multiple users we need to know when we've moved on to a new user
+        // If we're dealing with multiple users we need to know when we've moved on to a new user.
         static $previous_userid = null;
 
+        // If we're dealing with multiple courses we need to know when we've moved on to a new course.
+        static $previous_courseid = null;
+
         if( $this->showtotalsifcontainhidden==GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN ) {
             return $finalgrade;
         }
 
-        //if we've moved on to another user don't return the previous user's affected grades
-        if ($previous_userid!=$this->user->id) {
+        // If we've moved on to another course or user, reload the grades.
+        if ($previous_userid != $this->user->id || $previous_courseid != $courseid) {
             $hiding_affected = null;
             $previous_userid = $this->user->id;
+            $previous_courseid = $courseid;
         }
 
         if( !$hiding_affected ) {
diff --git a/grade/tests/reportlib_test.php b/grade/tests/reportlib_test.php
new file mode 100644 (file)
index 0000000..48b0eaa
--- /dev/null
@@ -0,0 +1,197 @@
+<?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 grade/report/lib.php.
+ *
+ * @pacakge  core_grade
+ * @category phpunit
+ * @author   Andrew Davis
+ * @license  http://www.gnu.org/copyleft/gpl.html GNU Public License
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->dirroot.'/grade/lib.php');
+require_once($CFG->dirroot.'/grade/report/lib.php');
+
+/**
+ * A test class used to test grade_report, the abstract grade report parent class
+ */
+class grade_report_test extends grade_report {
+    public function __construct($courseid, $gpr, $context, $user) {
+        parent::__construct($courseid, $gpr, $context);
+        $this->user = $user;
+    }
+
+    /**
+     * A wrapper around blank_hidden_total() to allow test code to call it directly
+     */
+    public function blank_hidden_total($courseid, $courseitem, $finalgrade) {
+        return parent::blank_hidden_total($courseid, $courseitem, $finalgrade);
+    }
+
+    /**
+     * Implementation of the abstract method process_data()
+     */
+    public function process_data($data) {
+    }
+
+    /**
+     * Implementation of the abstract method process_action()
+     */
+    public function process_action($target, $action) {
+    }
+}
+
+/**
+ * Tests grade_report, the parent class for all grade reports.
+ */
+class gradereportlib_testcase extends advanced_testcase {
+
+    /**
+     * Tests grade_report::blank_hidden_total()
+     */
+    public function test_blank_hidden_total() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        $student = $this->getDataGenerator()->create_user();
+        $this->setUser($student);
+
+        // Create a course and two activities.
+        // One activity will be hidden.
+        $course = $this->getDataGenerator()->create_course();
+        $coursegradeitem = grade_item::fetch_course_item($course->id);
+        $coursecontext = context_course::instance($course->id);
+
+        $data = $this->getDataGenerator()->create_module('data', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
+        $datacm = get_coursemodule_from_id('data', $data->cmid);
+
+        $forum = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
+        $forumcm = get_coursemodule_from_id('forum', $forum->cmid);
+
+        // Insert student grades for the two activities.
+        $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'data', 'iteminstance' => $data->id, 'courseid' => $course->id));
+        $datagrade = 50;
+        $grade_grade = new grade_grade();
+        $grade_grade->itemid = $gi->id;
+        $grade_grade->userid = $student->id;
+        $grade_grade->rawgrade = $datagrade;
+        $grade_grade->finalgrade = $datagrade;
+        $grade_grade->rawgrademax = 100;
+        $grade_grade->rawgrademin = 0;
+        $grade_grade->timecreated = time();
+        $grade_grade->timemodified = time();
+        $grade_grade->insert();
+
+        $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum->id, 'courseid' => $course->id));
+        $forumgrade = 70;
+        $grade_grade = new grade_grade();
+        $grade_grade->itemid = $gi->id;
+        $grade_grade->userid = $student->id;
+        $grade_grade->rawgrade = $forumgrade;
+        $grade_grade->finalgrade = $forumgrade;
+        $grade_grade->rawgrademax = 100;
+        $grade_grade->rawgrademin = 0;
+        $grade_grade->timecreated = time();
+        $grade_grade->timemodified = time();
+        $grade_grade->insert();
+
+        // Hide the database activity.
+        set_coursemodule_visible($datacm->id, 0);
+
+        $gpr = new grade_plugin_return(array('type' => 'report', 'courseid' => $course->id));
+        $report = new grade_report_test($course->id, $gpr, $coursecontext, $student);
+
+        // Should return the supplied student total grade regardless of hiding.
+        $report->showtotalsifcontainhidden = GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN;
+        $this->assertEquals($datagrade + $forumgrade, $report->blank_hidden_total($course->id, $coursegradeitem, $datagrade + $forumgrade));
+
+        // Should blank the student total as course grade depends on a hidden item.
+        $report->showtotalsifcontainhidden = GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN;
+        $this->assertEquals(null, $report->blank_hidden_total($course->id, $coursegradeitem, $datagrade + $forumgrade));
+
+        // Should return the course total minus the hidden database activity grade.
+        $report->showtotalsifcontainhidden = GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN;
+        $this->assertEquals($forumgrade, $report->blank_hidden_total($course->id, $coursegradeitem, $datagrade + $forumgrade));
+
+        // Note: we cannot simply hide modules and call $report->blank_hidden_total() again.
+        // It stores grades in a static variable so $report->blank_hidden_total() will return incorrect totals
+        // In practice this isn't a problem. Grade visibility isn't altered mid-request outside of the unit tests.
+
+        // Add a second course to test:
+        // 1) How a course with no visible activities behaves.
+        // 2) That $report->blank_hidden_total() correctly moves on to the new course.
+        $course = $this->getDataGenerator()->create_course();
+        $coursegradeitem = grade_item::fetch_course_item($course->id);
+        $coursecontext = context_course::instance($course->id);
+
+        $data = $this->getDataGenerator()->create_module('data', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
+        $datacm = get_coursemodule_from_id('data', $data->cmid);
+
+        $forum = $this->getDataGenerator()->create_module('forum', array('assessed' => 1, 'scale' => 100, 'course' => $course->id));
+        $forumcm = get_coursemodule_from_id('forum', $forum->cmid);
+
+        $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'data', 'iteminstance' => $data->id, 'courseid' => $course->id));
+        $datagrade = 50;
+        $grade_grade = new grade_grade();
+        $grade_grade->itemid = $gi->id;
+        $grade_grade->userid = $student->id;
+        $grade_grade->rawgrade = $datagrade;
+        $grade_grade->finalgrade = $datagrade;
+        $grade_grade->rawgrademax = 100;
+        $grade_grade->rawgrademin = 0;
+        $grade_grade->timecreated = time();
+        $grade_grade->timemodified = time();
+        $grade_grade->insert();
+
+        $gi = grade_item::fetch(array('itemtype' => 'mod', 'itemmodule' => 'forum', 'iteminstance' => $forum->id, 'courseid' => $course->id));
+        $forumgrade = 70;
+        $grade_grade = new grade_grade();
+        $grade_grade->itemid = $gi->id;
+        $grade_grade->userid = $student->id;
+        $grade_grade->rawgrade = $forumgrade;
+        $grade_grade->finalgrade = $forumgrade;
+        $grade_grade->rawgrademax = 100;
+        $grade_grade->rawgrademin = 0;
+        $grade_grade->timecreated = time();
+        $grade_grade->timemodified = time();
+        $grade_grade->insert();
+
+        // Hide both activities.
+        set_coursemodule_visible($datacm->id, 0);
+        set_coursemodule_visible($forumcm->id, 0);
+
+        $gpr = new grade_plugin_return(array('type' => 'report', 'courseid' => $course->id));
+        $report = new grade_report_test($course->id, $gpr, $coursecontext, $student);
+
+        // Should return the supplied student total grade regardless of hiding.
+        $report->showtotalsifcontainhidden = GRADE_REPORT_SHOW_REAL_TOTAL_IF_CONTAINS_HIDDEN;
+        $this->assertEquals($datagrade + $forumgrade, $report->blank_hidden_total($course->id, $coursegradeitem, $datagrade + $forumgrade));
+
+        // Should blank the student total as course grade depends on a hidden item.
+        $report->showtotalsifcontainhidden = GRADE_REPORT_HIDE_TOTAL_IF_CONTAINS_HIDDEN;
+        $this->assertEquals(null, $report->blank_hidden_total($course->id, $coursegradeitem, $datagrade + $forumgrade));
+
+        // Should return the course total minus the hidden activity grades.
+        // They are both hidden so should return null.
+        $report->showtotalsifcontainhidden = GRADE_REPORT_SHOW_TOTAL_IF_CONTAINS_HIDDEN;
+        $this->assertEquals(null, $report->blank_hidden_total($course->id, $coursegradeitem, $datagrade + $forumgrade));
+    }
+}
index e239a5f..93ea1f7 100644 (file)
@@ -41,6 +41,7 @@ $string['databasehost'] = 'Servidor de la base de datos';
 $string['databasename'] = 'Nombre de la base de datos';
 $string['databasetypehead'] = 'Seleccione el controlador de la base de datos';
 $string['dataroot'] = 'Directorio de Datos';
+$string['datarootpermission'] = 'Permiso directorios de datos';
 $string['dbprefix'] = 'Prefijo de tablas';
 $string['dirroot'] = 'Directorio Moodle';
 $string['environmenthead'] = 'Comprobando su entorno';
index f7c639b..eea0465 100644 (file)
@@ -32,4 +32,4 @@ defined('MOODLE_INTERNAL') || die();
 
 $string['parentlanguage'] = '';
 $string['thisdirection'] = 'ltr';
-$string['thislanguage'] = 'Español - Mexico';
+$string['thislanguage'] = 'Español - México';
index 1a2c751..6c8ae80 100644 (file)
@@ -48,7 +48,6 @@ $string['disableall_help'] = 'Temporarily disable all notifications except those
 $string['disabled'] = 'Messaging is disabled on this site';
 $string['disallowed'] = 'Disallowed';
 $string['discussion'] = 'Discussion';
-$string['editmymessage'] = 'Messaging';
 $string['emailmessages'] = 'Email messages when I am offline';
 $string['emailtagline'] = 'This is a copy of a message sent to you at "{$a->sitename}". Go to {$a->url} to reply.';
 $string['emptysearchstring'] = 'You must search for something';
index f21667f..4d4dd8c 100644 (file)
@@ -30,7 +30,9 @@ $string['availability'] = 'Availability';
 $string['checkforupdates'] = 'Check for available updates';
 $string['checkforupdateslast'] = 'Last check done on {$a}';
 $string['displayname'] = 'Plugin name';
+$string['err_response_curl'] = 'Unable to fetch available updates data - unexpected cURL error.';
 $string['err_response_format_version'] = 'Unexpected version of the response format. Please try to re-check for available updates.';
+$string['err_response_http_code'] = 'Unable to fetch available updates data - unexpected HTTP response code.';
 $string['filterall'] = 'Show all';
 $string['filtercontribonly'] = 'Show contributions only';
 $string['filtercontribonlyactive'] = 'Showing contributions only';
@@ -41,10 +43,14 @@ $string['nonehighlighted'] = 'No plugins require your attention now';
 $string['nonehighlightedinfo'] = 'Display the list of all installed plugins anyway';
 $string['noneinstalled'] = 'No plugins of this type are installed';
 $string['notes'] = 'Notes';
+$string['notdownloadable'] = 'Can not download the package';
+$string['notdownloadable_help'] = 'ZIP package with the update can not be downloaded automatically. Please refer to the documentation page for more help.';
+$string['notdownloadable_link'] = 'admin/mdeploy/notdownloadable';
 $string['notwritable'] = 'Plugin files not writable';
 $string['notwritable_help'] = 'You have enabled automatic updates deployment and there is available update for this plugin. However, the plugin files are not writable by the web server so the update can not be installed at the moment.
 
 Make the plugin folder and all its contents writable to be able to install the available update automatically.';
+$string['notwritable_link'] = 'admin/mdeploy/notwritable';
 $string['numtotal'] = 'Installed: {$a}';
 $string['numdisabled'] = 'Disabled: {$a}';
 $string['numextension'] = 'Contributions: {$a}';
index ec222d5..6bc53c2 100644 (file)
@@ -2956,8 +2956,13 @@ function get_user_roles_in_course($userid, $courseid) {
 function user_can_assign(context $context, $targetroleid) {
     global $DB;
 
-    // first check if user has override capability
-    // if not return false;
+    // First check to see if the user is a site administrator.
+    if (is_siteadmin()) {
+        return true;
+    }
+
+    // Check if user has override capability.
+    // If not return false.
     if (!has_capability('moodle/role:assign', $context)) {
         return false;
     }
index 041190a..470106d 100644 (file)
@@ -30,6 +30,7 @@ require_once(dirname(__FILE__) . '/../../config.php');
 $courseid = required_param('courseid', PARAM_INT);
 $pagelayout = required_param('pagelayout', PARAM_ALPHAEXT);
 $pagetype = required_param('pagetype', PARAM_ALPHAEXT);
+$subpage = optional_param('subpage', '', PARAM_ALPHANUMEXT);
 $cmid = optional_param('cmid', null, PARAM_INT);
 $action = optional_param('action', '', PARAM_ALPHA);
 // Params for blocks-move actions
@@ -51,6 +52,7 @@ require_sesskey();
 
 // Setting layout to replicate blocks configuration for the page we edit
 $PAGE->set_pagelayout($pagelayout);
+$PAGE->set_subpage($subpage);
 echo $OUTPUT->header(); // send headers
 
 switch ($action) {
index 454bc7e..bd917ba 100644 (file)
@@ -1802,7 +1802,7 @@ function add_to_log($courseid, $module, $action, $url='', $info='', $cm=0, $user
     $timenow = time();
     $info = $info;
     if (!empty($url)) { // could break doing html_entity_decode on an empty var.
-        $url = html_entity_decode($url);
+        $url = html_entity_decode($url, ENT_QUOTES, 'UTF-8');
     } else {
         $url = '';
     }
index 7242366..7dee7a7 100644 (file)
@@ -1501,5 +1501,21 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012112100.00);
     }
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
+    if ($oldversion < 2012120300.01) {
+        // Make sure site-course has format='site' //MDL-36840
+
+        if ($SITE->format !== 'site') {
+            $DB->set_field('course', 'format', 'site', array('id' => $SITE->id));
+            $SITE->format = 'site';
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2012120300.01);
+    }
+
     return true;
 }
index e096c87..8deccf9 100644 (file)
@@ -172,7 +172,7 @@ class oracle_sql_generator extends sql_generator {
     public function getTypeSQL($xmldb_type, $xmldb_length=null, $xmldb_decimals=null) {
 
         switch ($xmldb_type) {
-            case XMLDB_TYPE_INTEGER:    // From http://www.postgresql.org/docs/7.4/interactive/datatype.html
+            case XMLDB_TYPE_INTEGER:    // See http://www.acs.ilstu.edu/docs/oracle/server.101/b10759/sql_elements001.htm#sthref86.
                 if (empty($xmldb_length)) {
                     $xmldb_length = 10;
                 }
index 4aae9ce..61b161c 100644 (file)
@@ -172,7 +172,7 @@ abstract class pdo_moodle_database extends moodle_database {
      * Function to print/save/ignore debugging messages related to SQL queries.
      */
     protected function debug_query($sql, $params = null) {
-        echo '<hr /> (', $this->get_dbtype(), '): ',  htmlentities($sql);
+        echo '<hr /> (', $this->get_dbtype(), '): ',  htmlentities($sql, ENT_QUOTES, 'UTF-8');
         if($params) {
             echo ' (parameters ';
             print_r($params);
index c976b37..5010b72 100644 (file)
@@ -3844,7 +3844,7 @@ class dml_testcase extends database_driver_testcase {
         $this->assertEquals("Firstname Surname", $DB->get_field_sql($sql, $params));
     }
 
-    function sql_sql_order_by_text() {
+    function test_sql_order_by_text() {
         $DB = $this->tdb;
         $dbman = $DB->get_manager();
 
index 42d931e..d817291 100644 (file)
@@ -37,5 +37,9 @@ function xmldb_editor_tinymce_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 4012b1f..c833fee 100644 (file)
@@ -36,5 +36,9 @@ function xmldb_tinymce_spellchecker_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 44315ca..69205d9 100644 (file)
@@ -74,8 +74,8 @@ class MoodleExcelWorkbook {
      * @param string $name Name of the sheet
      * @return object MoodleExcelWorksheet
      */
-    function &add_worksheet($name = '') {
-    /// Create the Moodle Worksheet. Returns one pointer to it
+    function add_worksheet($name = '') {
+        // Create the Moodle Worksheet. Returns one pointer to it
         $ws = new MoodleExcelWorksheet ($name, $this->pear_excel_workbook, $this->latin_output);
         return $ws;
     }
@@ -138,6 +138,9 @@ class MoodleExcelWorksheet {
      */
     function MoodleExcelWorksheet($name, &$workbook, $latin_output=false) {
 
+        // Replace any characters in the name that Excel cannot cope with.
+        $name = strtr($name, '[]*/\?:', '       ');
+
         if (strlen($name) > 31) {
             // Excel does not seem able to cope with sheet names > 31 chars.
             // With $latin_output = false, it does not cope at all.
index 6c935ef..ab120b3 100644 (file)
@@ -2286,7 +2286,7 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
             $options = new stdClass();
             $options->newlines = false;
             $options->noclean = true;
-            $text = htmlentities($pathisstring ? $path : implode('', file($path)));
+            $text = htmlentities($pathisstring ? $path : implode('', file($path)), ENT_QUOTES, 'UTF-8');
             $output = '<pre>'. format_text($text, FORMAT_MOODLE, $options, $COURSE->id) .'</pre>';
 
             readstring_accel($output, $mimetype, false);
index dfc4fa0..049acfe 100644 (file)
@@ -67,6 +67,10 @@ class filestoragelib_testcase extends advanced_testcase {
     public function test_get_file_preview(stored_file $file) {
         global $CFG;
 
+        if (empty($CFG->gdversion)) {
+            $this->markTestSkipped('GD extension is disabled');
+        }
+
         $this->resetAfterTest(true);
         $fs = get_file_storage();
 
index d64b619..03c8c33 100644 (file)
@@ -1893,7 +1893,7 @@ function qf_errorHandler(element, _qfMsg) {
             list($jsArr,$element)=$jsandelement;
             //end of fix
             $escapedElementName = preg_replace_callback(
-                '/[_\[\]]/',
+                '/[_\[\]-]/',
                 create_function('$matches', 'return sprintf("_%2x",ord($matches[0]));'),
                 $elementName);
             $js .= '
index 5cd052d..87c494f 100644 (file)
@@ -886,7 +886,9 @@ class grade_category extends grade_object {
                 $droppedsomething = false;
 
                 $grade_keys = array_keys($grade_values);
-                if (count($grade_keys) === 0) {
+                $gradekeycount = count($grade_keys);
+
+                if ($gradekeycount === 0) {
                     //We've dropped all grade items
                     break;
                 }
@@ -912,7 +914,7 @@ class grade_category extends grade_object {
                 // Now iterate over the remaining grade items
                 // We're looking for other grade items with the same grade value but a higher grademax
                 $i = 1;
-                while ($originalindex+$i < count($grade_keys)) {
+                while ($originalindex + $i < $gradekeycount) {
 
                     $possibleitemid = $grade_keys[$originalindex+$i];
                     $i++;
index c0878b3..6572ecd 100644 (file)
@@ -426,6 +426,7 @@ M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) {
                     if (e.button == 1) {
                         buttonflag = 1;
                     }
+                    paramobject.lastindex = select.get('selectedIndex');
                 };
 
                 var changedown = function(e, paramobject) {
@@ -433,6 +434,7 @@ M.util.init_select_autosubmit = function(Y, formid, selectid, nothing) {
                         if(e.keyCode == 13) {
                             form.submit();
                         }
+                        paramobject.lastindex = select.get('selectedIndex');
                     }
                 }
 
index 9236a11..9a8ddf1 100644 (file)
@@ -1134,7 +1134,7 @@ function clean_param($param, $type) {
 
         case PARAM_TIMEZONE:    //can be int, float(with .5 or .0) or string seperated by '/' and can have '-_'
             $param = fix_utf8($param);
-            $timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3]|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/';
+            $timezonepattern = '/^(([+-]?(0?[0-9](\.[5|0])?|1[0-3](\.0)?|1[0-2]\.5))|(99)|[[:alnum:]]+(\/?[[:alpha:]_-])+)$/';
             if (preg_match($timezonepattern, $param)) {
                 return $param;
             } else {
index 1f2fa1d..b4e69c6 100644 (file)
@@ -3389,9 +3389,9 @@ class settings_navigation extends navigation_node {
                         continue;
                     }
                     if ($type->modclass == MOD_CLASS_RESOURCE) {
-                        $resources[html_entity_decode($type->type)] = $type->typestr;
+                        $resources[html_entity_decode($type->type, ENT_QUOTES, 'UTF-8')] = $type->typestr;
                     } else {
-                        $activities[html_entity_decode($type->type)] = $type->typestr;
+                        $activities[html_entity_decode($type->type, ENT_QUOTES, 'UTF-8')] = $type->typestr;
                     }
                 }
             } else {
@@ -3978,8 +3978,8 @@ class settings_navigation extends navigation_node {
 
         // Messaging
         if (($currentuser && has_capability('moodle/user:editownmessageprofile', $systemcontext)) || (!isguestuser($user) && has_capability('moodle/user:editmessageprofile', $usercontext) && !is_primary_admin($user->id))) {
-            $url = new moodle_url('/message/edit.php', array('id'=>$user->id, 'course'=>$course->id));
-            $usersetting->add(get_string('editmymessage', 'message'), $url, self::TYPE_SETTING);
+            $url = new moodle_url('/message/edit.php', array('id'=>$user->id));
+            $usersetting->add(get_string('messaging', 'message'), $url, self::TYPE_SETTING);
         }
 
         // Blogs
@@ -4275,7 +4275,7 @@ class navigation_json {
         }
 
         if ($child->forcetitle || $child->title !== $child->text) {
-            $attributes['title'] = htmlentities($child->title);
+            $attributes['title'] = htmlentities($child->title, ENT_QUOTES, 'UTF-8');
         }
         if (array_key_exists($child->key.':'.$child->type, $this->expandable)) {
             $attributes['expandable'] = $child->key;
index da1761b..dcd0875 100644 (file)
@@ -364,6 +364,9 @@ class theme_config {
         } else if ($themename == theme_config::DEFAULT_THEME) {
             throw new coding_exception('Default theme '.theme_config::DEFAULT_THEME.' not available or broken!');
 
+        } else if ($config = theme_config::find_theme_config($CFG->theme, $settings)) {
+            return new theme_config($config);
+
         } else {
             // bad luck, the requested theme has some problems - admin see details in theme config
             return new theme_config(theme_config::find_theme_config(theme_config::DEFAULT_THEME, $settings));
@@ -618,20 +621,34 @@ class theme_config {
 
         $urls = array();
 
+        $svg = $this->use_svg_icons();
+
         if ($rev > -1) {
+            $url = new moodle_url("$CFG->httpswwwroot/theme/styles.php");
             if (check_browser_version('MSIE', 5)) {
                 // We need to split the CSS files for IE
-                $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'plugins'));
-                $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'parents'));
-                $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'theme'));
+                $urls[] = new moodle_url($url, array('theme' => $this->name,'rev' => $rev, 'type' => 'plugins', 'svg' => '0'));
+                $urls[] = new moodle_url($url, array('theme' => $this->name,'rev' => $rev, 'type' => 'parents', 'svg' => '0'));
+                $urls[] = new moodle_url($url, array('theme' => $this->name,'rev' => $rev, 'type' => 'theme', 'svg' => '0'));
             } else {
                 if (!empty($CFG->slasharguments)) {
-                    $url = new moodle_url("$CFG->httpswwwroot/theme/styles.php");
-                    $url->set_slashargument('/'.$this->name.'/'.$rev.'/all', 'noparam', true);
-                    $urls[] = $url;
+                    $slashargs = '/'.$this->name.'/'.$rev.'/all';
+                    if (!$svg) {
+                        // We add a simple /_s to the start of the path.
+                        // The underscore is used to ensure that it isn't a valid theme name.
+                        $slashargs = '/_s'.$slashargs;
+                    }
+                    $url->set_slashargument($slashargs, 'noparam', true);
                 } else {
-                    $urls[] = new moodle_url($CFG->httpswwwroot.'/theme/styles.php', array('theme'=>$this->name,'rev'=>$rev, 'type'=>'all'));
+                    $params = array('theme' => $this->name,'rev' => $rev, 'type' => 'all');
+                    if (!$svg) {
+                        // We add an SVG param so that we know not to serve SVG images.
+                        // We do this because all modern browsers support SVG and this param will one day be removed.
+                        $params['svg'] = '0';
+                    }
+                    $url->params($params);
                 }
+                $urls[] = $url;
             }
         } else {
             // find out the current CSS and cache it now for 5 seconds
@@ -641,7 +658,11 @@ class theme_config {
                 define('THEME_DESIGNER_CACHE_LIFETIME', 4); // this can be also set in config.php
             }
             $candidatedir = "$CFG->cachedir/theme/$this->name";
-            $candidatesheet = "$candidatedir/designer.ser";
+            if ($svg) {
+                $candidatesheet = "$candidatedir/designer.ser";
+            } else {
+                $candidatesheet = "$candidatedir/designer_nosvg.ser";
+            }
             $rebuild = true;
             if (file_exists($candidatesheet) and filemtime($candidatesheet) > time() - THEME_DESIGNER_CACHE_LIFETIME) {
                 if ($css = file_get_contents($candidatesheet)) {
@@ -674,8 +695,12 @@ class theme_config {
                 ignore_user_abort($prevabort);
             }
 
-            $baseurl = $CFG->httpswwwroot.'/theme/styles_debug.php';
-
+            $baseurl = new moodle_url($CFG->httpswwwroot.'/theme/styles_debug.php');
+            if (!$svg) {
+                // We add an SVG param so that we know not to serve SVG images.
+                // We do this because all modern browsers support SVG and this param will one day be removed.
+                $baseurl->param('svg', '0');
+            }
             if (check_browser_version('MSIE', 5)) {
                 // lalala, IE does not allow more than 31 linked CSS files from main document
                 $urls[] = new moodle_url($baseurl, array('theme'=>$this->name, 'type'=>'ie', 'subtype'=>'plugins'));
@@ -948,12 +973,6 @@ class theme_config {
     public function post_process($css) {
         // now resolve all image locations
         if (preg_match_all('/\[\[pix:([a-z_]+\|)?([^\]]+)\]\]/', $css, $matches, PREG_SET_ORDER)) {
-            // We are going to disable the use of SVG images when available in CSS background-image properties
-            // as support for it in browsers is at best quirky.
-            // When we choose to support SVG in background css we will need to remove this code and implement a solution that is
-            // either consistent or varies the URL for serving CSS depending upon SVG being used if available, or not.
-            $originalsvguse = $this->use_svg_icons();
-            $this->force_svg_use(false);
             $replaced = array();
             foreach ($matches as $match) {
                 if (isset($replaced[$match[0]])) {
@@ -967,7 +986,6 @@ class theme_config {
                 $imageurl = preg_replace('|^http.?://[^/]+|', '', $imageurl);
                 $css = str_replace($match[0], $imageurl, $css);
             }
-            $this->force_svg_use($originalsvguse);
         }
 
         // now resolve all theme settings or do any other postprocessing
@@ -1145,10 +1163,11 @@ class theme_config {
      * Forces the usesvg setting to either true or false, avoiding any decision making.
      *
      * This function should only ever be used when absolutely required, and before any generation of image URL's has occurred.
+     * DO NOT ABUSE THIS FUNCTION... not that you'd want to right ;)
      *
      * @param bool $setting True to force the use of svg when available, null otherwise.
      */
-    private function force_svg_use($setting) {
+    public function force_svg_use($setting) {
         $this->usesvg = (bool)$setting;
     }
 
index 3d7f410..0e8052d 100644 (file)
@@ -1565,9 +1565,15 @@ class core_renderer extends renderer_base {
             $output .= html_writer::label($select->label, $select->attributes['id'], false, $select->labelattributes);
         }
 
-        $select->attributes['class'] = 'autosubmit';
+        $classes = array();
+        if (!$select->showbutton) {
+            $classes[] = 'autosubmit';
+        }
         if ($select->class) {
-            $select->attributes['class'] .= ' ' . $select->class;
+            $classes[] = $select->class;
+        }
+        if (count($classes)) {
+            $select->attributes['class'] = implode(' ', $classes);
         }
 
         if ($select->helpicon instanceof help_icon) {
index 132c4db..0bf70c4 100644 (file)
@@ -15,7 +15,7 @@ Composer is a new dependency manager for PHP projects.
 It installs PHP libraries into /vendor/ subdirectory inside your moodle dirroot.
 
 1. install Composer - http://getcomposer.org/doc/00-intro.md
-2. go to your moodle dirroot and execute `php composer.phar install`
+2. go to your moodle dirroot and execute `php composer.phar install --dev`
 
 
 PEAR installation (not recommended)
index 7383d7b..b740093 100644 (file)
@@ -826,7 +826,11 @@ class available_update_checker {
         require_once($CFG->libdir.'/filelib.php');
 
         $curl = new curl(array('proxy' => true));
-        $response = $curl->post($this->prepare_request_url(), $this->prepare_request_params());
+        $response = $curl->post($this->prepare_request_url(), $this->prepare_request_params(), $this->prepare_request_options());
+        $curlerrno = $curl->get_errno();
+        if (!empty($curlerrno)) {
+            throw new available_update_checker_exception('err_response_curl', 'cURL error '.$curlerrno.': '.$curl->error);
+        }
         $curlinfo = $curl->get_info();
         if ($curlinfo['http_code'] != 200) {
             throw new available_update_checker_exception('err_response_http_code', $curlinfo['http_code']);
@@ -1069,6 +1073,29 @@ class available_update_checker {
         return $params;
     }
 
+    /**
+     * Returns the list of cURL options to use when fetching available updates data
+     *
+     * @return array of (string)param => (string)value
+     */
+    protected function prepare_request_options() {
+        global $CFG;
+
+        $options = array(
+            'CURLOPT_SSL_VERIFYHOST' => 2,      // this is the default in {@link curl} class but just in case
+            'CURLOPT_SSL_VERIFYPEER' => true,
+        );
+
+        $cacertfile = $CFG->dataroot.'/moodleorgca.crt';
+        if (is_readable($cacertfile)) {
+            // Do not use CA certs provided by the operating system. Instead,
+            // use this CA cert to verify the updates provider.
+            $options['CURLOPT_CAINFO'] = $cacertfile;
+        }
+
+        return $options;
+    }
+
     /**
      * Returns the current timestamp
      *
@@ -1223,19 +1250,28 @@ class available_update_checker {
                 foreach ($componentupdates as $componentupdate) {
                     if ($componentupdate->version == $componentchange['version']) {
                         if ($component == 'core') {
-                            // in case of 'core' this is enough, we already know that the
-                            // $componentupdate is a real update with higher version
-                            $notifications[] = $componentupdate;
+                            // In case of 'core', we already know that the $componentupdate
+                            // is a real update with higher version ({@see self::get_update_info()}).
+                            // We just perform additional check for the release property as there
+                            // can be two Moodle releases having the same version (e.g. 2.4.0 and 2.5dev shortly
+                            // after the release). We can do that because we have the release info
+                            // always available for the core.
+                            if ((string)$componentupdate->release === (string)$componentchange['release']) {
+                                $notifications[] = $componentupdate;
+                            }
                         } else {
-                            // use the plugin_manager to check if the reported $componentchange
-                            // is a real update with higher version. such a real update must be
-                            // present in the 'availableupdates' property of one of the component's
-                            // available_update_info object
+                            // Use the plugin_manager to check if the detected $componentchange
+                            // is a real update with higher version. That is, the $componentchange
+                            // is present in the array of {@link available_update_info} objects
+                            // returned by the plugin's available_updates() method.
                             list($plugintype, $pluginname) = normalize_component($component);
-                            if (!empty($plugins[$plugintype][$pluginname]->availableupdates)) {
-                                foreach ($plugins[$plugintype][$pluginname]->availableupdates as $availableupdate) {
-                                    if ($availableupdate->version == $componentchange['version']) {
-                                        $notifications[] = $componentupdate;
+                            if (!empty($plugins[$plugintype][$pluginname])) {
+                                $availableupdates = $plugins[$plugintype][$pluginname]->available_updates();
+                                if (!empty($availableupdates)) {
+                                    foreach ($availableupdates as $availableupdate) {
+                                        if ($availableupdate->version == $componentchange['version']) {
+                                            $notifications[] = $componentupdate;
+                                        }
                                     }
                                 }
                             }
@@ -1562,6 +1598,10 @@ class available_update_deployer {
             $impediments['missingdownloadmd5'] = true;
         }
 
+        if (!empty($info->download) and !$this->update_downloadable($info->download)) {
+            $impediments['notdownloadable'] = true;
+        }
+
         if (!$this->component_writable($info->component)) {
             $impediments['notwritable'] = true;
         }
@@ -1660,6 +1700,28 @@ class available_update_deployer {
             'returnurl' => $upgradeurl->out(true),
         );
 
+        if (!empty($CFG->proxyhost)) {
+            // MDL-36973 - Beware - we should call just !is_proxybypass() here. But currently, our
+            // cURL wrapper class does not do it. So, to have consistent behaviour, we pass proxy
+            // setting regardless the $CFG->proxybypass setting. Once the {@link curl} class is
+            // fixed, the condition should be amended.
+            if (true or !is_proxybypass($info->download)) {
+                if (empty($CFG->proxyport)) {
+                    $params['proxy'] = $CFG->proxyhost;
+                } else {
+                    $params['proxy'] = $CFG->proxyhost.':'.$CFG->proxyport;
+                }
+
+                if (!empty($CFG->proxyuser) and !empty($CFG->proxypassword)) {
+                    $params['proxyuserpwd'] = $CFG->proxyuser.':'.$CFG->proxypassword;
+                }
+
+                if (!empty($CFG->proxytype)) {
+                    $params['proxytype'] = $CFG->proxytype;
+                }
+            }
+        }
+
         $widget = new single_button(
             new moodle_url('/mdeploy.php', $params),
             get_string('updateavailableinstall', 'core_admin'),
@@ -1890,6 +1952,40 @@ class available_update_deployer {
         return $this->directory_writable($directory);
     }
 
+    /**
+     * Checks if the mdeploy.php will be able to fetch the ZIP from the given URL
+     *
+     * This is mainly supposed to check if the transmission over HTTPS would
+     * work. That is, if the CA certificates are present at the server.
+     *
+     * @param string $downloadurl the URL of the ZIP package to download
+     * @return bool
+     */
+    protected function update_downloadable($downloadurl) {
+        global $CFG;
+
+        $curloptions = array(
+            'CURLOPT_SSL_VERIFYHOST' => 2,      // this is the default in {@link curl} class but just in case
+            'CURLOPT_SSL_VERIFYPEER' => true,
+        );
+
+        $cacertfile = $CFG->dataroot.'/moodleorgca.crt';
+        if (is_readable($cacertfile)) {
+            // Do not use CA certs provided by the operating system. Instead,
+            // use this CA cert to verify the updates provider.
+            $curloptions['CURLOPT_CAINFO'] = $cacertfile;
+        }
+
+        $curl = new curl(array('proxy' => true));
+        $result = $curl->head($downloadurl, $curloptions);
+        $errno = $curl->get_errno();
+        if (empty($errno)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     /**
      * Checks if the directory and all its contents (recursively) is writable
      *
index 123c70e..8052517 100644 (file)
@@ -41,13 +41,13 @@ function session_get_instance() {
 
     static $session = null;
 
-    if (!defined('NO_MOODLE_COOKIES')) {
-        // Moodle session was not initialised yet in lib/setup.php.
-        $session = new emergency_session();
-        return $session;
-    }
-
     if (is_null($session)) {
+        if (!defined('NO_MOODLE_COOKIES') or empty($DB)) {
+            // Moodle was not initialised properly in lib/setup.php.
+            $session = new emergency_session();
+            return $session;
+        }
+
         if (empty($CFG->sessiontimeout)) {
             $CFG->sessiontimeout = 7200;
         }
@@ -1122,6 +1122,10 @@ function get_moodle_cookie() {
 function session_set_user($user) {
     $_SESSION['USER'] = $user;
     unset($_SESSION['USER']->description); // conserve memory
+    if (isset($_SESSION['USER']->lang)) {
+        // Make sure it is a valid lang pack name.
+        $_SESSION['USER']->lang = clean_param($_SESSION['USER']->lang, PARAM_LANG);
+    }
     sesskey(); // init session key
 
     if (PHPUNIT_TEST) {
index bfb9df9..55f9f0d 100644 (file)
@@ -60,6 +60,7 @@ class flexible_table {
     var $column_class    = array();
     var $column_suppress = array();
     var $column_nosort   = array('userpic');
+    private $column_textsort = array();
     var $setup           = false;
     var $sess            = NULL;
     var $baseurl         = NULL;
@@ -214,6 +215,14 @@ class flexible_table {
         $this->sort_default_order  = $defaultorder;
     }
 
+    /**
+     * Use text sorting functions for this column (required for text columns with Oracle).
+     * @param string column name
+     */
+    function text_sorting($column) {
+        $this->column_textsort[] = $column;
+    }
+
     /**
      * Do not sort using this column
      * @param string column name
@@ -513,10 +522,14 @@ class flexible_table {
      * @param array $cols column name => SORT_ASC or SORT_DESC
      * @return SQL fragment that can be used in an ORDER BY clause.
      */
-    public static function construct_order_by($cols) {
+    public static function construct_order_by($cols, $textsortcols=array()) {
+        global $DB;
         $bits = array();
 
         foreach ($cols as $column => $order) {
+            if (in_array($column, $textsortcols)) {
+                $column = $DB->sql_order_by_text($column);
+            }
             if ($order == SORT_ASC) {
                 $bits[] = $column . ' ASC';
             } else {
@@ -531,7 +544,7 @@ class flexible_table {
      * @return SQL fragment that can be used in an ORDER BY clause.
      */
     public function get_sql_sort() {
-        return self::construct_order_by($this->get_sort_columns());
+        return self::construct_order_by($this->get_sort_columns(), $this->column_textsort);
     }
 
     /**
@@ -1495,7 +1508,7 @@ class table_spreadsheet_export_format_parent extends table_default_export_format
     }
 
     function start_table($sheettitle) {
-        $this->worksheet =& $this->workbook->add_worksheet($sheettitle);
+        $this->worksheet = $this->workbook->add_worksheet($sheettitle);
         $this->rownum=0;
     }
 
index 33c014c..731d725 100644 (file)
@@ -943,9 +943,23 @@ class moodlelib_testcase extends advanced_testcase {
             '0'                              => '0',
             '0.0'                            => '0.0',
             '0.5'                            => '0.5',
+            '9.0'                            => '9.0',
+            '-9.0'                           => '-9.0',
+            '+9.0'                           => '+9.0',
+            '9.5'                            => '9.5',
+            '-9.5'                           => '-9.5',
+            '+9.5'                           => '+9.5',
+            '12.0'                           => '12.0',
+            '-12.0'                          => '-12.0',
+            '+12.0'                          => '+12.0',
+            '12.5'                           => '12.5',
             '-12.5'                          => '-12.5',
             '+12.5'                          => '+12.5',
+            '13.0'                           => '13.0',
+            '-13.0'                          => '-13.0',
+            '+13.0'                          => '+13.0',
             '13.5'                           => '',
+            '+13.5'                          => '',
             '-13.5'                          => '',
             '0.2'                            => '');
 
index d36095e..c1d1d31 100644 (file)
@@ -265,15 +265,17 @@ class webdav_client {
      * Public method get
      *
      * Gets a file from a webdav collection.
-     * @param string path, string &buffer
-     * @return status code and &$buffer (by reference) with response data from server on success. False on error.
+     * @param string $path the path to the file on the webdav server
+     * @param string &$buffer the buffer to store the data in
+     * @param resource $fp optional if included, the data is written directly to this resource and not to the buffer
+     * @return string|bool status code and &$buffer (by reference) with response data from server on success. False on error.
      */
-    function get($path, &$buffer) {
+    function get($path, &$buffer, $fp = null) {
         $this->_path = $this->translate_uri($path);
         $this->header_unset();
         $this->create_basic_request('GET');
         $this->send_request();
-        $this->get_respond();
+        $this->get_respond($fp);
         $response = $this->process_respond();
 
         $http_version = $response['status']['http-version'];
@@ -283,8 +285,13 @@ class webdav_client {
                 // seems to be http ... proceed
                 // We expect a 200 code
                 if ($response['status']['status-code'] == 200 ) {
-                    $this->_error_log('returning buffer with ' . strlen($response['body']) . ' bytes.');
-                    $buffer = $response['body'];
+                    if (!is_null($fp)) {
+                        $stat = fstat($fp);
+                        $this->_error_log('file created with ' . $stat['size'] . ' bytes.');
+                    } else {
+                        $this->_error_log('returning buffer with ' . strlen($response['body']) . ' bytes.');
+                        $buffer = $response['body'];
+                    }
                 }
                 return $response['status']['status-code'];
             }
@@ -387,27 +394,24 @@ class webdav_client {
      * Gets a file from a collection into local filesystem.
      *
      * fopen() is used.
-     * @param string srcpath, string localpath
-     * @return true on success. false on error.
+     * @param string $srcpath
+     * @param string $localpath
+     * @return bool true on success. false on error.
      */
     function get_file($srcpath, $localpath) {
 
-        if ($this->get($srcpath, $buffer)) {
-            // convert utf-8 filename to iso-8859-1
+        $localpath = $this->utf_decode_path($localpath);
 
-            $localpath = $this->utf_decode_path($localpath);
-
-            $handle = fopen ($localpath, 'w');
-            if ($handle) {
-                fwrite($handle, $buffer);
-                fclose($handle);
+        $handle = fopen($localpath, 'wb');
+        if ($handle) {
+            $unused = '';
+            $ret = $this->get($srcpath, $unused, $handle);
+            fclose($handle);
+            if ($ret) {
                 return true;
-            } else {
-                return false;
             }
-        } else {
-            return false;
         }
+        return false;
     }
 
     /**
@@ -1447,8 +1451,9 @@ EOD;
      * This routine is the weakest part of this class, because it very depends how php does handle a socket stream.
      * If the stream is blocked for some reason php is blocked as well.
      * @access private
+     * @param resource $fp optional the file handle to write the body content to (stored internally in the '_body' if not set)
      */
-    private function get_respond() {
+    private function get_respond($fp = null) {
         $this->_error_log('get_respond()');
         // init vars (good coding style ;-)
         $buffer = '';
@@ -1509,7 +1514,8 @@ EOD;
                     $read = 0;
                     // Reading the chunk in one bite is not secure, we read it byte by byte.
                     while ($read < $chunk_size) {
-                        $buffer .= fread($this->sock, 1);
+                        $chunk = fread($this->sock, 1);
+                        self::update_file_or_buffer($chunk, $fp, $buffer);
                         $read++;
                     }
                 }
@@ -1525,21 +1531,20 @@ EOD;
             if ($matches[1] <= $max_chunk_size ) {
                 // only read something if Content-Length is bigger than 0
                 if ($matches[1] > 0 ) {
-                    $buffer = fread($this->sock, $matches[1]);
-                    $loadsize = strlen($buffer);
+                    $chunk = fread($this->sock, $matches[1]);
+                    $loadsize = strlen($chunk);
                     //did we realy get the full length?
                     if ($loadsize < $matches[1]) {
                         $max_chunk_size = $loadsize;
                         do {
-                            $mod = $max_chunk_size % ($matches[1] - strlen($buffer));
-                            $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($buffer));
-                            $buffer .= fread($this->sock, $chunk_size);
-                            $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($buffer));
+                            $mod = $max_chunk_size % ($matches[1] - strlen($chunk));
+                            $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($chunk));
+                            $chunk .= fread($this->sock, $chunk_size);
+                            $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($chunk));
                         } while ($mod == $max_chunk_size);
-                        break;
-                    } else {
-                        break;
                     }
+                    self::update_file_or_buffer($chunk, $fp, $buffer);
+                    break;
                 } else {
                     $buffer = '';
                     break;
@@ -1548,20 +1553,23 @@ EOD;
 
             // data is to big to handle it as one. Get it chunk per chunk...
             //trying to get the full length of max_chunk_size
-            $buffer = fread($this->sock, $max_chunk_size);
-            $loadsize = strlen($buffer);
+            $chunk = fread($this->sock, $max_chunk_size);
+            $loadsize = strlen($chunk);
+            self::update_file_or_buffer($chunk, $fp, $buffer);
             if ($loadsize < $max_chunk_size) {
                 $max_chunk_size = $loadsize;
             }
             do {
-                $mod = $max_chunk_size % ($matches[1] - strlen($buffer));
-                $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - strlen($buffer));
-                $buffer .= fread($this->sock, $chunk_size);
-                $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . strlen($buffer));
+                $mod = $max_chunk_size % ($matches[1] - $loadsize);
+                $chunk_size = ($mod == $max_chunk_size ? $max_chunk_size : $matches[1] - $loadsize);
+                $chunk = fread($this->sock, $chunk_size);
+                self::update_file_or_buffer($chunk, $fp, $buffer);
+                $loadsize += strlen($chunk);
+                $this->_error_log('mod: ' . $mod . ' chunk: ' . $chunk_size . ' total: ' . $loadsize);
             } while ($mod == $max_chunk_size);
-            $loadsize = strlen($buffer);
             if ($loadsize < $matches[1]) {
-                $buffer .= fread($this->sock, $matches[1] - $loadsize);
+                $chunk = fread($this->sock, $matches[1] - $loadsize);
+                self::update_file_or_buffer($chunk, $fp, $buffer);
             }
             break;
 
@@ -1577,7 +1585,8 @@ EOD;
             $this->_error_log('reading until feof...' . $header);
             socket_set_timeout($this->sock, 0, 0);
             while (!feof($this->sock)) {
-                $buffer .= fread($this->sock, 4096);
+                $chunk = fread($this->sock, 4096);
+                self::update_file_or_buffer($chunk, $fp, $buffer);
             }
             // renew the socket timeout...does it do something ???? Is it needed. More debugging needed...
             socket_set_timeout($this->sock, $this->_socket_timeout, 0);
@@ -1591,6 +1600,19 @@ EOD;
 
     }
 
+    /**
+     * Write the chunk to the file if $fp is set, otherwise append the data to the buffer
+     * @param string $chunk the data to add
+     * @param resource $fp the file handle to write to (or null)
+     * @param string &$buffer the buffer to append to (if $fp is null)
+     */
+    static private function update_file_or_buffer($chunk, $fp, &$buffer) {
+        if ($fp) {
+            fwrite($fp, $chunk);
+        } else {
+            $buffer .= $chunk;
+        }
+    }
 
     /**
      * Private method process_respond
index 0975b87..edb655f 100644 (file)
@@ -99,7 +99,13 @@ YUI.add('moodle-core-blocks', function(Y) {
         },
 
         get_block_region : function(node) {
-            return node.ancestor('div.'+CSS.BLOCKREGION).get('id').replace(/region/i, 'side');
+            var region = node.ancestor('div.'+CSS.BLOCKREGION).get('id').replace(/region-/i, '');
+            if (Y.Array.indexOf(this.get('regions'), region) === -1) {
+                // Must be standard side-X
+                return 'side-' + region;
+            }
+            // Perhaps custom region
+            return region;
         },
 
         get_region_id : function(node) {
@@ -207,6 +213,7 @@ YUI.add('moodle-core-blocks', function(Y) {
                 courseid : this.get('courseid'),
                 pagelayout : this.get('pagelayout'),
                 pagetype : this.get('pagetype'),
+                subpage : this.get('subpage'),
                 action : 'move',
                 bui_moveid : this.get_block_id(dragnode),
                 bui_newregion : this.get_block_region(dropnode)
@@ -262,6 +269,9 @@ YUI.add('moodle-core-blocks', function(Y) {
             pagetype : {
                 value : null
             },
+            subpage : {
+                value : null
+            },
             regions : {
                 value : null
             }
index 9fba3ba..8d7884b 100644 (file)
@@ -171,6 +171,9 @@ class input_manager extends singleton_pattern {
         $supportedoptions = array(
             array('', 'passfile', input_manager::TYPE_FILE, 'File name of the passphrase file (HTTP access only)'),
             array('', 'password', input_manager::TYPE_RAW, 'Session passphrase (HTTP access only)'),
+            array('', 'proxy', input_manager::TYPE_RAW, 'HTTP proxy host and port (e.g. \'our.proxy.edu:8888\')'),
+            array('', 'proxytype', input_manager::TYPE_RAW, 'Proxy type (HTTP or SOCKS5)'),
+            array('', 'proxyuserpwd', input_manager::TYPE_RAW, 'Proxy username and password (e.g. \'username:password\')'),
             array('', 'returnurl', input_manager::TYPE_URL, 'Return URL (HTTP access only)'),
             array('d', 'dataroot', input_manager::TYPE_PATH, 'Full path to the dataroot (moodledata) directory'),
             array('h', 'help', input_manager::TYPE_FLAG, 'Prints usage information'),
@@ -618,9 +621,12 @@ class output_http_provider extends output_provider {
      * @param Exception $e uncaught exception
      */
     public function exception(Exception $e) {
+
+        $docslink = 'http://docs.moodle.org/en/admin/mdeploy/'.get_class($e);
         $this->start_output();
         echo('<h1>Oops! It did it again</h1>');
-        echo('<p><strong>Moodle deployment utility had a trouble with your request. See the debugging information for more details.</strong></p>');
+        echo('<p><strong>Moodle deployment utility had a trouble with your request.
+            See <a href="'.$docslink.'">the docs page</a> and the debugging information for more details.</strong></p>');
         echo('<pre>');
         echo exception_handlers::format_exception_info($e);
         echo('</pre>');
@@ -748,7 +754,7 @@ class worker extends singleton_pattern {
             }
 
             // Looking good, let's try it.
-            if (!$this->move_directory($sourcelocation, $backuplocation)) {
+            if (!$this->move_directory($sourcelocation, $backuplocation, true)) {
                 throw new backup_folder_exception('Unable to backup the current version of the plugin (moving failed)');
             }
 
@@ -967,6 +973,36 @@ class worker extends singleton_pattern {
         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 20); // nah, moodle.org is never unavailable! :-p
         curl_setopt($ch, CURLOPT_URL, $source);
 
+        $dataroot = $this->input->get_option('dataroot');
+        $cacertfile = $dataroot.'/moodleorgca.crt';
+        if (is_readable($cacertfile)) {
+            // Do not use CA certs provided by the operating system. Instead,
+            // use this CA cert to verify the ZIP provider.
+            $this->log('Using custom CA certificate '.$cacertfile);
+            curl_setopt($ch, CURLOPT_CAINFO, $cacertfile);
+        }
+
+        $proxy = $this->input->get_option('proxy', false);
+        if (!empty($proxy)) {
+            curl_setopt($ch, CURLOPT_PROXY, $proxy);
+
+            $proxytype = $this->input->get_option('proxytype', false);
+            if (strtoupper($proxytype) === 'SOCKS5') {
+                $this->log('Using SOCKS5 proxy');
+                curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
+            } else if (!empty($proxytype)) {
+                $this->log('Using HTTP proxy');
+                curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
+                curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, false);
+            }
+
+            $proxyuserpwd = $this->input->get_option('proxyuserpwd', false);
+            if (!empty($proxyuserpwd)) {
+                curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyuserpwd);
+                curl_setopt($ch, CURLOPT_PROXYAUTH, CURLAUTH_BASIC | CURLAUTH_NTLM);
+            }
+        }
+
         $targetfile = fopen($target, 'w');
 
         if (!$targetfile) {
@@ -1100,23 +1136,45 @@ class worker extends singleton_pattern {
     /**
      * Moves the given source into a new location recursively
      *
+     * The target location can not exist.
+     *
      * @param string $source full path to the existing directory
      * @param string $destination full path to the new location of the folder
+     * @param bool $keepsourceroot should the root of the $source be kept or removed at the end
      * @return bool
      */
-    protected function move_directory($source, $target) {
+    protected function move_directory($source, $target, $keepsourceroot = false) {
 
         if (file_exists($target)) {
             throw new filesystem_exception('Unable to move the directory - target location already exists');
         }
 
+        return $this->move_directory_into($source, $target, $keepsourceroot);
+    }
+
+    /**
+     * Moves the given source into a new location recursively
+     *
+     * If the target already exists, files are moved into it. The target is created otherwise.
+     *
+     * @param string $source full path to the existing directory
+     * @param string $destination full path to the new location of the folder
+     * @param bool $keepsourceroot should the root of the $source be kept or removed at the end
+     * @return bool
+     */
+    protected function move_directory_into($source, $target, $keepsourceroot = false) {
+
         if (is_dir($source)) {
             $handle = opendir($source);
         } else {
             throw new filesystem_exception('Source location is not a directory');
         }
 
-        mkdir($target, 02777);
+        if (is_dir($target)) {
+            $result = true;
+        } else {
+            $result = mkdir($target, 02777);
+        }
 
         while ($filename = readdir($handle)) {
             $sourcepath = $source.'/'.$filename;
@@ -1127,26 +1185,37 @@ class worker extends singleton_pattern {
             }
 
             if (is_dir($sourcepath)) {
-                $this->move_directory($sourcepath, $targetpath);
+                $result = $result && $this->move_directory($sourcepath, $targetpath, false);
 
             } else {
-                rename($sourcepath, $targetpath);
+                $result = $result && rename($sourcepath, $targetpath);
             }
         }
 
         closedir($handle);
-        return rmdir($source);
+
+        if (!$keepsourceroot) {
+            $result = $result && rmdir($source);
+        }
+
+        clearstatcache();
+
+        return $result;
     }
 
     /**
      * Deletes the given directory recursively
      *
      * @param string $path full path to the directory
+     * @param bool $keeppathroot should the root of the $path be kept (i.e. remove the content only) or removed too
+     * @return bool
      */
-    protected function remove_directory($path) {
+    protected function remove_directory($path, $keeppathroot = false) {
+
+        $result = true;
 
         if (!file_exists($path)) {
-            return;
+            return $result;
         }
 
         if (is_dir($path)) {
@@ -1163,15 +1232,22 @@ class worker extends singleton_pattern {
             }
 
             if (is_dir($filepath)) {
-                $this->remove_directory($filepath);
+                $result = $result && $this->remove_directory($filepath, false);
 
             } else {
-                unlink($filepath);
+                $result = $result && unlink($filepath);
             }
         }
 
         closedir($handle);
-        return rmdir($path);
+
+        if (!$keeppathroot) {
+            $result = $result && rmdir($path);
+        }
+
+        clearstatcache();
+
+        return $result;
     }
 
     /**
@@ -1206,8 +1282,8 @@ class worker extends singleton_pattern {
 
         if (!$zip->extractTo($plugintyperoot)) {
             $zip->close();
-            $this->remove_directory($expectedlocation); // just in case something was created
-            $this->move_directory($backuplocation, $expectedlocation);
+            $this->remove_directory($expectedlocation, true); // just in case something was created
+            $this->move_directory_into($backuplocation, $expectedlocation);
             throw new zip_exception('Unable to extract the zip package');
         }
 
index 471a9d3..4724172 100644 (file)
@@ -85,6 +85,30 @@ class testable_input_manager extends input_manager {
 }
 
 
+/**
+ * Testable subclass
+ *
+ * @copyright 2012 David Mudrak <david@moodle.com>
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class testable_worker extends worker {
+
+    /**
+     * Provides access to the protected method.
+     */
+    public function move_directory($source, $target, $keepsourceroot = false) {
+        return parent::move_directory($source, $target, $keepsourceroot);
+    }
+
+    /**
+     * Provides access to the protected method.
+     */
+    public function remove_directory($path, $keeppathroot = false) {
+        return parent::remove_directory($path, $keeppathroot);
+    }
+}
+
+
 /**
  * Test cases for the mdeploy utility
  *
@@ -212,4 +236,26 @@ class mdeploytest extends PHPUnit_Framework_TestCase {
             $this->assertTrue(true);
         }
     }
+
+    public function test_moving_and_removing_directories() {
+        $worker = testable_worker::instance();
+
+        $root = sys_get_temp_dir().'/'.uniqid('mdeploytest', true);
+        mkdir($root.'/a', 0777, true);
+        touch($root.'/a/a.txt');
+
+        $this->assertTrue(file_exists($root.'/a/a.txt'));
+        $this->assertFalse(file_exists($root.'/b/a.txt'));
+        $this->assertTrue($worker->move_directory($root.'/a', $root.'/b'));
+        $this->assertFalse(is_dir($root.'/a'));
+        $this->assertTrue(file_exists($root.'/b/a.txt'));
+        $this->assertTrue($worker->move_directory($root.'/b', $root.'/c', true));
+        $this->assertTrue(file_exists($root.'/c/a.txt'));
+        $this->assertFalse(file_exists($root.'/b/a.txt'));
+        $this->assertTrue(is_dir($root.'/b'));
+        $this->assertTrue($worker->remove_directory($root.'/c', true));
+        $this->assertFalse(file_exists($root.'/c/a.txt'));
+        $this->assertTrue($worker->remove_directory($root.'/c'));
+        $this->assertFalse(is_dir($root.'/c'));
+    }
 }
index 2d78553..89c4373 100644 (file)
@@ -26,31 +26,15 @@ require_once(dirname(__FILE__) . '/../config.php');
 require_once($CFG->dirroot . '/message/lib.php');
 
 $userid = optional_param('id', $USER->id, PARAM_INT);    // user id
-$course = optional_param('course', SITEID, PARAM_INT);   // course id (defaults to Site)
 $disableall = optional_param('disableall', 0, PARAM_BOOL); //disable all of this user's notifications
 
 $url = new moodle_url('/message/edit.php');
 $url->param('id', $userid);
-$url->param('course', $course);
 
 $PAGE->set_url($url);
 $PAGE->set_popup_notification_allowed(false); // We are within the messaging system so don't show message popups
 
-if (!$course = $DB->get_record('course', array('id' => $course))) {
-    print_error('invalidcourseid');
-}
-
-if ($course->id != SITEID) {
-    require_login($course);
-
-} else {
-    if (!isloggedin()) {
-        if (empty($SESSION->wantsurl)) {
-            $SESSION->wantsurl = $CFG->httpswwwroot.'/message/edit.php';
-        }
-        redirect(get_login_url());
-    }
-}
+require_login();
 
 if (isguestuser()) {
     print_error('guestnoeditmessage', 'message');
@@ -71,10 +55,6 @@ $PAGE->requires->js_init_call('M.core_message.init_editsettings');
 if ($user->id == $USER->id) {
     //editing own message profile
     require_capability('moodle/user:editownmessageprofile', $systemcontext);
-    if ($course->id != SITEID && $node = $PAGE->navigation->find($course->id, navigation_node::TYPE_COURSE)) {
-        $node->make_active();
-        $PAGE->navbar->includesettingsbase = true;
-    }
 } else {
     // teachers, parents, etc.
     require_capability('moodle/user:editmessageprofile', $personalcontext);
@@ -144,7 +124,7 @@ if (($form = data_submitted()) && confirm_sesskey()) {
         print_error('cannotupdateusermsgpref');
     }
 
-    redirect("$CFG->wwwroot/message/edit.php?id=$user->id&course=$course->id");
+    redirect("$CFG->wwwroot/message/edit.php?id=$user->id");
 }
 
 /// Load preferences
@@ -178,15 +158,9 @@ $preferences->blocknoncontacts  =  get_user_preferences( 'message_blocknoncontac
 //$preferences->beepnewmessage    =  get_user_preferences( 'message_beepnewmessage', '', $user->id);
 
 /// Display page header
-$streditmymessage = get_string('editmymessage', 'message');
-$strparticipants  = get_string('participants');
-
-$PAGE->set_title("$course->shortname: $streditmymessage");
-if ($course->id != SITEID) {
-    $PAGE->set_heading("$course->fullname: $streditmymessage");
-} else {
-    $PAGE->set_heading($course->fullname);
-}
+$strmessaging = get_string('messaging', 'message');
+$PAGE->set_title($strmessaging);
+$PAGE->set_heading($strmessaging);
 
 // Grab the renderer
 $renderer = $PAGE->get_renderer('core', 'message');
index 14d5a68..0d81b3a 100644 (file)
@@ -71,17 +71,6 @@ define('MESSAGE_PERMITTED_MASK', 0x0c); // 1100
  */
 define('MESSAGE_DEFAULT_PERMITTED', 'permitted');
 
-//TODO: defaults must be initialised via settings - this is a bad hack! (skodak)
-if (!isset($CFG->message_contacts_refresh)) {  // Refresh the contacts list every 60 seconds
-    $CFG->message_contacts_refresh = 60;
-}
-if (!isset($CFG->message_chat_refresh)) {      // Look for new comments every 5 seconds
-    $CFG->message_chat_refresh = 5;
-}
-if (!isset($CFG->message_offline_time)) {
-    $CFG->message_offline_time = 300;
-}
-
 /**
  * Print the selector that allows the user to view their contacts, course participants, their recent
  * conversations etc
@@ -179,7 +168,18 @@ function message_print_participants($context, $courseid, $contactselecturl=null,
     }
 
     $countparticipants = count_enrolled_users($context);
-    $participants = get_enrolled_users($context, '', 0, 'u.*', '', $page*MESSAGE_CONTACTS_PER_PAGE, MESSAGE_CONTACTS_PER_PAGE);
+
+    list($esql, $params) = get_enrolled_sql($context);
+    $params['mcuserid'] = $USER->id;
+    $ufields = user_picture::fields('u');
+
+    $sql = "SELECT $ufields, mc.id as contactlistid, mc.blocked
+              FROM {user} u
+              JOIN ($esql) je ON je.id = u.id
+              LEFT JOIN {message_contacts} mc ON mc.contactid = u.id AND mc.userid = :mcuserid
+             WHERE u.deleted = 0";
+
+    $participants = $DB->get_records_sql($sql, $params, $page * MESSAGE_CONTACTS_PER_PAGE, MESSAGE_CONTACTS_PER_PAGE);
 
     $pagingbar = new paging_bar($countparticipants, $page, MESSAGE_CONTACTS_PER_PAGE, $PAGE->url, 'page');
     echo $OUTPUT->render($pagingbar);
@@ -190,11 +190,23 @@ function message_print_participants($context, $courseid, $contactselecturl=null,
     echo html_writer::tag('td', $titletodisplay, array('colspan' => 3, 'class' => 'heading'));
     echo html_writer::end_tag('tr');
 
-    //todo these need to come from somewhere if the course participants list is to show users with unread messages
-    $iscontact = true;
-    $isblocked = false;
     foreach ($participants as $participant) {
         if ($participant->id != $USER->id) {
+
+            $iscontact = false;
+            $isblocked = false;
+            if ( $participant->contactlistid )  {
+                if ($participant->blocked == 0) {
+                    // Is contact. Is not blocked.
+                    $iscontact = true;
+                    $isblocked = false;
+                } else {
+                    // Is blocked.
+                    $iscontact = false;
+                    $isblocked = true;
+                }
+            }
+
             $participant->messagecount = 0;//todo it would be nice if the course participant could report new messages
             message_print_contactlist_user($participant, $iscontact, $isblocked, $contactselecturl, $showactionlinks, $user2);
         }
index da3d22a..e4fb8ae 100644 (file)
@@ -40,6 +40,10 @@ function xmldb_message_email_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index eb78904..26cd701 100644 (file)
@@ -40,6 +40,10 @@ function xmldb_message_jabber_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index ef556ee..c87e7d8 100644 (file)
@@ -40,6 +40,10 @@ function xmldb_message_popup_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 1d50651..686bf0a 100644 (file)
@@ -592,7 +592,7 @@ abstract class assign_plugin {
 
     /**
      * If this plugin should not include a column in the grading table or a row on the summary page
-     * return false
+     * then return false
      *
      * @return bool
      */
index 20b7b91..9b5405e 100644 (file)
@@ -197,6 +197,10 @@ function xmldb_assign_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 97409c6..d9ec10a 100644 (file)
@@ -33,6 +33,10 @@ function xmldb_assignfeedback_comments_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index e59722d..4aa3a27 100644 (file)
@@ -33,6 +33,10 @@ function xmldb_assignfeedback_file_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index c3338b3..a5aad69 100644 (file)
@@ -83,7 +83,7 @@ class assignfeedback_file_import_zip_form extends moodleform implements renderab
                     $userdesc = fullname($user);
                     if ($assignment->is_blind_marking()) {
                         $userdesc = get_string('hiddenuser', 'assign') .
-                                    $assignment->get_unique_id_for_user($user->id);
+                                    $assignment->get_uniqueid_for_user($user->id);
                     }
                     $grade = $assignment->get_user_grade($user->id, false);
 
index 326dc5a..baba18d 100644 (file)
@@ -92,7 +92,7 @@ class assignfeedback_offline_import_grades_form extends moodleform implements re
             $modified = $record->modified;
             $userdesc = fullname($user);
             if ($assignment->is_blind_marking()) {
-                $userdesc = get_string('hiddenuser', 'assign') . $assignment->get_unique_id_for_user($user->id);
+                $userdesc = get_string('hiddenuser', 'assign') . $assignment->get_uniqueid_for_user($user->id);
             }
 
             $usergrade = $assignment->get_user_grade($user->id, false);
index 38b73c8..e2a7284 100644 (file)
@@ -70,7 +70,7 @@ class mod_assign_grading_batch_operations_form extends moodleform {
         $mform->addElement('hidden', 'returnaction', 'grading');
 
         $objs = array();
-        $objs[] =& $mform->createElement('select', 'operation', '', $options);
+        $objs[] =& $mform->createElement('select', 'operation', get_string('chooseoperation', 'assign'), $options);
         $objs[] =& $mform->createElement('submit', 'submit', get_string('go'));
         $mform->addElement('group', 'actionsgrp', get_string('batchoperationsdescription', 'assign'), $objs, ' ', false);
 
index b24f4fa..8eb7ce3 100644 (file)
@@ -59,7 +59,7 @@ class mod_assign_grading_options_form extends moodleform {
 
         // quickgrading
         if ($instance['showquickgrading']) {
-            $mform->addElement('checkbox', 'quickgrading', get_string('quickgrading', 'assign'), '');
+            $mform->addElement('checkbox', 'quickgrading', get_string('quickgrading', 'assign'), '', $dirtyclass);
             $mform->addHelpButton('quickgrading', 'quickgrading', 'assign');
             $mform->setDefault('quickgrading', $instance['quickgrading']);
         }
index e9747e6..c565d7b 100644 (file)
@@ -772,7 +772,7 @@ class assign_grading_table extends table_sql implements renderable {
         $edit .= html_writer::start_tag('ul');
         $edit .= html_writer::start_tag('li', array('class'=>'menuicon'));
 
-        $menuicon = $this->output->pix_icon('i/menu', get_string('actions'));
+        $menuicon = $this->output->pix_icon('t/contextmenu', get_string('actions'));
         $edit .= $this->output->action_link('#menu' . $row->id, $menuicon, null, array('class'=>'yui3-menu-label'));
         $edit .= $this->output->container_start(array('yui3-menu', 'yui3-loading'), 'menu' . $row->id);
         $edit .= $this->output->container_start(array('yui3-menu-content'));
index 671607b..ef1c7ed 100644 (file)
@@ -72,6 +72,8 @@ $string['batchoperationreverttodraft'] = 'revert submissions to draft';
 $string['blindmarking'] = 'Blind marking';
 $string['blindmarking_help'] = 'Blind marking hides the identity of students to markers. Blind marking settings will be locked once a submission or grade has been made in relation to this assignment.';
 $string['changegradewarning'] = 'This assignment has graded submissions and changing the grade will not automatically re-calculate existing submission grades. You must re-grade all existing submissions, if you wish to change the grade.';
+$string['choosegradingaction'] = 'Grading action';
+$string['chooseoperation'] = 'Choose operation';
 $string['comment'] = 'Comment';
 $string['completionsubmit'] = 'Student must submit to this activity to complete it';
 $string['conversionexception'] = 'Could not convert assignment. Exception was: {$a}.';
index 7c5a925..39deae3 100644 (file)
@@ -1022,6 +1022,8 @@ class assign {
 
         static $scalegrades = array();
 
+        $o = '';
+
         if ($this->get_instance()->grade >= 0) {
             // Normal number
             if ($editing && $this->get_instance()->grade > 0) {
@@ -1030,17 +1032,20 @@ class assign {
                 } else {
                     $displaygrade = format_float($grade);
                 }
-                $o = '<label class="accesshide" for="quickgrade_' . $userid . '">' . get_string('usergrade', 'assign') . '</label>';
-                $o .= '<input type="text" id="quickgrade_' . $userid . '" name="quickgrade_' . $userid . '" value="' . $displaygrade
-                        . '" size="6" maxlength="10" class="quickgrade"/>';
+                $o .= '<label class="accesshide" for="quickgrade_' . $userid . '">' . get_string('usergrade', 'assign') . '</label>';
+                $o .= '<input type="text" id="quickgrade_' . $userid . '" name="quickgrade_' . $userid . '" value="' .
+                      $displaygrade . '" size="6" maxlength="10" class="quickgrade"/>';
                 $o .= '&nbsp;/&nbsp;' . format_float($this->get_instance()->grade,2);
                 $o .= '<input type="hidden" name="grademodified_' . $userid . '" value="' . $modified . '"/>';
                 return $o;
             } else {
+                $o .= '<input type="hidden" name="grademodified_' . $userid . '" value="' . $modified . '"/>';
                 if ($grade == -1 || $grade === null) {
-                    return '-';
+                    $o .= '-';
+                    return $o;
                 } else {
-                    return format_float(($grade),2) .'&nbsp;/&nbsp;'. format_float($this->get_instance()->grade,2);
+                    $o .= format_float(($grade),2) .'&nbsp;/&nbsp;'. format_float($this->get_instance()->grade,2);
+                    return $o;
                 }
             }
 
@@ -1050,11 +1055,12 @@ class assign {
                 if ($scale = $DB->get_record('scale', array('id'=>-($this->get_instance()->grade)))) {
                     $this->cache['scale'] = make_menu_from_list($scale->scale);
                 } else {
-                    return '-';
+                    $o .= '-';
+                    return $o;
                 }
             }
             if ($editing) {
-                $o = '<label class="accesshide" for="quickgrade_' . $userid . '">' . get_string('usergrade', 'assign') . '</label>';
+                $o .= '<label class="accesshide" for="quickgrade_' . $userid . '">' . get_string('usergrade', 'assign') . '</label>';
                 $o .= '<select name="quickgrade_' . $userid . '" class="quickgrade">';
                 $o .= '<option value="-1">' . get_string('nograde') . '</option>';
                 foreach ($this->cache['scale'] as $optionid => $option) {
@@ -1070,9 +1076,11 @@ class assign {
             } else {
                 $scaleid = (int)$grade;
                 if (isset($this->cache['scale'][$scaleid])) {
-                    return $this->cache['scale'][$scaleid];
+                    $o .= $this->cache['scale'][$scaleid];
+                    return $o;
                 }
-                return '-';
+                $o .= '-';
+                return $o;
             }
         }
     }
@@ -1123,22 +1131,37 @@ class assign {
     /**
      * Load a count of users submissions in the current module that require grading
      * This means the submission modification time is more recent than the
-     * grading modification time.
+     * grading modification time and the status is SUBMITTED.
      *
      * @return int number of matching submissions
      */
     public function count_submissions_need_grading() {
         global $DB;
 
-        $params = array($this->get_course_module()->instance);
+        if ($this->get_instance()->teamsubmission) {
+            // This does not make sense for group assignment because the submission is shared.
+            return 0;
+        }
+
+        $currentgroup = groups_get_activity_group($this->get_course_module(), true);
+        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
+
+        $params['assignid'] = $this->get_instance()->id;
+        $params['submitted'] = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
+
+        $sql = 'SELECT COUNT(s.userid)
+                   FROM {assign_submission} s
+                   LEFT JOIN {assign_grades} g ON
+                        s.assignment = g.assignment AND
+                        s.userid = g.userid
+                   JOIN(' . $esql . ') AS e ON e.id = s.userid
+                   WHERE
+                        s.assignment = :assignid AND
+                        s.timemodified IS NOT NULL AND
+                        s.status = :submitted AND
+                        (s.timemodified > g.timemodified OR g.timemodified IS NULL)';
 
-        return $DB->count_records_sql("SELECT COUNT('x')
-                                       FROM {assign_submission} s
-                                       LEFT JOIN {assign_grades} g ON s.assignment = g.assignment AND s.userid = g.userid
-                                       WHERE s.assignment = ?
-                                           AND s.timemodified IS NOT NULL
-                                           AND (s.timemodified > g.timemodified OR g.timemodified IS NULL)",
-                                       $params);
+        return $DB->count_records_sql($sql, $params);
     }
 
     /**
@@ -1153,8 +1176,15 @@ class assign {
             return 0;
         }
 
-        $sql = 'SELECT COUNT(id) FROM {assign_grades} WHERE assignment = ?';
-        $params = array($this->get_course_module()->instance);
+        $currentgroup = groups_get_activity_group($this->get_course_module(), true);
+        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
+
+        $params['assignid'] = $this->get_instance()->id;
+
+        $sql = 'SELECT COUNT(g.userid)
+                   FROM {assign_grades} g
+                   JOIN(' . $esql . ') AS e ON e.id = g.userid
+                   WHERE g.assignment = :assignid';
 
         return $DB->count_records_sql($sql, $params);
     }
@@ -1171,14 +1201,33 @@ class assign {
             return 0;
         }
 
-        $sql = 'SELECT COUNT(id) FROM {assign_submission} WHERE assignment = ?';
-        $params = array($this->get_course_module()->instance);
+        $params = array();
 
         if ($this->get_instance()->teamsubmission) {
-            // only look at team submissions
-            $sql .= ' AND userid = ?';
-            $params[] = 0;
+            // We cannot join on the enrolment tables for group submissions (no userid).
+            $sql = 'SELECT COUNT(s.groupid)
+                        FROM {assign_submission} s
+                        WHERE
+                            s.assignment = :assignid AND
+                            s.timemodified IS NOT NULL AND
+                            s.userid = :groupuserid';
+
+            $params['assignid'] = $this->get_instance()->id;
+            $params['groupuserid'] = 0;
+        } else {
+            $currentgroup = groups_get_activity_group($this->get_course_module(), true);
+            list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
+
+            $params['assignid'] = $this->get_instance()->id;
+
+            $sql = 'SELECT COUNT(s.userid)
+                       FROM {assign_submission} s
+                       JOIN(' . $esql . ') AS e ON e.id = s.userid
+                       WHERE
+                            s.assignment = :assignid AND
+                            s.timemodified IS NOT NULL';
         }
+
         return $DB->count_records_sql($sql, $params);
     }
 
@@ -1190,14 +1239,32 @@ class assign {
      */
     public function count_submissions_with_status($status) {
         global $DB;
-        $sql = 'SELECT COUNT(id) FROM {assign_submission} WHERE assignment = ? AND status = ?';
-        $params = array($this->get_course_module()->instance, $status);
+
+        $currentgroup = groups_get_activity_group($this->get_course_module(), true);
+        list($esql, $params) = get_enrolled_sql($this->get_context(), 'mod/assign:submit', $currentgroup, false);
+
+        $params['assignid'] = $this->get_instance()->id;
+        $params['submissionstatus'] = $status;
 
         if ($this->get_instance()->teamsubmission) {
-            // only look at team submissions
-            $sql .= ' AND userid = ?';
-            $params[] = 0;
+            $sql = 'SELECT COUNT(s.groupid)
+                        FROM {assign_submission} s
+                        WHERE
+                            s.assignment = :assignid AND
+                            s.timemodified IS NOT NULL AND
+                            s.userid = :groupuserid AND
+                            s.status = :submissionstatus';
+            $params['groupuserid'] = 0;
+        } else {
+            $sql = 'SELECT COUNT(s.userid)
+                        FROM {assign_submission} s
+                        JOIN(' . $esql . ') AS e ON e.id = s.userid
+                        WHERE
+                            s.assignment = :assignid AND
+                            s.timemodified IS NOT NULL AND
+                            s.status = :submissionstatus';
         }
+
         return $DB->count_records_sql($sql, $params);
     }
 
@@ -1964,12 +2031,7 @@ class assign {
             $submission->userid       = $userid;
             $submission->timecreated = time();
             $submission->timemodified = $submission->timecreated;
-
-            if ($this->get_instance()->submissiondrafts) {
-                $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
-            } else {
-                $submission->status = ASSIGN_SUBMISSION_STATUS_SUBMITTED;
-            }
+            $submission->status = ASSIGN_SUBMISSION_STATUS_DRAFT;
             $sid = $DB->insert_record('assign_submission', $submission);
             $submission->id = $sid;
             return $submission;
@@ -2114,6 +2176,9 @@ class assign {
             } else {
                 $showsubmit = $showedit && $submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_DRAFT);
             }
+            if (!$this->get_instance()->submissiondrafts) {
+                $showsubmit = false;
+            }
             $viewfullnames = has_capability('moodle/site:viewfullnames', $this->get_course_context());
 
             $o .= $this->get_renderer()->render(new assign_submission_status($this->get_instance()->allowsubmissionsfromdate,
@@ -2260,6 +2325,7 @@ class assign {
         }
 
         $gradingactions = new url_select($links);
+        $gradingactions->set_label(get_string('choosegradingaction', 'assign'));
 
         $gradingmanager = get_grading_manager($this->get_context(), 'mod_assign', 'submissions');
 
@@ -2653,6 +2719,9 @@ class assign {
             if ($submission && ($submission->status == ASSIGN_SUBMISSION_STATUS_SUBMITTED)) {
                 $showsubmit = false;
             }
+            if (!$this->get_instance()->submissiondrafts) {
+                $showsubmit = false;
+            }
             $extensionduedate = null;
             if ($grade) {
                 $extensionduedate = $grade->extensionduedate;
@@ -2980,7 +3049,7 @@ class assign {
      * @param int $userid - Optional userid so we can see if a different user can submit
      * @return bool
      */
-    private function submissions_open($userid = 0) {
+    public function submissions_open($userid = 0) {
         global $USER;
 
         if (!$userid) {
@@ -3448,19 +3517,16 @@ class assign {
         // gets a list of possible users and look for values based upon that.
         foreach ($participants as $userid => $unused) {
             $modified = optional_param('grademodified_' . $userid, -1, PARAM_INT);
-            if ($modified >= 0) {
-                // gather the userid, updated grade and last modified value
-                $record = new stdClass();
-                $record->userid = $userid;
-                $record->grade = unformat_float(required_param('quickgrade_' . $record->userid, PARAM_TEXT));
-                $record->lastmodified = $modified;
-                $record->gradinginfo = grade_get_grades($this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, array($userid));
-                $users[$userid] = $record;
+            // Gather the userid, updated grade and last modified value.
+            $record = new stdClass();
+            $record->userid = $userid;
+            $gradevalue = optional_param('quickgrade_' . $userid, '', PARAM_TEXT);
+            if($modified >= 0) {
+                $record->grade = unformat_float(optional_param('quickgrade_' . $record->userid, -1, PARAM_TEXT));
             }
-        }
-        if (empty($users)) {
-            // Quick check to see whether we have any users to update and we don't
-            return get_string('quickgradingchangessaved', 'assign'); // Technical lie
+            $record->lastmodified = $modified;
+            $record->gradinginfo = grade_get_grades($this->get_course()->id, 'mod', 'assign', $this->get_instance()->id, array($userid));
+            $users[$userid] = $record;
         }
 
         list($userids, $params) = $DB->get_in_or_equal(array_keys($users), SQL_PARAMS_NAMED);
@@ -3904,6 +3970,11 @@ class assign {
                 $grademenu = make_grades_menu($this->get_instance()->grade);
                 if (count($grademenu) > 0) {
                     $gradingelement = $mform->addElement('select', 'grade', get_string('grade').':', $grademenu);
+
+                    // The grade is already formatted with format_float so it needs to be converted back to an integer.
+                    if (!empty($data->grade)) {
+                        $data->grade = (int)unformat_float($data->grade);
+                    }
                     $mform->setType('grade', PARAM_INT);
                     if ($gradingdisabled) {
                         $gradingelement->freeze();
diff --git a/mod/assign/pix/gradefeedback.png b/mod/assign/pix/gradefeedback.png
new file mode 100644 (file)
index 0000000..3264934
Binary files /dev/null and b/mod/assign/pix/gradefeedback.png differ
diff --git a/mod/assign/pix/gradefeedback.svg b/mod/assign/pix/gradefeedback.svg
new file mode 100644 (file)
index 0000000..f6b8252
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="12px" height="12px" viewBox="0 0 12 12" style="overflow:visible;enable-background:new 0 0 12 12;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M11,5.2V11c0,0.5-0.5,1-1,1H1c-0.5,0-1-0.5-1-1V2c0-0.5,0.5-1,1-1h5.8l-1,1H2C1.5,2,1,2.4,1,3v3.8V10\r
+       v1h1h3.2H9c0.5,0,1-0.5,1-1V6.2L11,5.2z M6.8,2.3L2,7.1V10h2.8l4.8-4.8L6.8,2.3z M11.4,0.6L11.4,0.6c-0.8-0.8-2.1-0.8-2.8,0L7.8,1.3\r
+       l2.8,2.8l0.7-0.7C12.2,2.6,12.2,1.4,11.4,0.6z"/>\r
+</svg>\r
index a2f94da..c8b83e5 100644 (file)
@@ -254,8 +254,10 @@ class mod_assign_renderer extends plugin_renderer_base {
         if ($summary->submissionsenabled) {
             $this->add_table_row_tuple($t, get_string('numberofsubmittedassignments', 'assign'),
                                        $summary->submissionssubmittedcount);
-            $this->add_table_row_tuple($t, get_string('numberofsubmissionsneedgrading', 'assign'),
-                                       $summary->submissionsneedgradingcount);
+            if (!$summary->teamsubmission) {
+                $this->add_table_row_tuple($t, get_string('numberofsubmissionsneedgrading', 'assign'),
+                                           $summary->submissionsneedgradingcount);
+            }
         }
 
         $time = time();
@@ -572,10 +574,11 @@ class mod_assign_renderer extends plugin_renderer_base {
             $t->data[] = $row;
 
             foreach ($status->submissionplugins as $plugin) {
+                $pluginshowsummary = !$plugin->is_empty($submission) || !$plugin->allow_submissions();
                 if ($plugin->is_enabled() &&
                     $plugin->is_visible() &&
                     $plugin->has_user_summary() &&
-                    !$plugin->is_empty($submission)) {
+                    $pluginshowsummary) {
 
                     $row = new html_table_row();
                     $cell1 = new html_table_cell($plugin->get_name());
index 45fd7e4..aa3c880 100644 (file)
@@ -114,7 +114,9 @@ div.earlysubmission {
 
 .gradingtable .yui3-menu-label {
     padding-left: 0px;
+    line-height: 12px;
 }
+.gradingtable .yui3-menu-label img { padding: 0 3px; }
 .gradingtable .yui3-menu li {
     list-style-type: none;
 }
@@ -125,6 +127,7 @@ div.earlysubmission {
 
 .gradingtable .yui3-menu .yui3-menu-content {
     border: 0px;
+    padding-top: 0;
 }
 
 #page-mod-assign-view div.gradingtable tr .quickgrademodified {
index 565fc0f..499d781 100644 (file)
@@ -32,6 +32,10 @@ function xmldb_assignsubmission_comments_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 29a1d5e..513dca9 100644 (file)
@@ -78,12 +78,13 @@ class assign_submission_comments extends assign_submission_plugin {
     }
 
     /**
-     * Always return false because at a minimum there is the comments control
+     * Always return true because the submission comments are not part of the submission form.
+     *
      * @param stdClass $submission
      * @return bool
      */
     public function is_empty(stdClass $submission) {
-        return false;
+        return true;
     }
 
   /**
index a86a5fb..87c15bb 100644 (file)
@@ -32,6 +32,10 @@ function xmldb_assignsubmission_file_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 056c0bb..b93cd4c 100644 (file)
@@ -32,6 +32,10 @@ function xmldb_assignsubmission_onlinetext_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index a94cc85..85f57a1 100644 (file)
@@ -226,7 +226,12 @@ class assign_submission_onlinetext extends assign_submission_plugin {
         $showviewlink = true;
 
         if ($onlinetextsubmission) {
-            $text = format_text($onlinetextsubmission->onlinetext, $onlinetextsubmission->onlineformat, array('context'=>$this->assignment->get_context()));
+            $text = $this->assignment->render_editor_content(ASSIGNSUBMISSION_ONLINETEXT_FILEAREA,
+                                                             $onlinetextsubmission->submission,
+                                                             $this->get_type(),
+                                                             'onlinetext',
+                                                             'assignsubmission_onlinetext');
+
             $shorttext = shorten_text($text, 140);
             $plagiarismlinks = '';
             if (!empty($CFG->enableplagiarism)) {
@@ -422,7 +427,9 @@ class assign_submission_onlinetext extends assign_submission_plugin {
      * @return bool
      */
     public function is_empty(stdClass $submission) {
-        return $this->view($submission) == '';
+        $onlinetextsubmission = $this->get_onlinetext_submission($submission->id);
+
+        return empty($onlinetextsubmission->onlinetext);
     }
 
     /**
index ff6ee6d..d8bb7c2 100644 (file)
@@ -143,6 +143,8 @@ class assign_upgrade_manager {
                     if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) {
                         $rollback = true;
                     }
+                } else {
+                    $plugin->disable();
                 }
             }
             foreach ($newassignment->get_feedback_plugins() as $plugin) {
@@ -151,6 +153,8 @@ class assign_upgrade_manager {
                     if (!$plugin->upgrade_settings($oldcontext, $oldassignment, $log)) {
                         $rollback = true;
                     }
+                } else {
+                    $plugin->disable();
                 }
             }
 
@@ -238,14 +242,12 @@ class assign_upgrade_manager {
 
             $newassignment->update_calendar($newcoursemodule->id);
 
-            // copy the grades from the old assignment to the new one
+            // Reassociate grade_items from the old assignment instance to the new assign instance.
+            // This includes outcome linked grade_items.
+            $params = array('assign', $newassignment->get_instance()->id, 'assignment', $oldassignment->id);
+            $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?';
+            $DB->execute($sql, $params);
 
-            $gradeitem = $DB->get_record('grade_items', array('iteminstance'=>$oldassignment->id, 'itemmodule'=>'assignment'), 'id', IGNORE_MISSING);
-            if ($gradeitem) {
-                $gradeitem->iteminstance = $newassignment->get_instance()->id;
-                $gradeitem->itemmodule = 'assign';
-                $DB->update_record('grade_items', $gradeitem);
-            }
             $gradesdone = true;
 
         } catch (Exception $exception) {
@@ -256,13 +258,10 @@ class assign_upgrade_manager {
         if ($rollback) {
             // roll back the grades changes
             if ($gradesdone) {
-                // copy the grades from the old assignment to the new one
-                $gradeitem = $DB->get_record('grade_items', array('iteminstance'=>$newassignment->get_instance()->id, 'itemmodule'=>'assign'), 'id', IGNORE_MISSING);
-                if ($gradeitem) {
-                    $gradeitem->iteminstance = $oldassignment->id;
-                    $gradeitem->itemmodule = 'assignment';
-                    $DB->update_record('grade_items', $gradeitem);
-                }
+                // Reassociate grade_items from the new assign instance to the old assignment instance.
+                $params = array('assignment', $oldassignment->id, 'assign', $newassignment->get_instance()->id);
+                $sql = 'UPDATE {grade_items} SET itemmodule = ?, iteminstance = ? WHERE itemmodule = ? AND iteminstance = ?';
+                $DB->execute($sql, $params);
             }
             // roll back the completion changes
             if ($completiondone) {
index 293c723..97bc738 100644 (file)
@@ -70,6 +70,10 @@ function xmldb_assignment_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2012061701, 'assignment');
     }
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 3198609..132281c 100644 (file)
@@ -1397,6 +1397,7 @@ class assignment_base {
 
         $table->no_sorting('finalgrade');
         $table->no_sorting('outcome');
+        $table->text_sorting('submissioncomment');
 
         // Start working -- this is necessary as soon as the niceties are over
         $table->setup();
index 2be92f6..20cef10 100644 (file)
@@ -190,5 +190,9 @@ function xmldb_book_upgrade($oldversion) {
     }
     // End of MDL-35297 "replayed" steps.
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index c716494..afff2eb 100644 (file)
@@ -33,6 +33,10 @@ function xmldb_chat_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index e46ab79..9b196f4 100644 (file)
@@ -33,6 +33,10 @@ function xmldb_choice_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 745d0cb..ed06ae4 100644 (file)
@@ -33,6 +33,10 @@ function xmldb_data_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 6e36814..2a68d4c 100644 (file)
@@ -75,8 +75,10 @@ if ($rid) {
 
 require_course_login($course, true, $cm);
 
+$context = context_module::instance($cm->id);
+
 /// If it's hidden then it's don't show anything.  :)
-if (empty($cm->visible) and !has_capability('moodle/course:viewhiddenactivities',context_module::instance($cm->id))) {
+if (empty($cm->visible) and !has_capability('moodle/course:viewhiddenactivities', $context)) {
     $PAGE->set_title($data->name);
     echo $OUTPUT->header();
     notice(get_string("activityiscurrentlyhidden"));
index 992b8cf..332fbbe 100644 (file)
@@ -762,7 +762,7 @@ if ($showactivity) {
         $records = array();
     }
 
-    if ($mode == '' && !empty($CFG->enableportfolios)) {
+    if ($mode == '' && !empty($CFG->enableportfolios) && !empty($records)) {
         require_once($CFG->libdir . '/portfoliolib.php');
         $button = new portfolio_add_button();
         $button->set_callback_options('data_portfolio_caller', array('id' => $cm->id), 'mod_data');
index fd0d9a5..37ddbcd 100644 (file)
@@ -47,6 +47,10 @@ function xmldb_feedback_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 8dd1bf7..a1c8196 100644 (file)
@@ -29,7 +29,7 @@ class feedback_captcha_form extends feedback_item_form {
         $mform =& $this->_form;
 
         $mform->addElement('header', 'general', get_string($this->type, 'feedback'));
-        $mform->addElement('checkbox', 'required', get_string('required', 'feedback'));
+        $mform->addElement('advcheckbox', 'required', get_string('required', 'feedback'), '' , null , array(0, 1));
         $mform->addElement('text',
                             'name',
                             get_string('item_name', 'feedback'),
index 25074fd..6dd192d 100644 (file)
@@ -29,7 +29,7 @@ class feedback_multichoice_form extends feedback_item_form {
 
         $mform->addElement('header', 'general', get_string($this->type, 'feedback'));
 
-        $mform->addElement('checkbox', 'required', get_string('required', 'feedback'));
+        $mform->addElement('advcheckbox', 'required', get_string('required', 'feedback'), '' , null , array(0, 1));
 
         $mform->addElement('text',
                             'name',
index e95fb44..0c1632a 100644 (file)
@@ -29,7 +29,7 @@ class feedback_multichoicerated_form extends feedback_item_form {
 
         $mform->addElement('header', 'general', get_string($this->type, 'feedback'));
 
-        $mform->addElement('checkbox', 'required', get_string('required', 'feedback'));
+        $mform->addElement('advcheckbox', 'required', get_string('required', 'feedback'), '' , null , array(0, 1));
 
         $mform->addElement('text',
                             'name',
index 1252870..5abbea0 100644 (file)
@@ -29,7 +29,7 @@ class feedback_numeric_form extends feedback_item_form {
         $mform =& $this->_form;
 
         $mform->addElement('header', 'general', get_string($this->type, 'feedback'));
-        $mform->addElement('checkbox', 'required', get_string('required', 'feedback'));
+        $mform->addElement('advcheckbox', 'required', get_string('required', 'feedback'), '' , null , array(0, 1));
 
         $mform->addElement('text',
                             'name',
index d498241..0a777e7 100644 (file)
@@ -28,7 +28,7 @@ class feedback_textarea_form extends feedback_item_form {
         $mform =& $this->_form;
 
         $mform->addElement('header', 'general', get_string($this->type, 'feedback'));
-        $mform->addElement('checkbox', 'required', get_string('required', 'feedback'));
+        $mform->addElement('advcheckbox', 'required', get_string('required', 'feedback'), '' , null , array(0, 1));
 
         $mform->addElement('text',
                             'name',
index 59c25ce..9361dff 100644 (file)
@@ -28,7 +28,7 @@ class feedback_textfield_form extends feedback_item_form {
         $mform =& $this->_form;
 
         $mform->addElement('header', 'general', get_string($this->type, 'feedback'));
-        $mform->addElement('checkbox', 'required', get_string('required', 'feedback'));
+        $mform->addElement('advcheckbox', 'required', get_string('required', 'feedback'), '' , null , array(0, 1));
 
         $mform->addElement('text',
                             'name',
index d996b61..c7b3e4b 100644 (file)
@@ -59,5 +59,9 @@ function xmldb_folder_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 4c2f297..9d82829 100644 (file)
@@ -54,6 +54,10 @@ function xmldb_forum_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 48e85b8..8fb41bd 100644 (file)
@@ -48,6 +48,10 @@ function xmldb_glossary_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index e4c4b0a..a12da34 100644 (file)
@@ -39,5 +39,9 @@ function xmldb_imscp_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index a083759..ccaad7b 100644 (file)
@@ -59,6 +59,10 @@ function xmldb_label_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 0cc8e03..b95a687 100644 (file)
@@ -65,6 +65,10 @@ function xmldb_lesson_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 9bbf4f5..5ff51bf 100644 (file)
@@ -366,14 +366,6 @@ class qformat_default {
                     $newpage->contents = $question->questiontext;
                     $newpage->contentsformat = isset($question->questionformat) ? $question->questionformat : FORMAT_HTML;
 
-                    // Sometimes, questiontext is not a simple text, but one array
-                    // containing both text and format, so we need to support here
-                    // that case with the following dirty patch. MDL-35147
-                    if (is_array($question->questiontext)) {
-                        $newpage->contents = isset($question->questiontext['text']) ? $question->questiontext['text'] : '';
-                        $newpage->contentsformat = isset($question->questiontext['format']) ? $question->questiontext['format'] : FORMAT_HTML;
-                    }
-
                     // set up page links
                     if ($pageid) {
                         // the new page follows on from this page
index 6e70243..c4dc634 100644 (file)
@@ -338,6 +338,7 @@ if ($pageid != LESSON_EOL) {
         if (lesson_display_teacher_warning($lesson)) {
             // This is the warning msg for teachers to inform them that cluster
             // and unseen does not work while logged in as a teacher
+            $warningvars = new stdClass();
             $warningvars->cluster = get_string('clusterjump', 'lesson');
             $warningvars->unseen = get_string('unseenpageinbranch', 'lesson');
             $lesson->add_message(get_string('teacherjumpwarning', 'lesson', $warningvars));
index b728116..2c0b038 100644 (file)
@@ -70,6 +70,7 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
             'timemodified',
             'typeid',
             'toolurl',
+            'securetoolurl',
             'preferheight',
             'launchcontainer',
             'instructorchoicesendname',
@@ -79,8 +80,11 @@ class backup_lti_activity_structure_step extends backup_activity_structure_step
             'instructorchoiceallowsetting',
             'grade',
             'instructorcustomparameters',
+            'debuglaunch',
             'showtitlelaunch',
-            'showdescriptionlaunch'
+            'showdescriptionlaunch',
+            'icon',
+            'secureicon',
             )
         );
 
index 9fd4e13..7bec031 100644 (file)
@@ -72,6 +72,10 @@ function xmldb_lti_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index b1c4e91..74d1888 100644 (file)
@@ -59,5 +59,9 @@ function xmldb_page_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index a8d94ff..df17c03 100644 (file)
@@ -2,11 +2,9 @@ This files describes API changes for quiz access rule plugins.
 
 Overview of this plugin type at http://docs.moodle.org/dev/Quiz_access_rules
 
+=== 2.4 ===
 
-=== 2.2 ===
-
-* This plugin type was new in Moodle 2.2!
-
+* Replaced time_left() with new time_left_display() and end_time() functions.
 
 === 2.3 ===
 
@@ -14,6 +12,8 @@ Overview of this plugin type at http://docs.moodle.org/dev/Quiz_access_rules
   lib.php file containing
 function quizaccess_mypluginname_cron() {};
 
-=== 2.4 ===
+=== 2.2 ===
+
+* This plugin type was new in Moodle 2.2!
+
 
-* Replaced time_left() with new time_left_display() and end_time() functions.
\ No newline at end of file
index b8bb709..604eb12 100644 (file)
@@ -392,6 +392,10 @@ function xmldb_quiz_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2012100801, 'quiz');
     }
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index ea82013..8bacae7 100644 (file)
@@ -1624,6 +1624,9 @@ function quiz_groups_group_deleted_handler($event) {
              WHERE quiz.course = :courseid AND grp.id IS NULL";
     $params = array('courseid'=>$event->courseid);
     $records = $DB->get_records_sql_menu($sql, $params);
+    if (!$records) {
+        return; // Nothing to do.
+    }
     $DB->delete_records_list('quiz_overrides', 'id', array_keys($records));
     quiz_update_open_attempts(array('quizid'=>array_unique(array_values($records))));
 }
index 1f3270e..3795776 100644 (file)
@@ -272,16 +272,8 @@ abstract class quiz_attempts_report_table extends table_sql {
     protected function icon_for_fraction($fraction) {
         global $OUTPUT;
 
-        $state = question_state::graded_state_for_fraction($fraction);
-        if ($state == question_state::$gradedright) {
-            $icon = 'i/grade_correct';
-        } else if ($state == question_state::$gradedpartial) {
-            $icon = 'i/grade_partiallycorrect';
-        } else {
-            $icon = 'i/grade_incorrect';
-        }
-
-        return $OUTPUT->pix_icon($icon, get_string($state->get_feedback_class(), 'question'),
+        $feedbackclass = question_state::graded_state_for_fraction($fraction)->get_feedback_class();
+        return $OUTPUT->pix_icon('i/grade_' . $feedbackclass, get_string($feedbackclass, 'question'),
                 'moodle', array('class' => 'icon'));
     }
 
index f8aa7c8..27edbb6 100644 (file)
@@ -42,5 +42,9 @@ function xmldb_quiz_overview_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 144d95a..1a671dd 100644 (file)
@@ -67,6 +67,10 @@ function xmldb_quiz_statistics_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 822e307..fd7bb08 100644 (file)
@@ -36,9 +36,13 @@ $attemptobj = quiz_attempt::create($attemptid);
 // Check login.
 require_login($attemptobj->get_course(), false, $attemptobj->get_cm());
 
-// If this is not our own attempt, display an error.
+// Check that this attempt belongs to this user.
 if ($attemptobj->get_userid() != $USER->id) {
-    print_error('notyourattempt', 'quiz', $attemptobj->view_url());
+    if ($attemptobj->has_capability('mod/quiz:viewreports')) {
+        redirect($attemptobj->review_url(null, $page));
+    } else {
+        throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'notyourattempt');
+    }
 }
 
 // Check capabilites.
index 383e888..f2f43db 100644 (file)
@@ -59,5 +59,9 @@ function xmldb_resource_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index fc90630..faac4e1 100644 (file)
@@ -76,6 +76,10 @@ function xmldb_scorm_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index c641e1f..f9c2b5c 100644 (file)
@@ -278,8 +278,6 @@ function scorm_delete_instance($id) {
             }
         }
         $DB->delete_records('scorm_scoes', array('scorm'=>$scorm->id));
-    } else {
-        $result = false;
     }
     if (! $DB->delete_records('scorm', array('id'=>$scorm->id))) {
         $result = false;
index 10c6ef8..d08c42a 100644 (file)
@@ -33,6 +33,10 @@ function xmldb_survey_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
 
index 5449ecb..1e467ca 100644 (file)
@@ -59,5 +59,9 @@ function xmldb_url_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index b528958..0569fa2 100644 (file)
@@ -70,5 +70,9 @@ function xmldb_wiki_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 2616c4f..507c4c7 100644 (file)
@@ -656,7 +656,7 @@ class page_wiki_comments extends page_wiki {
                     $parsedcontent = wiki_parse_content('nwiki', $comment->content, $options);
                 }
 
-                $cell4->text = format_text(html_entity_decode($parsedcontent['parsed_text']), FORMAT_HTML);
+                $cell4->text = format_text(html_entity_decode($parsedcontent['parsed_text'], ENT_QUOTES, 'UTF-8'), FORMAT_HTML);
             } else {
                 $cell4->text = format_text($comment->content, FORMAT_HTML);
             }
index eb14bc0..f777f28 100644 (file)
@@ -14,9 +14,9 @@ require_once($CFG->dirroot . "/lib/outputcomponents.php");
 class parser_utils {
         
     public static function h($tag, $text = null, $options = array(), $escape_text = false) {
-        $tag = htmlentities($tag);
+        $tag = htmlentities($tag, ENT_COMPAT, 'UTF-8');
         if(!empty($text) && $escape_text) {
-                $text = htmlentities($text);
+                $text = htmlentities($text, ENT_COMPAT, 'UTF-8');
             }
         return html_writer::tag($tag, $text, $options);
     }
index d1446a3..ba21f49 100644 (file)
@@ -109,5 +109,9 @@ function xmldb_workshop_upgrade($oldversion) {
     }
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index fd8065d..0cd2eb1 100644 (file)
@@ -42,5 +42,9 @@ function xmldb_workshopform_accumulative_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index a369ae9..2b2269f 100644 (file)
@@ -42,5 +42,9 @@ function xmldb_workshopform_comments_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 459abbb..44dd927 100644 (file)
@@ -42,5 +42,9 @@ function xmldb_workshopform_numerrors_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index 968f8e5..c2de8ae 100644 (file)
@@ -42,5 +42,9 @@ function xmldb_workshopform_rubric_upgrade($oldversion) {
     // Put any upgrade step following this
 
 
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this
+
+
     return true;
 }
index b9d93c6..f927ac2 100644 (file)
@@ -1046,7 +1046,7 @@ function workshop_get_extra_capabilities() {
  * Needed by grade_update_mod_grades() in lib/gradelib.php. Also used by
  * {@link workshop_update_grades()}.
  *
- * @param stdClass $workshop instance object with extra cmidnumber and modname property
+ * @param stdClass $workshop instance object with extra cmidnumber property
  * @param stdClass $submissiongrades data for the first grade item
  * @param stdClass $assessmentgrades data for the second grade item
  * @return void
diff --git a/pix/c/course.png b/pix/c/course.png
new file mode 100644 (file)
index 0000000..4f86f2c
Binary files /dev/null and b/pix/c/course.png differ
diff --git a/pix/c/course.svg b/pix/c/course.svg
new file mode 100644 (file)
index 0000000..e60475f
--- /dev/null
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="-0.2 0 16 16" style="overflow:visible;enable-background:new -0.2 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M6,0.4c0.4-0.1,0.8-0.1,1.3-0.1c1.5,0,3,0.5,4.2,1.4c-0.6,0.2-1.2,0.5-1.7,1C9.7,2.7,9.6,2.8,9.6,2.8\r
+       C8.9,2.5,8.1,2.2,7.2,2.2c-0.1,0-0.2,0-0.3,0C6.8,1.6,6.5,0.9,6,0.4z M11.7,9.4c-0.2,0.5-0.5,1-1,1.4c-0.3,0.3-0.8,0.6-1.2,0.8\r
+       c0.2,0.7,0.3,1.4,0.1,2.2c0.9-0.3,1.8-0.9,2.5-1.6c0.9-0.9,1.4-1.9,1.8-3C13.2,9.5,12.5,9.5,11.7,9.4z M3,10.1C3,10,3.1,10,3,10.1\r
+       C2.5,9.1,2.2,8,2.3,6.9C1.6,6.8,0.9,6.5,0.4,6C0,8,0.5,10.2,2,11.9C2.2,11.2,2.5,10.6,3,10.1z M10.4,3.4c-1.2,1.2-1.2,3.1,0,4.2\r
+       c1.2,1.2,3.1,1.2,4.2,0s1.2-3.1,0-4.2C13.5,2.2,11.6,2.2,10.4,3.4z M3.7,10.8c-1.2,1.2-1.2,3.1,0,4.2c1.2,1.2,3.1,1.2,4.2,0\r
+       c1.2-1.2,1.2-3.1,0-4.2C6.8,9.6,4.9,9.6,3.7,10.8z M0.9,0.9c-1.2,1.2-1.2,3.1,0,4.2C2,6.3,4,6.3,5.1,5.1c1.2-1.2,1.2-3.1,0-4.2\r
+       C4-0.3,2-0.3,0.9,0.9z"/>\r
+</svg>\r
diff --git a/pix/c/group.png b/pix/c/group.png
new file mode 100644 (file)
index 0000000..393371b
Binary files /dev/null and b/pix/c/group.png differ
diff --git a/pix/c/group.svg b/pix/c/group.svg
new file mode 100644 (file)
index 0000000..7107000
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M3,16H0v-4l3.2-1.7C3.6,10,3.8,9.5,3.4,9C3.4,9,2,7.2,2,5.2C2,2.3,3.8,0,6,0c0.4,0,0.7,0.1,1.1,0.2\r
+       c-1.2,1.1-2,3-2,5c0,1.7,0.6,2.7,1,3.5c0.6,0.9,0.1,1-0.3,1.3C5.6,10.1,3,11.4,3,11.4V16z M16,11.9l-3.1-1.6\r
+       C12.4,10,12.3,9.4,12.6,9c0,0,1.4-1.8,1.4-3.8C14,2.3,12.2,0,10,0S6,2.3,6,5.2C6,7.2,7.5,9,7.5,9c0.3,0.4,0.2,1-0.3,1.3L4,12v4h12\r
+       V11.9z"/>\r
+</svg>\r
diff --git a/pix/c/site.png b/pix/c/site.png
new file mode 100644 (file)
index 0000000..02a18b5
Binary files /dev/null and b/pix/c/site.png differ
diff --git a/pix/c/site.svg b/pix/c/site.svg
new file mode 100644 (file)
index 0000000..23f6975
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M14,1h-2v1.5C12,3.3,11.3,4,10.5,4S9,3.3,9,2.5V1H6v1.5C6,3.3,5.3,4,4.5,4S3,3.3,3,2.5V1H1\r
+       C0.4,1,0,1.4,0,2v13c0,0.6,0.4,1,1,1h13c0.6,0,1-0.4,1-1V2C15,1.4,14.6,1,14,1z M4,14H2v-2h2V14z M4,11H2V9h2V11z M4,8H2V6h2V8z\r
+        M7,14H5v-2h2V14z M7,11H5V9h2V11z M7,8H5V6h2V8z M10,14H8v-2h2V14z M10,11H8V9h2V11z M10,8H8V6h2V8z M13,14h-2v-2h2V14z M13,11h-2\r
+       V9h2V11z M13,8h-2V6h2V8z M5,2.5C5,2.8,4.8,3,4.5,3l0,0C4.2,3,4,2.8,4,2.5v-2C4,0.2,4.2,0,4.5,0l0,0C4.8,0,5,0.2,5,0.5V2.5z M11,2.5\r
+       C11,2.8,10.8,3,10.5,3l0,0C10.2,3,10,2.8,10,2.5v-2C10,0.2,10.2,0,10.5,0l0,0C10.8,0,11,0.2,11,0.5V2.5z"/>\r
+</svg>\r
diff --git a/pix/c/user.png b/pix/c/user.png
new file mode 100644 (file)
index 0000000..f803c86
Binary files /dev/null and b/pix/c/user.png differ
diff --git a/pix/c/user.svg b/pix/c/user.svg
new file mode 100644 (file)
index 0000000..9fc0081
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M5.2,10.3L0,13.1V16h16v-3l-5.2-2.7C10.3,10,10.2,9.4,10.6,9c0,0,1.4-1.8,1.4-3.8C12,2.3,10.2,0,8,0\r
+       S4,2.3,4,5.2C4,7.2,5.4,9,5.4,9C5.8,9.5,5.7,10,5.2,10.3z"/>\r
+</svg>\r
diff --git a/pix/i/approve.png b/pix/i/approve.png
new file mode 100644 (file)
index 0000000..43540a5
Binary files /dev/null and b/pix/i/approve.png differ
diff --git a/pix/i/approve.svg b/pix/i/approve.svg
new file mode 100644 (file)
index 0000000..5c768d1
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="12px" height="12px" viewBox="0 0 12 12" style="overflow:visible;enable-background:new 0 0 12 12;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M11.4,0.6l-0.9-0.5C10.1-0.1,9.5,0,9.2,0.5L4.7,8.2L2,6.6C1.5,6.3,0.9,6.5,0.6,7L0.1,7.8\r
+       C-0.1,8.3,0,8.9,0.5,9.2L5,11.8c0.1,0.1,0.3,0.1,0.4,0.1c0.4,0.1,0.8-0.1,1-0.5L11.8,2C12.1,1.5,11.9,0.9,11.4,0.6z"/>\r
+</svg>\r
diff --git a/pix/i/calendar.png b/pix/i/calendar.png
new file mode 100644 (file)
index 0000000..02a18b5
Binary files /dev/null and b/pix/i/calendar.png differ
diff --git a/pix/i/calendar.svg b/pix/i/calendar.svg
new file mode 100644 (file)
index 0000000..23f6975
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M14,1h-2v1.5C12,3.3,11.3,4,10.5,4S9,3.3,9,2.5V1H6v1.5C6,3.3,5.3,4,4.5,4S3,3.3,3,2.5V1H1\r
+       C0.4,1,0,1.4,0,2v13c0,0.6,0.4,1,1,1h13c0.6,0,1-0.4,1-1V2C15,1.4,14.6,1,14,1z M4,14H2v-2h2V14z M4,11H2V9h2V11z M4,8H2V6h2V8z\r
+        M7,14H5v-2h2V14z M7,11H5V9h2V11z M7,8H5V6h2V8z M10,14H8v-2h2V14z M10,11H8V9h2V11z M10,8H8V6h2V8z M13,14h-2v-2h2V14z M13,11h-2\r
+       V9h2V11z M13,8h-2V6h2V8z M5,2.5C5,2.8,4.8,3,4.5,3l0,0C4.2,3,4,2.8,4,2.5v-2C4,0.2,4.2,0,4.5,0l0,0C4.8,0,5,0.2,5,0.5V2.5z M11,2.5\r
+       C11,2.8,10.8,3,10.5,3l0,0C10.2,3,10,2.8,10,2.5v-2C10,0.2,10.2,0,10.5,0l0,0C10.8,0,11,0.2,11,0.5V2.5z"/>\r
+</svg>\r
diff --git a/pix/i/configlock.png b/pix/i/configlock.png
new file mode 100644 (file)
index 0000000..5d2ee7c
Binary files /dev/null and b/pix/i/configlock.png differ
diff --git a/pix/i/configlock.svg b/pix/i/configlock.svg
new file mode 100644 (file)
index 0000000..05600a9
--- /dev/null
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="0 0 16 16" style="overflow:visible;enable-background:new 0 0 16 16;"\r
+        xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#999999;" d="M6,13.7c0-0.1-0.6-0.2-0.9-0.4l-1.8,1.2C2.6,14,2,13.4,1.5,12.6l1.2-1.8c-0.2-0.4-0.4-0.8-0.5-1.2\r
+       L0.1,9.3C0,8.9,0,8.4,0,8c0-0.4,0-0.9,0.1-1.3l2.1-0.4c0.1-0.4,0.3-0.8,0.5-1.2L1.5,3.4C2,2.6,2.6,2,3.4,1.5l1.8,1.2\r
+       c0.4-0.2,0.8-0.4,1.2-0.5l0.4-2.1C7.1,0,7.6,0,8,0s0.9,0,1.3,0.1l0.4,2.1c0.4,0.1,0.8,0.3,1.2,0.5l1.8-1.2C13.4,2,14,2.6,14.5,3.4\r
+       l-1.2,1.8c0.2,0.4,0.4,0.8,0.5,1.2l2.1,0.4C16,7.1,16,7.6,16,8s0,0.9-0.1,1.3l-0.4,0.1c-0.1-0.8-0.5-1.6-1.1-2.2\r
+       C13.6,6.4,12.6,6,11.6,6c-0.8,0-1.5,0.2-2.1,0.6C9.1,6.2,8.6,6,8,6C6.9,6,6.1,6.9,6.1,8c0,1,0.6,1.7,1.5,1.9c0,0,0,0.2,0,0.2\r
+       c-0.4,0.1-0.7,0.3-1,0.5C6.2,11,6,11.5,6,12V13.7z M14.5,10v1H15c0.5,0,1,0.5,1,1v3c0,0.5-0.5,1-1,1l-7,0c-0.5,0-1-0.5-1-1v-3\r
+       c0-0.5,0.4-1,1-1h0.6v-1c0-1.6,1.3-3,2.9-3C13.2,7,14.5,8.4,14.5,10z M11.5,12c-0.5,0-1,0.4-1,1c0,0.4,0.2,0.7,0.6,0.9L11,14l-0.5,1\r
+       l1,0l1,0L12,14L12,13.9c0.3-0.2,0.6-0.5,0.6-0.9C12.5,12.5,12.1,12,11.5,12z M11.5,9c-0.5,0-1,0.4-1,1v1h1.9v-1\r
+       C12.5,9.5,12.1,9,11.5,9z"/>\r
+</svg>\r
diff --git a/pix/i/cross_red_big.png b/pix/i/cross_red_big.png
new file mode 100644 (file)
index 0000000..0f5cfe0
Binary files /dev/null and b/pix/i/cross_red_big.png differ
diff --git a/pix/i/cross_red_big.svg b/pix/i/cross_red_big.svg
new file mode 100644 (file)
index 0000000..de6808d
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:a="http://ns.adobe.com/AdobeSVGViewerExtensions/3.0/"\r
+        x="0px" y="0px" width="16px" height="16px" viewBox="-1.6 -0.5 16 16"\r
+        style="overflow:visible;enable-background:new -1.6 -0.5 16 16;" xml:space="preserve" preserveAspectRatio="xMinYMid meet">\r
+<defs>\r
+</defs>\r
+<path style="fill:#FF403C;" d="M12.8,2.7L10.1,0c0,0-1.6,1.5-3.7,4C4.3,1.5,2.7,0,2.7,0L0,2.7c0,0,1.9,1.3,4.6,3.7\r
+       C3,8.7,1.3,11.6,0,14.9C2.2,12.2,4.4,9.9,6.4,8c2,1.9,4.2,4.2,6.4,6.9c-1.3-3.3-3-6.2-4.6-8.6C10.9,4,12.8,2.7,12.8,2.7z"/>\r
+</svg>\r
diff --git a/pix/i/cross_red_small.png b/pix/i/cross_red_small.png
new file mode 100644 (file)
index 0000000..0f5cfe0
Binary files /dev/null and b/pix/i/cross_red_small.png differ
diff --git a/pix/i/cross_red_small.svg b/pix/i/cross_red_small.svg
new file mode 100644 (file)
index 0000000..de6808d
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>\r
+<!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In  -->\r
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [\r
+       <!ENTITY ns_flows "http://ns.adobe.com/Flows/1.0/">\r
+]>\r
+<svg version="1.1"\r
+        xmlns="http://www.w3.org/2000/svg" xmlns:xlink="htt