Merge branch 'w51_MDL-37301_m23_coursecatinfoas' of git://github.com/skodak/moodle...
authorDan Poltawski <dan@moodle.com>
Tue, 8 Jan 2013 02:27:12 +0000 (10:27 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 8 Jan 2013 02:27:12 +0000 (10:27 +0800)
58 files changed:
admin/tool/uploaduser/index.php
backup/backupfilesedit.php
backup/converter/moodle1/lib.php
backup/converter/moodle1/tests/lib_test.php
backup/moodle2/restore_stepslib.php
blocks/dock.js
blocks/rss_client/backup/moodle2/restore_rss_client_stepslib.php
blocks/tags/edit_form.php
blocks/tags/lang/en/block_tags.php
comment/comment_post.php
course/category.php
course/dnduploadlib.php
course/search.php
course/switchrole.php
enrol/otherusers.php
grade/edit/tree/lib.php
group/externallib.php
lang/en/block.php
lang/en/error.php
lang/en/question.php
lib/editor/tinymce/tiny_mce/3.5.1.1/plugins/spellchecker/changelog.txt
lib/editor/tinymce/tiny_mce/3.5.1.1/plugins/spellchecker/classes/GoogleSpell.php
lib/filebrowser/file_info_context_course.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/rsslib.php
lib/tests/textlib_test.php
lib/textlib.class.php
lib/weblib.php
lib/yui/formautosubmit/formautosubmit.js [new file with mode: 0644]
mnet/lib.php
mod/assign/locallib.php
mod/assign/submission/comments/lib.php
mod/choice/lib.php
mod/feedback/analysis_course.php
mod/forum/lib.php
mod/forum/rsslib.php
mod/forum/subscribe.php
mod/lesson/report.php
mod/quiz/lang/en/quiz.php
mod/quiz/mod_form.php
mod/quiz/settings.php
mod/scorm/report/reportlib.php
mod/wiki/create.php
mod/wiki/filesedit.php
mod/wiki/pagelib.php
question/format/learnwise/format.php
question/format/learnwise/lang/en/qformat_learnwise.php
question/preview.php
question/previewlib.php
question/type/shortanswer/question.php
question/type/shortanswer/tests/question_test.php
report/outline/index.php
tag/coursetags_add.php
theme/mymobile/renderers.php
user/files.php
user/profile.php
version.php

index 0d97168..911d26a 100644 (file)
@@ -285,7 +285,11 @@ if ($formdata = $mform2->is_cancelled()) {
             $userserrors++;
             continue;
         }
-
+        if ($user->username !== clean_param($user->username, PARAM_USERNAME)) {
+            $upt->track('status', get_string('invalidusername', 'error', 'username'), 'error');
+            $upt->track('username', $errorstr, 'error');
+            $userserrors++;
+        }
         if ($existinguser = $DB->get_record('user', array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id))) {
             $upt->track('id', $existinguser->id, 'normal', false);
         }
index 0059bbc..67e72a0 100644 (file)
@@ -33,7 +33,7 @@ $currentcontext = required_param('currentcontext', PARAM_INT);
 // file parameters
 $component  = optional_param('component', null, PARAM_COMPONENT);
 $filearea   = optional_param('filearea', null, PARAM_AREA);
-$returnurl  = optional_param('returnurl', null, PARAM_URL);
+$returnurl  = optional_param('returnurl', null, PARAM_LOCALURL);
 
 list($context, $course, $cm) = get_context_info_array($currentcontext);
 $filecontext = get_context_instance_by_id($contextid);
index 9edda48..3b3fd4a 100644 (file)
@@ -641,7 +641,9 @@ class moodle1_converter extends base_converter {
         }
         foreach ($matches[2] as $match) {
             $file = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
-            $files[] = rawurldecode($file);
+            if ($file === clean_param($file, PARAM_PATH)) {
+                $files[] = rawurldecode($file);
+            }
         }
 
         return array_unique($files);
@@ -1209,6 +1211,10 @@ class moodle1_file_manager implements loggable {
 
         $sourcefullpath = $this->basepath.'/'.$sourcepath;
 
+        if ($sourcefullpath !== clean_param($sourcefullpath, PARAM_PATH)) {
+            throw new moodle1_convert_exception('file_invalid_path', $sourcefullpath);
+        }
+
         if (!is_readable($sourcefullpath)) {
             throw new moodle1_convert_exception('file_not_readable', $sourcefullpath);
         }
index bf4935c..154b6c8 100644 (file)
@@ -273,6 +273,15 @@ class moodle1_converter_testcase extends advanced_testcase {
         $fileids = $fileman->get_fileids();
         $this->assertEquals(gettype($fileids), 'array');
         $this->assertEquals(0, count($fileids));
+        // try to migrate an invalid file
+        $fileman->itemid = 1;
+        $thrown = false;
+        try {
+            $fileman->migrate_file('/../../../../../../../../../../../../../../etc/passwd');
+        } catch (moodle1_convert_exception $e) {
+            $thrown = true;
+        }
+        $this->assertTrue($thrown);
         // migrate a single file
         $fileman->itemid = 4;
         $fileman->migrate_file('moddata/unittest/4/icon.gif');
@@ -435,6 +444,8 @@ class moodle1_converter_testcase extends advanced_testcase {
 
         $text = 'This is a text containing links to file.php
 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif" /><a href="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif$@FORCEDOWNLOAD@$">download image</a><br />
+    <div><a href=\'$@FILEPHP@$/../../../../../../../../../../../../../../../etc/passwd\'>download passwords</a></div>
+    <div><a href=\'$@FILEPHP@$$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$etc$@SLASH@$shadow\'>download shadows</a></div>
     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />';
 
         $files = moodle1_converter::find_referenced_files($text);
@@ -446,6 +457,8 @@ as it is parsed from the backup file. <br /><br /><img border="0" width="110" vs
         $text = moodle1_converter::rewrite_filephp_usage($text, array('/pics/news.gif', '/another/file/notused.txt'));
         $this->assertEquals($text, 'This is a text containing links to file.php
 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="@@PLUGINFILE@@/pics/news.gif" /><a href="@@PLUGINFILE@@/pics/news.gif?forcedownload=1">download image</a><br />
+    <div><a href=\'$@FILEPHP@$/../../../../../../../../../../../../../../../etc/passwd\'>download passwords</a></div>
+    <div><a href=\'$@FILEPHP@$$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$etc$@SLASH@$shadow\'>download shadows</a></div>
     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />');
     }
 
index 3247a82..740c202 100644 (file)
@@ -297,8 +297,13 @@ class restore_gradebook_structure_step extends restore_structure_step {
 
         $data->courseid = $this->get_courseid();
 
-        $newitemid = $DB->insert_record('grade_settings', $data);
-        //$this->set_mapping('grade_setting', $oldid, $newitemid);
+        if (!$DB->record_exists('grade_settings', array('courseid' => $data->courseid, 'name' => $data->name))) {
+            $newitemid = $DB->insert_record('grade_settings', $data);
+        } else {
+            $newitemid = $data->id;
+        }
+
+        $this->set_mapping('grade_setting', $oldid, $newitemid);
     }
 
     /**
index a759913..6f63816 100644 (file)
@@ -916,7 +916,7 @@ M.core_dock.genericblock.prototype = {
             }, this);
             // Add a close icon
             // Must set the image src seperatly of we get an error with XML strict headers
-            var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="" style="width:11px;height:11px;cursor:pointer;" /></span>');
+            var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="'+M.str.block.hidepanel+'" title="'+M.str.block.hidedockpanel+'" style="width:11px;height:11px;cursor:pointer;"/></span>');
             closeicon.one('img').setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
             closeicon.on('forceclose|click', this.hide, this);
             closeicon.on('dock:actionkey',this.hide, this, {actions:{enter:true,toggle:true}});
index 8e28ac2..c23d7b9 100644 (file)
@@ -78,6 +78,9 @@ class restore_rss_client_block_structure_step extends restore_structure_step {
         $configdata = $DB->get_field('block_instances', 'configdata', array('id' => $this->task->get_blockid()));
         // Extract configdata
         $config = unserialize(base64_decode($configdata));
+        if (empty($config)) {
+            $config = new stdClass();
+        }
         // Set array of used rss feeds
         $config->rssid = $feedsarr;
         // Serialize back the configdata
index eec3202..c1b4d5d 100644 (file)
@@ -34,7 +34,7 @@ class block_tags_edit_form extends block_edit_form {
         // Fields for editing HTML block title and contents.
         $mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
 
-        $mform->addElement('text', 'config_title', get_string('pluginname', 'block_tags'));
+        $mform->addElement('text', 'config_title', get_string('configtitle', 'block_tags'));
         $mform->setType('config_title', PARAM_MULTILANG);
         $mform->setDefault('config_title', get_string('pluginname', 'block_tags'));
 
index 265cd5a..4998f68 100644 (file)
@@ -31,6 +31,7 @@ $string['arrowtitle'] = 'Click here to enter the suggested text (grey letters).'
 $string['communitytags'] = 'Community tags:';
 $string['communitytags1'] = 'community tags';
 $string['communitytags2'] = 'Show all user created course tags';
+$string['configtitle'] = 'Block title';
 $string['coursetags'] = 'Course tags:';
 $string['coursetags1'] = 'course tags';
 $string['coursetags2'] = 'Show tags for this course';
index b7a39a8..a960293 100644 (file)
@@ -38,7 +38,7 @@ $action    = optional_param('action',    '',  PARAM_ALPHA);
 $area      = optional_param('area',      '',  PARAM_AREA);
 $content   = optional_param('content',   '',  PARAM_RAW);
 $itemid    = optional_param('itemid',    '',  PARAM_INT);
-$returnurl = optional_param('returnurl', '/', PARAM_URL);
+$returnurl = optional_param('returnurl', '/', PARAM_LOCALURL);
 $component = optional_param('component', '',  PARAM_COMPONENT);
 
 // Currently this script can only add comments
index cb1d0c7..7751058 100644 (file)
@@ -191,6 +191,7 @@ if ($editingon && can_edit_in_category()) {
     // Integrate into the admin tree only if the user can edit categories at the top level,
     // otherwise the admin block does not appear to this user, and you get an error.
     require_once($CFG->libdir . '/adminlib.php');
+    navigation_node::override_active_url(new moodle_url('/course/category.php', array('id' => $id)));
     admin_externalpage_setup('coursemgmt', '', $urlparams, $CFG->wwwroot . '/course/category.php');
     $PAGE->set_context($context);   // Ensure that we are actually showing blocks etc for the cat context
 
@@ -434,8 +435,11 @@ if (!$courses) {
         $movetocategories[$category->id] = get_string('moveselectedcoursesto');
         echo '<tr><td colspan="3" align="right">';
         echo html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
-        echo html_writer::select($movetocategories, 'moveto', $category->id, null, array('id'=>'movetoid'));
-        $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('movecourses', 'movetoid', false));
+        echo html_writer::select($movetocategories, 'moveto', $category->id, null, array('id'=>'movetoid', 'class' => 'autosubmit'));
+        $PAGE->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => 'movetoid', 'nothing' => $category->id))
+        );
         echo '<input type="hidden" name="id" value="'.$category->id.'" />';
         echo '</td></tr>';
     }
index 038c09b..2aa6b02 100644 (file)
@@ -555,6 +555,15 @@ class dndupload_ajax_processor {
         $this->cm->groupmode = $this->course->groupmode;
         $this->cm->groupingid = $this->course->defaultgroupingid;
 
+        // Set the correct default for completion tracking.
+        $this->cm->completion = COMPLETION_TRACKING_NONE;
+        $completion = new completion_info($this->course);
+        if ($completion->is_enabled()) {
+            if (plugin_supports('mod', $this->cm->modulename, FEATURE_MODEDIT_DEFAULT_COMPLETION, true)) {
+                $this->cm->completion = COMPLETION_TRACKING_MANUAL;
+            }
+        }
+
         if (!$this->cm->id = add_course_module($this->cm)) {
             throw new coding_exception("Unable to create the course module");
         }
index 1dda021..0022d5a 100644 (file)
@@ -375,8 +375,11 @@ if ($courses) {
         echo "<input type=\"button\" onclick=\"checknone()\" value=\"$strdeselectall\" />\n";
         // Select box should only show categories in which user has min capability to move course.
         echo html_writer::label(get_string('moveselectedcoursesto'), 'movetoid', false, array('class' => 'accesshide'));
-        echo html_writer::select($usercatlist, 'moveto', '', array(''=>get_string('moveselectedcoursesto')), array('id'=>'movetoid'));
-        $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('movecourses', 'movetoid', false));
+        echo html_writer::select($usercatlist, 'moveto', '', array(''=>get_string('moveselectedcoursesto')), array('id'=>'movetoid', 'class' => 'autosubmit'));
+        $PAGE->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => 'movetoid', 'nothing' => false))
+        );
         echo "</td>\n</tr>\n";
         echo "</table>\n</form>";
 
index 12cba20..dc387fb 100644 (file)
@@ -35,7 +35,7 @@ require_once($CFG->dirroot.'/course/lib.php');
 
 $id         = required_param('id', PARAM_INT);
 $switchrole = optional_param('switchrole',-1, PARAM_INT);
-$returnurl  = optional_param('returnurl', false, PARAM_URL);
+$returnurl  = optional_param('returnurl', false, PARAM_LOCALURL);
 
 $PAGE->set_url('/course/switchrole.php', array('id'=>$id));
 
@@ -86,4 +86,4 @@ if ($returnurl === false) {
     $returnurl = new moodle_url('/course/view.php', array('id' => $course->id));
 }
 
-redirect($returnurl);
\ No newline at end of file
+redirect($returnurl);
index 52b34bf..2665b2f 100644 (file)
@@ -47,6 +47,7 @@ $PAGE->set_pagelayout('admin');
 $manager = new course_enrolment_manager($PAGE, $course, $filter);
 $table = new course_enrolment_other_users_table($manager, $PAGE);
 $PAGE->set_url('/enrol/otherusers.php', $manager->get_url_params()+$table->get_url_params());
+navigation_node::override_active_url(new moodle_url('/enrol/otherusers.php', array('id' => $id)));
 
 $userdetails = array (
     'picture' => false,
index 2bc2dc7..df0638c 100644 (file)
@@ -273,12 +273,6 @@ class grade_edit_tree {
                 $root = true;
             }
 
-            $row_count_offset = 0;
-
-            if (empty($category_total_item) && !$this->moving) {
-                $row_count_offset = -1;
-            }
-
             $levelclass = "level$level";
 
             $courseclass = '';
@@ -297,7 +291,7 @@ class grade_edit_tree {
             $headercell->scope = 'row';
             $headercell->attributes['title'] = $object->stripped_name;
             $headercell->attributes['class'] = 'cell rowspan ' . $levelclass;
-            $headercell->rowspan = $row_count+1+$row_count_offset;
+            $headercell->rowspan = $row_count + 1;
             $row->cells[] = $headercell;
 
             foreach ($this->columns as $column) {
index 39db087..c12d5a5 100644 (file)
@@ -576,7 +576,7 @@ class core_group_external extends external_api {
                             'courseid' => new external_value(PARAM_INT, 'id of course'),
                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
-                            'descriptionformat' => new external_format_value('descripiton', VALUE_DEFAULT)
+                            'descriptionformat' => new external_format_value('description', VALUE_DEFAULT)
                         )
                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
                 )
index a685f0e..d662beb 100644 (file)
@@ -39,6 +39,8 @@ $string['defaultweight'] = 'Default weight';
 $string['defaultweight_help'] = 'The default weight allows you to choose roughly where you want the block to appear in the chosen region, either at the top or the bottom. The final location is calculated from all the blocks in that region (for example, only one block can actually be at the top). This value can be overridden on specific pages if required.';
 $string['deletecheck'] = 'Delete {$a} block?';
 $string['deleteblockcheck'] = 'Are you sure that you want to delete this block titled {$a}?';
+$string['hidedockpanel'] = 'Hide the dock panel';
+$string['hidepanel'] = 'Hide panel';
 $string['moveblockhere'] = 'Move block here';
 $string['movingthisblockcancel'] = 'Moving this block ({$a})';
 $string['onthispage'] = 'On this page';
index 9640c74..06fe926 100644 (file)
@@ -329,6 +329,7 @@ $string['invalidurl'] = 'Invalid URL';
 $string['invaliduser'] = 'Invalid user';
 $string['invaliduserid'] = 'Invalid user id';
 $string['invaliduserfield'] = 'Invalid user field: {$a}';
+$string['invalidusername'] = 'The given username contains invalid characters';
 $string['invalidxmlfile'] = '"{$a}" is not a valid XML file';
 $string['iplookupfailed'] = 'Cannot find geo information about this IP address {$a}';
 $string['iplookupprivate'] = 'Cannot display lookup of private IP address';
index dfa1aeb..56d1a07 100644 (file)
@@ -306,6 +306,8 @@ $string['cannotloadquestion'] = 'Could not load question';
 $string['cannotpreview'] = 'You can\'t preview these questions!';
 $string['category'] = 'Category';
 $string['changeoptions'] = 'Change options';
+$string['attemptoptions'] = 'Attempt options';
+$string['displayoptions'] = 'Display options';
 $string['check'] = 'Check';
 $string['clearwrongparts'] = 'Clear incorrect responses';
 $string['closepreview'] = 'Close preview';
@@ -384,6 +386,7 @@ $string['requiresgrading'] = 'Requires grading';
 $string['responsehistory'] = 'Response history';
 $string['restart'] = 'Start again';
 $string['restartwiththeseoptions'] = 'Start again with these options';
+$string['updatedisplayoptions'] = 'Update display options';
 $string['rightanswer'] = 'Right answer';
 $string['rightanswer_help'] = 'an automatically generated summary of the correct response. This can be limited, so you may wish to consider explaining the correct solution in the general feedback for the question, and turning this option off.';
 $string['saved'] = 'Saved: {$a}';
index ed606cf..afe04fb 100644 (file)
@@ -1,3 +1,5 @@
+Version 2.0.6.1 (2012-11-16)\r
+       Fixed security issue with google spellchecker.\r
 Version 2.0.6 (2011-09-29)\r
        Fixed incorrect position of suggestion menu.\r
        Fixed handling of mispelled words with no suggestions in PSpellShell engine.\r
index e3acf2d..5edf76a 100644 (file)
@@ -51,6 +51,8 @@ class GoogleSpell extends SpellChecker {
        }\r
 \r
        function &_getMatches($lang, $str) {\r
+               $lang = preg_replace('/[^a-z\-]/i', '', $lang); // Sanitize, remove everything but a-z or -\r
+               $str = preg_replace('/[\x00-\x1F\x7F]/', '', $str); // Sanitize, remove all control characters\r
                $server = "www.google.com";\r
                $port = 443;\r
                $path = "/tbproxy/spell?lang=" . $lang . "&hl=en";\r
@@ -126,6 +128,7 @@ class GoogleSpell extends SpellChecker {
        }\r
 \r
        function _unhtmlentities($string) {\r
+        return textlib::entities_to_utf8($string); // Moodle hack\r
                $string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string);\r
                $string = preg_replace('~&#([0-9]+);~e', 'chr(\\1)', $string);\r
 \r
index 5e61f56..dae2d8b 100644 (file)
@@ -813,7 +813,7 @@ class file_info_area_backup_section extends file_info {
             'component' => 'backup',
             'filearea' => 'section',
             'emptyfilename' => '.');
-        $sql1 = "SELECT DISTINCT cs.id sectionid FROM {files} f, {course_sections} cs
+        $sql1 = "SELECT DISTINCT cs.id AS sectionid FROM {files} f, {course_sections} cs
             WHERE cs.course = :courseid
             AND f.contextid = :contextid
             AND f.component = :component
index 85816b1..bc89a75 100644 (file)
@@ -1336,6 +1336,11 @@ class core_renderer extends renderer_base {
             $select->attributes['title'] = $select->tooltip;
         }
 
+        $select->attributes['class'] = 'autosubmit';
+        if ($select->class) {
+            $select->attributes['class'] .= ' ' . $select->class;
+        }
+
         if ($select->label) {
             $output .= html_writer::label($select->label, $select->attributes['id'], false, $select->labelattributes);
         }
@@ -1351,7 +1356,10 @@ class core_renderer extends renderer_base {
         $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('style'=>'inline'));
 
         $nothing = empty($select->nothing) ? false : key($select->nothing);
-        $this->page->requires->js_init_call('M.util.init_select_autosubmit', array($select->formid, $select->attributes['id'], $nothing));
+        $this->page->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => $select->attributes['id'], 'nothing' => $nothing))
+        );
 
         // then div wrapper for xhtml strictness
         $output = html_writer::tag('div', $output);
@@ -1417,6 +1425,17 @@ class core_renderer extends renderer_base {
             $output .= html_writer::label($select->label, $select->attributes['id'], false, $select->labelattributes);
         }
 
+        $classes = array();
+        if (!$select->showbutton) {
+            $classes[] = 'autosubmit';
+        }
+        if ($select->class) {
+            $classes[] = $select->class;
+        }
+        if (count($classes)) {
+            $select->attributes['class'] = implode(' ', $classes);
+        }
+
         if ($select->helpicon instanceof help_icon) {
             $output .= $this->render($select->helpicon);
         } else if ($select->helpicon instanceof old_help_icon) {
@@ -1474,7 +1493,10 @@ class core_renderer extends renderer_base {
             $go = html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('go')));
             $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('style'=>'inline'));
             $nothing = empty($select->nothing) ? false : key($select->nothing);
-            $output .= $this->page->requires->js_init_call('M.util.init_url_select', array($select->formid, $select->attributes['id'], $nothing));
+            $this->page->requires->yui_module('moodle-core-formautosubmit',
+                'M.core.init_formautosubmit',
+                array(array('selectid' => $select->attributes['id'], 'nothing' => $nothing))
+            );
         } else {
             $output .= html_writer::empty_tag('input', array('type'=>'submit', 'value'=>$select->showbutton));
         }
index e438abf..dda9f6b 100644 (file)
@@ -480,7 +480,7 @@ class page_requirements_manager {
                     $module = array('name'     => 'core_dock',
                                     'fullpath' => '/blocks/dock.js',
                                     'requires' => array('base', 'node', 'event-custom', 'event-mouseenter', 'event-resize'),
-                                    'strings' => array(array('addtodock', 'block'),array('undockitem', 'block'),array('undockall', 'block'),array('thisdirectionvertical', 'langconfig')));
+                                    'strings' => array(array('addtodock', 'block'),array('undockitem', 'block'),array('undockall', 'block'),array('thisdirectionvertical', 'langconfig'),array('hidedockpanel', 'block'),array('hidepanel', 'block')));
                     break;
                 case 'core_message':
                     $module = array('name'     => 'core_message',
index dda8714..6046e66 100644 (file)
@@ -207,10 +207,21 @@ function rss_get_file_full_name($componentname, $filename) {
  *
  * @param stdClass $instance the instance of the source of the RSS feed
  * @param string $sql the SQL used to produce the RSS feed
+ * @param array $params the parameters used in the SQL query
  * @return string the name of the RSS file
  */
-function rss_get_file_name($instance, $sql) {
-    return $instance->id.'_'.md5($sql);
+function rss_get_file_name($instance, $sql, $params = array()) {
+    if ($params) {
+        // If a parameters array is passed, then we want to
+        // serialize it and then concatenate it with the sql.
+        // The reason for this is to generate a unique filename
+        // for queries using the same sql but different parameters.
+        asort($parms);
+        $serializearray = serialize($params);
+        return $instance->id.'_'.md5($sql . $serializearray);
+    } else {
+        return $instance->id.'_'.md5($sql);
+    }
 }
 
 /**
index f071531..7a6e843 100644 (file)
@@ -293,8 +293,8 @@ class core_textlib_testcase extends basic_testcase {
      * @return void
      */
     public function test_entities_to_utf8() {
-        $str = "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#237;&#269;ek";
-        $this->assertSame(textlib::entities_to_utf8($str), "Žluťoučký koníček");
+        $str = "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&iacute;&#269;ek&copy;&quot;&amp;&lt;&gt;&sect;&laquo;";
+        $this->assertSame("Žluťoučký koníček©\"&<>§«", textlib::entities_to_utf8($str));
     }
 
     /**
@@ -302,10 +302,13 @@ class core_textlib_testcase extends basic_testcase {
      * @return void
      */
     public function test_utf8_to_entities() {
-        $str = "Žluťoučký koníček";
-        $this->assertSame(textlib::utf8_to_entities($str), "&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#xed;&#x10d;ek");
-        $this->assertSame(textlib::utf8_to_entities($str, true), "&#381;lu&#357;ou&#269;k&#253; kon&#237;&#269;ek");
+        $str = "&#x17d;luťoučký kon&iacute;ček&copy;&quot;&amp;&lt;&gt;&sect;&laquo;";
+        $this->assertSame("&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&iacute;&#x10d;ek&copy;&quot;&amp;&lt;&gt;&sect;&laquo;", textlib::utf8_to_entities($str));
+        $this->assertSame("&#381;lu&#357;ou&#269;k&#253; kon&iacute;&#269;ek&copy;&quot;&amp;&lt;&gt;&sect;&laquo;", textlib::utf8_to_entities($str, true));
 
+        $str = "&#381;luťoučký kon&iacute;ček&copy;&quot;&amp;&lt;&gt;&sect;&laquo;";
+        $this->assertSame("&#x17d;lu&#x165;ou&#x10d;k&#xfd; kon&#xed;&#x10d;ek&#xa9;\"&<>&#xa7;&#xab;", textlib::utf8_to_entities($str, false, true));
+        $this->assertSame("&#381;lu&#357;ou&#269;k&#253; kon&#237;&#269;ek&#169;\"&<>&#167;&#171;", textlib::utf8_to_entities($str, true, true));
     }
 
     /**
index ab0db3c..bddcaa7 100644 (file)
@@ -441,6 +441,34 @@ class textlib {
         return $encoded;
     }
 
+    /**
+     * Returns HTML entity transliteration table.
+     * @return array with (html entity => utf-8) elements
+     */
+    protected static function get_entities_table() {
+        static $trans_tbl = null;
+
+        // Generate/create $trans_tbl
+        if (!isset($trans_tbl)) {
+            if (version_compare(phpversion(), '5.3.4') < 0) {
+                $trans_tbl = array();
+                foreach (get_html_translation_table(HTML_ENTITIES) as $val=>$key) {
+                    $trans_tbl[$key] = textlib::convert($val, 'ISO-8859-1', 'utf-8');
+                }
+
+            } else if (version_compare(phpversion(), '5.4.0') < 0) {
+                $trans_tbl = get_html_translation_table(HTML_ENTITIES, ENT_COMPAT, 'UTF-8');
+                $trans_tbl = array_flip($trans_tbl);
+
+            } else {
+                $trans_tbl = get_html_translation_table(HTML_ENTITIES, ENT_COMPAT | ENT_HTML401, 'UTF-8');
+                $trans_tbl = array_flip($trans_tbl);
+            }
+        }
+
+        return $trans_tbl;
+    }
+
     /**
      * Converts all the numeric entities &#nnnn; or &#xnnn; to UTF-8
      * Original from laurynas dot butkus at gmail at:
@@ -450,28 +478,24 @@ class textlib {
      * @param string $str input string
      * @param boolean $htmlent convert also html entities (defaults to true)
      * @return string encoded UTF-8 string
-     *
-     * NOTE: we could have used typo3 entities_to_utf8() here
-     *       but the direct alternative used runs 400% quicker
-     *       and uses 0.5Mb less memory, so, let's use it
-     *       (tested against 10^6 conversions)
      */
     public static function entities_to_utf8($str, $htmlent=true) {
-        static $trans_tbl; // Going to use static transliteration table
+        static $callback1 = null ;
+        static $callback2 = null ;
+
+        if (!$callback1 or !$callback2) {
+            $callback1 = create_function('$matches', 'return textlib::code2utf8(hexdec($matches[1]));');
+            $callback2 = create_function('$matches', 'return textlib::code2utf8($matches[1]);');
+        }
 
-        // Replace numeric entities
-        $result = preg_replace('~&#x([0-9a-f]+);~ei', 'textlib::code2utf8(hexdec("\\1"))', $str);
-        $result = preg_replace('~&#([0-9]+);~e', 'textlib::code2utf8(\\1)', $result);
+        $result = (string)$str;
+        $result = preg_replace_callback('/&#x([0-9a-f]+);/i', $callback1, $result);
+        $result = preg_replace_callback('/&#([0-9]+);/', $callback2, $result);
 
         // Replace literal entities (if desired)
         if ($htmlent) {
-            // Generate/create $trans_tbl
-            if (!isset($trans_tbl)) {
-                $trans_tbl = array();
-                foreach (get_html_translation_table(HTML_ENTITIES) as $val=>$key) {
-                    $trans_tbl[$key] = utf8_encode($val);
-                }
-            }
+            $trans_tbl = self::get_entities_table();
+            // It should be safe to search for ascii strings and replace them with utf-8 here.
             $result = strtr($result, $trans_tbl);
         }
         // Return utf8-ised string
@@ -487,17 +511,24 @@ class textlib {
      * @return string converted string
      */
     public static function utf8_to_entities($str, $dec=false, $nonnum=false) {
-        // Avoid some notices from Typo3 code
-        $oldlevel = error_reporting(E_PARSE);
+        static $callback = null ;
+
         if ($nonnum) {
-            $str = self::typo3()->entities_to_utf8((string)$str, true);
+            $str = self::entities_to_utf8($str, true);
         }
+
+        // Avoid some notices from Typo3 code
+        $oldlevel = error_reporting(E_PARSE);
         $result = self::typo3()->utf8_to_entities((string)$str);
+        error_reporting($oldlevel);
+
         if ($dec) {
-            $result = preg_replace('/&#x([0-9a-f]+);/ie', "'&#'.hexdec('$1').';'", $result);
+            if (!$callback) {
+                $callback = create_function('$matches', 'return \'&#\'.(hexdec($matches[1])).\';\';');
+            }
+            $result = preg_replace_callback('/&#x([0-9a-f]+);/i', $callback, $result);
         }
-        // Restore original debug level
-        error_reporting($oldlevel);
+
         return $result;
     }
 
index 84362fe..500467b 100644 (file)
@@ -1382,7 +1382,7 @@ function format_text_email($text, $format) {
         case FORMAT_WIKI:
             // there should not be any of these any more!
             $text = wikify_links($text);
-            return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
+            return textlib::entities_to_utf8(strip_tags($text), true);
             break;
 
         case FORMAT_HTML:
@@ -1393,7 +1393,7 @@ function format_text_email($text, $format) {
         case FORMAT_MARKDOWN:
         default:
             $text = wikify_links($text);
-            return strtr(strip_tags($text), array_flip(get_html_translation_table(HTML_ENTITIES)));
+            return textlib::entities_to_utf8(strip_tags($text), true);
             break;
     }
 }
diff --git a/lib/yui/formautosubmit/formautosubmit.js b/lib/yui/formautosubmit/formautosubmit.js
new file mode 100644 (file)
index 0000000..01d6668
--- /dev/null
@@ -0,0 +1,108 @@
+YUI.add('moodle-core-formautosubmit',
+    function(Y) {
+        // The CSS selectors we use
+        var CSS = {
+            AUTOSUBMIT : 'autosubmit'
+        };
+
+        var FORMAUTOSUBMITNAME = 'core-formautosubmit';
+
+        var FORMAUTOSUBMIT = function() {
+            FORMAUTOSUBMIT.superclass.constructor.apply(this, arguments);
+        }
+
+        //  We only want to initialize the module fully once
+        var INITIALIZED = false;
+
+        Y.extend(FORMAUTOSUBMIT, Y.Base, {
+
+            /**
+              * Initialize the module
+              */
+            initializer : function(config) {
+                // We only apply the delegation once
+                if (!INITIALIZED) {
+                    INITIALIZED = true;
+                    var applyto = Y.one('body');
+
+                    // We don't listen for change events by default as using the keyboard triggers these too.
+                    applyto.delegate('key', this.process_changes, 'press:13', 'select.' + CSS.AUTOSUBMIT, this);
+                    applyto.delegate('click', this.process_changes, 'select.' + CSS.AUTOSUBMIT, this);
+
+                    if (Y.UA.os == 'macintosh' && Y.UA.webkit) {
+                        // Macintosh webkit browsers like change events, but non-macintosh webkit browsers don't.
+                        applyto.delegate('change', this.process_changes, 'select.' + CSS.AUTOSUBMIT, this);
+                    }
+                    if (Y.UA.ios) {
+                        // IOS doesn't trigger click events because it's touch-based.
+                        applyto.delegate('change', this.process_changes, 'select.' + CSS.AUTOSUBMIT, this);
+                    }
+                }
+
+                // Assign this select items 'nothing' value and lastindex (current value)
+                var thisselect = Y.one('select#' + this.get('selectid'));
+                thisselect.setData('nothing', this.get('nothing'));
+                thisselect.setData('startindex', thisselect.get('selectedIndex'));
+            },
+
+            /**
+             * Check whether the select element was changed
+             */
+            check_changed : function(e) {
+                var select = e.target.ancestor('select.' + CSS.AUTOSUBMIT, true);
+                if (!select) {
+                    return false;
+                }
+
+                var nothing = select.getData('nothing');
+                var startindex = select.getData('startindex');
+                var currentindex = select.get('selectedIndex');
+
+                var previousindex = select.getAttribute('data-previousindex');
+                select.setAttribute('data-previousindex', currentindex);
+                if (!previousindex) {
+                    previousindex = startindex;
+                }
+
+                // Check whether the field has changed, and is not the 'nothing' value
+                if ((nothing===false || select.get('value') != nothing) && startindex != select.get('selectedIndex') && currentindex != previousindex) {
+                    return select;
+                }
+                return false;
+            },
+
+            /**
+             * Process any changes
+             */
+            process_changes : function(e) {
+                var select = this.check_changed(e);
+                if (select) {
+                    var form = select.ancestor('form', true);
+                    form.submit();
+                }
+            }
+        },
+        {
+            NAME : FORMAUTOSUBMITNAME,
+            ATTRS : {
+                selectid : {
+                    'value' : ''
+                },
+                nothing : {
+                    'value' : ''
+                },
+                ignorechangeevent : {
+                    'value' : false
+                }
+            }
+        });
+
+        M.core = M.core || {};
+        M.core.init_formautosubmit = M.core.init_formautosubmit || function(config) {
+            return new FORMAUTOSUBMIT(config);
+        };
+    },
+    '@VERSION@', {
+        requires : ['base', 'event-key']
+    }
+);
index 074b86a..1705221 100644 (file)
@@ -447,6 +447,7 @@ function mnet_update_sso_access_control($username, $mnet_host_id, $accessctrl) {
                 "SSO ACL: $accessctrl user '$username' from {$mnethost->name}");
     } else {
         // insert
+        $aclrecord = new stdClass();
         $aclrecord->username = $username;
         $aclrecord->accessctrl = $accessctrl;
         $aclrecord->mnet_host_id = $mnet_host_id;
index 2564d0e..f61aa40 100644 (file)
@@ -1346,6 +1346,23 @@ class assign {
         return true;
     }
 
+    /**
+     * Mark in the database that this grade record should have an update notification sent by cron.
+     *
+     * @param stdClass $grade a grade record keyed on id
+     * @return bool true for success
+     */
+    public function notify_grade_modified($grade) {
+        global $DB;
+
+        $grade->timemodified = time();
+        if ($grade->mailed != 1) {
+            $grade->mailed = 0;
+        }
+
+        return $DB->update_record('assign_grades', $grade);
+    }
+
     /**
      * Update a grade in the grade table for the assignment and in the gradebook
      *
@@ -1691,6 +1708,10 @@ class assign {
             $grade->locked = 0;
             $grade->grade = -1;
             $grade->grader = $USER->id;
+
+            // The mailed flag can be one of 3 values: 0 is unsent, 1 is sent and 2 is do not send yet.
+            // This is because students only want to be notified about certain types of update (grades and feedback).
+            $grade->mailed = 2;
             $gid = $DB->insert_record('assign_grades', $grade);
             $grade->id = $gid;
             return $grade;
@@ -2192,7 +2213,7 @@ class assign {
                 }
 
                 $gradeddate = $gradebookgrade->dategraded;
-                $grader = $DB->get_record('user', array('id'=>$gradebookgrade->usermodified));
+                $grader = $DB->get_record('user', array('id'=>$grade->grader));
 
                 $feedbackstatus = new assign_feedback_status($gradefordisplay,
                                                       $gradeddate,
@@ -2761,6 +2782,7 @@ class assign {
             }
 
             $this->update_grade($grade);
+            $this->notify_grade_modified($grade);
 
             // save outcomes
             if ($CFG->enableoutcomes) {
@@ -3406,6 +3428,7 @@ class assign {
             $grade->mailed = 0;
 
             $this->update_grade($grade);
+            $this->notify_grade_modified($grade);
 
             $user = $DB->get_record('user', array('id' => $userid), '*', MUST_EXIST);
 
index e4745f2..5844463 100644 (file)
@@ -31,6 +31,39 @@ defined('MOODLE_INTERNAL') || die();
  * @return bool
  */
 function assignsubmission_comments_comment_validate(stdClass $options) {
+    global $USER, $CFG, $DB;
+
+    if ($options->commentarea != 'submission_comments' &&
+            $options->commentarea != 'submission_comments_upgrade') {
+        throw new comment_exception('invalidcommentarea');
+    }
+    if (!$submission = $DB->get_record('assign_submission', array('id'=>$options->itemid))) {
+        throw new comment_exception('invalidcommentitemid');
+    }
+    $context = $options->context;
+
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    $assignment = new assign($context, null, null);
+
+    if ($assignment->get_instance()->id != $submission->assignment) {
+        throw new comment_exception('invalidcontext');
+    }
+    if (!has_capability('mod/assign:grade', $context)) {
+        if (!has_capability('mod/assign:submit', $context)) {
+            throw new comment_exception('nopermissiontocomment');
+        } else if ($assignment->get_instance()->teamsubmission) {
+            $group = $assignment->get_submission_group($USER->id);
+            $groupid = 0;
+            if ($group) {
+                $groupid = $group->id;
+            }
+            if ($groupid != $submission->groupid) {
+                throw new comment_exception('nopermissiontocomment');
+            }
+        } else if ($submission->userid != $USER->id) {
+            throw new comment_exception('nopermissiontocomment');
+        }
+    }
 
     return true;
 }
@@ -42,6 +75,39 @@ function assignsubmission_comments_comment_validate(stdClass $options) {
  * @return array
  */
 function assignsubmission_comments_comment_permissions(stdClass $options) {
+    global $USER, $CFG, $DB;
+
+    if ($options->commentarea != 'submission_comments' &&
+            $options->commentarea != 'submission_comments_upgrade') {
+        throw new comment_exception('invalidcommentarea');
+    }
+    if (!$submission = $DB->get_record('assign_submission', array('id'=>$options->itemid))) {
+        throw new comment_exception('invalidcommentitemid');
+    }
+    $context = $options->context;
+
+    require_once($CFG->dirroot . '/mod/assign/locallib.php');
+    $assignment = new assign($context, null, null);
+
+    if ($assignment->get_instance()->id != $submission->assignment) {
+        throw new comment_exception('invalidcontext');
+    }
+    if (!has_capability('mod/assign:grade', $context)) {
+        if (!has_capability('mod/assign:submit', $context)) {
+            return array('post' => false, 'view' => false);
+        } else if ($assignment->get_instance()->teamsubmission) {
+            $group = $assignment->get_submission_group($USER->id);
+            $groupid = 0;
+            if ($group) {
+                $groupid = $group->id;
+            }
+            if ($groupid != $submission->groupid) {
+                return array('post' => false, 'view' => false);
+            }
+        } else if ($submission->userid != $USER->id) {
+            return array('post' => false, 'view' => false);
+        }
+    }
 
     return array('post' => true, 'view' => true);
 }
index 2b37b38..2e1bdb1 100644 (file)
@@ -501,8 +501,11 @@ function prepare_choice_show_results($choice, $course, $cm, $allresponses, $forc
                 echo '<a href="javascript:deselect_all_in(\'DIV\',null,\'tablecontainer\');">'.get_string('deselectall').'</a> ';
                 echo '&nbsp;&nbsp;';
                 echo html_writer::label(get_string('withselected', 'choice'), 'menuaction');
-                echo html_writer::select(array('delete' => get_string('delete')), 'action', '', array(''=>get_string('withselectedusers')), array('id'=>'menuaction'));
-                $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('attemptsform', 'menuaction', ''));
+                echo html_writer::select(array('delete' => get_string('delete')), 'action', '', array(''=>get_string('withselectedusers')), array('id'=>'menuaction', 'class' => 'autosubmit'));
+                $PAGE->requires->yui_module('moodle-core-formautosubmit',
+                    'M.core.init_formautosubmit',
+                    array(array('selectid' => 'menuaction'))
+                );
                 echo '<noscript id="noscriptmenuaction" style="display:inline">';
                 echo '<div>';
                 echo '<input type="submit" value="'.get_string('go').'" /></div></noscript>';
index faf95ff..ccbcaef 100644 (file)
@@ -187,10 +187,12 @@ if ($courseitemfilter > 0) {
 
          echo ' '. html_writer::label(get_string('filter_by_course', 'feedback'), 'coursefilterid'). ': ';
          echo html_writer::select($courses, 'coursefilter', $coursefilter,
-                                  null, array('id'=>'coursefilterid'));
+                                  null, array('id'=>'coursefilterid', 'class' => 'autosubmit'));
 
-         $PAGE->requires->js_init_call('M.util.init_select_autosubmit',
-                                        array('analysis-form', 'coursefilterid', false));
+        $PAGE->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => 'coursefilterid', 'nothing' => false))
+        );
     }
     echo '<hr />';
     $itemnr = 0;
index b515244..106f0d9 100644 (file)
@@ -5655,7 +5655,7 @@ function forum_print_latest_discussions($course, $forum, $maxdiscussions=-1, $di
                     $link = true;
                 } else {
                     $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
-                    $link = forum_user_can_post($forum, $discussion, $USER, $cm, $course, $modcontext);
+                    $link = forum_user_can_see_discussion($forum, $discussion, $modcontext, $USER);
                 }
 
                 $discussion->forum = $forum->id;
index 86bf31b..bb46362 100644 (file)
@@ -56,16 +56,10 @@ function forum_rss_get_feed($context, $args) {
     }
 
     //the sql that will retreive the data for the feed and be hashed to get the cache filename
-    $sql = forum_rss_get_sql($forum, $cm);
+    list($sql, $params) = forum_rss_get_sql($forum, $cm);
 
     // Hash the sql to get the cache file name.
-    // If the forum is Q and A then we need to cache the files per user. This can
-    // have a large impact on performance, so we want to only do it on this type of forum.
-    if ($forum->type == 'qanda') {
-        $filename = rss_get_file_name($forum, $sql . $USER->id);
-    } else {
-        $filename = rss_get_file_name($forum, $sql);
-    }
+    $filename = rss_get_file_name($forum, $sql, $params);
     $cachedfilepath = rss_get_file_full_name('mod_forum', $filename);
 
     //Is the cache out of date?
@@ -75,9 +69,9 @@ function forum_rss_get_feed($context, $args) {
     }
     //if the cache is more than 60 seconds old and there's new stuff
     $dontrecheckcutoff = time()-60;
-    if ( $dontrecheckcutoff > $cachedfilelastmodified && forum_rss_newstuff($forum, $cm, $cachedfilelastmodified)) {
+    if ($dontrecheckcutoff > $cachedfilelastmodified && forum_rss_newstuff($forum, $cm, $cachedfilelastmodified)) {
         //need to regenerate the cached version
-        $result = forum_rss_feed_contents($forum, $sql, $modcontext);
+        $result = forum_rss_feed_contents($forum, $sql, $params, $modcontext);
         if (!empty($result)) {
             $status = rss_save_file('mod_forum',$filename,$result);
         }
@@ -111,10 +105,12 @@ function forum_rss_delete_file($forum) {
 function forum_rss_newstuff($forum, $cm, $time) {
     global $DB;
 
-    $sql = forum_rss_get_sql($forum, $cm, $time);
+    list($sql, $params) = forum_rss_get_sql($forum, $cm, $time);
+    if ($DB->count_records_sql($sql, $params) > 0) {
+        return true;
+    }
 
-    $recs = $DB->get_records_sql($sql, null, 0, 1);//limit of 1. If we get even 1 back we have new stuff
-    return ($recs && !empty($recs));
+    return false;
 }
 
 /**
@@ -126,17 +122,11 @@ function forum_rss_newstuff($forum, $cm, $time) {
  * @return string the SQL query to be used to get the Discussion/Post details from the forum table of the database
  */
 function forum_rss_get_sql($forum, $cm, $time=0) {
-    $sql = null;
-
-    if (!empty($forum->rsstype)) {
-        if ($forum->rsstype == 1) {    //Discussion RSS
-            $sql = forum_rss_feed_discussions_sql($forum, $cm, $time);
-        } else {                //Post RSS
-            $sql = forum_rss_feed_posts_sql($forum, $cm, $time);
-        }
+    if ($forum->rsstype == 1) { // Discussion RSS
+        return forum_rss_feed_discussions_sql($forum, $cm, $time);
+    } else { // Post RSS
+        return forum_rss_feed_posts_sql($forum, $cm, $time);
     }
-
-    return $sql;
 }
 
 /**
@@ -155,7 +145,7 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
     $modcontext = null;
 
     $now = round(time(), -2);
-    $params = array($cm->instance);
+    $params = array();
 
     $modcontext = context_module::instance($cm->id);
 
@@ -172,21 +162,21 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
         }
     }
 
-    //do we only want new posts?
+    // Do we only want new posts?
     if ($newsince) {
-        $newsince = " AND p.modified > '$newsince'";
+        $params['newsince'] = $newsince;
+        $newsince = " AND p.modified > :newsince";
     } else {
         $newsince = '';
     }
 
-    //get group enforcing SQL
-    $groupmode    = groups_get_activity_groupmode($cm);
+    // Get group enforcing SQL.
+    $groupmode = groups_get_activity_groupmode($cm);
     $currentgroup = groups_get_activity_group($cm);
-    $groupselect = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
+    list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
 
-    if ($groupmode && $currentgroup) {
-        $params['groupid'] = $currentgroup;
-    }
+    // Add the groupparams to the params array.
+    $params = array_merge($params, $groupparams);
 
     $forumsort = "d.timemodified DESC";
     $postdata = "p.id AS postid, p.subject, p.created as postcreated, p.modified, p.discussion, p.userid, p.message as postmessage, p.messageformat AS postformat, p.messagetrust AS posttrust";
@@ -199,7 +189,7 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
              WHERE d.forum = {$forum->id} AND p.parent = 0
                    $timelimit $groupselect $newsince
           ORDER BY $forumsort";
-    return $sql;
+    return array($sql, $params);
 }
 
 /**
@@ -213,19 +203,20 @@ function forum_rss_feed_discussions_sql($forum, $cm, $newsince=0) {
 function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
     $modcontext = context_module::instance($cm->id);
 
-    //get group enforcement SQL
-    $groupmode    = groups_get_activity_groupmode($cm);
+    // Get group enforcement SQL.
+    $groupmode = groups_get_activity_groupmode($cm);
     $currentgroup = groups_get_activity_group($cm);
+    $params = array();
 
-    $groupselect = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
+    list($groupselect, $groupparams) = forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext);
 
-    if ($groupmode && $currentgroup) {
-        $params['groupid'] = $currentgroup;
-    }
+    // Add the groupparams to the params array.
+    $params = array_merge($params, $groupparams);
 
-    //do we only want new posts?
+    // Do we only want new posts?
     if ($newsince) {
-        $newsince = " AND p.modified > '$newsince'";
+        $params['newsince'] = $newsince;
+        $newsince = " AND p.modified > :newsince";
     } else {
         $newsince = '';
     }
@@ -250,7 +241,7 @@ function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
                 $groupselect
             ORDER BY p.created desc";
 
-    return $sql;
+    return array($sql, $params);
 }
 
 /**
@@ -264,6 +255,7 @@ function forum_rss_feed_posts_sql($forum, $cm, $newsince=0) {
  */
 function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=null) {
     $groupselect = '';
+    $params = array();
 
     if ($groupmode) {
         if ($groupmode == VISIBLEGROUPS or has_capability('moodle/site:accessallgroups', $modcontext)) {
@@ -272,7 +264,7 @@ function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=nul
                 $params['groupid'] = $currentgroup;
             }
         } else {
-            //seprate groups without access all
+            // Separate groups without access all.
             if ($currentgroup) {
                 $groupselect = "AND (d.groupid = :groupid OR d.groupid = -1)";
                 $params['groupid'] = $currentgroup;
@@ -282,7 +274,7 @@ function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=nul
         }
     }
 
-    return $groupselect;
+    return array($groupselect, $params);
 }
 
 /**
@@ -290,21 +282,19 @@ function forum_rss_get_group_sql($cm, $groupmode, $currentgroup, $modcontext=nul
  * It returns false if something is wrong
  *
  * @param stdClass $forum the forum object
- * @param string   $sql   The SQL used to retrieve the contents from the database
+ * @param string $sql the SQL used to retrieve the contents from the database
+ * @param array $params the SQL parameters used
  * @param object $context the context this forum relates to
  * @return bool|string false if the contents is empty, otherwise the contents of the feed is returned
  *
  * @Todo MDL-31129 implement post attachment handling
  */
 
-function forum_rss_feed_contents($forum, $sql) {
+function forum_rss_feed_contents($forum, $sql, $params, $context) {
     global $CFG, $DB, $USER;
 
-
     $status = true;
 
-    $params = array();
-    //$params['forumid'] = $forum->id;
     $recs = $DB->get_recordset_sql($sql, $params, 0, $forum->rssarticles);
 
     //set a flag. Are we displaying discussions or posts?
@@ -316,7 +306,6 @@ function forum_rss_feed_contents($forum, $sql) {
     if (!$cm = get_coursemodule_from_instance('forum', $forum->id, $forum->course)) {
         print_error('invalidcoursemodule');
     }
-    $context = context_module::instance($cm->id);
 
     $formatoptions = new stdClass();
     $items = array();
index 58a5fd5..a7ea07b 100644 (file)
@@ -103,19 +103,19 @@ if (!is_null($mode) and has_capability('mod/forum:managesubscriptions', $context
     require_sesskey();
     switch ($mode) {
         case FORUM_CHOOSESUBSCRIBE : // 0
-            forum_forcesubscribe($forum->id, 0);
+            forum_forcesubscribe($forum->id, FORUM_CHOOSESUBSCRIBE);
             redirect($returnto, get_string("everyonecannowchoose", "forum"), 1);
             break;
         case FORUM_FORCESUBSCRIBE : // 1
-            forum_forcesubscribe($forum->id, 1);
+            forum_forcesubscribe($forum->id, FORUM_FORCESUBSCRIBE);
             redirect($returnto, get_string("everyoneisnowsubscribed", "forum"), 1);
             break;
         case FORUM_INITIALSUBSCRIBE : // 2
-            forum_forcesubscribe($forum->id, 2);
+            forum_forcesubscribe($forum->id, FORUM_INITIALSUBSCRIBE);
             redirect($returnto, get_string("everyoneisnowsubscribed", "forum"), 1);
             break;
         case FORUM_DISALLOWSUBSCRIBE : // 3
-            forum_forcesubscribe($forum->id, 3);
+            forum_forcesubscribe($forum->id, FORUM_DISALLOWSUBSCRIBE);
             redirect($returnto, get_string("noonecansubscribenow", "forum"), 1);
             break;
         default:
index 37d6cba..889b433 100644 (file)
@@ -315,8 +315,11 @@ if ($action === 'delete') {
         $checklinks  = '<a href="javascript: checkall();">'.get_string('selectall').'</a> / ';
         $checklinks .= '<a href="javascript: checknone();">'.get_string('deselectall').'</a>';
         $checklinks .= html_writer::label('action', 'menuaction', false, array('class' => 'accesshide'));
-        $checklinks .= html_writer::select(array('delete' => get_string('deleteselected')), 'action', 0, array(''=>'choosedots'), array('id'=>'actionid'));
-        $PAGE->requires->js_init_call('M.util.init_select_autosubmit', array('theform', 'actionid', ''));
+        $checklinks .= html_writer::select(array('delete' => get_string('deleteselected')), 'action', 0, array(''=>'choosedots'), array('id'=>'actionid', 'class' => 'autosubmit'));
+        $PAGE->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => 'actionid', 'nothing' => false))
+        );
         echo $OUTPUT->box($checklinks, 'center');
         echo '</form>';
     }
index 629e2db..7ab6626 100644 (file)
@@ -165,6 +165,7 @@ $string['configintro'] = 'The values you set here define the default values that
 $string['configmaximumgrade'] = 'The default grade that the quiz grade is scaled to be out of.';
 $string['confignewpageevery'] = 'When adding questions to the quiz page breaks will automatically be inserted according to the setting you choose here.';
 $string['confignavmethod'] = 'In Free navigation, questions may be answered in any order using navigation. In Sequential, questions must be answered in strict sequence.';
+$string['configoutcomesadvanced'] = 'If this option is turned on, then the Outcomes on the quiz editing form are advanced settings.';
 $string['configpenaltyscheme'] = 'Penalty subtracted for each wrong response in adaptive mode.';
 $string['configpopup'] = 'Force the attempt to open in a popup window, and use JavaScript tricks to try to restrict copy and paste, etc. during quiz attempts.';
 $string['configrequirepassword'] = 'Students must enter this password before they can attempt the quiz.';
@@ -517,6 +518,7 @@ To add page breaks after particular questions, tick the checkboxes next to the q
 To arrange the questions over a number of pages, click the Repaginate button and select the desired number of questions per page.';
 $string['orderingquiz'] = 'Order and paging';
 $string['orderingquizx'] = 'Order and paging: {$a}';
+$string['outcomesadvanced'] = 'Outcomes are advanced settings';
 $string['outof'] = '{$a->grade} out of a maximum of {$a->maxgrade}';
 $string['outofpercent'] = '{$a->grade} out of a maximum of {$a->maxgrade} ({$a->percent}%)';
 $string['outofshort'] = '{$a->grade}/{$a->maxgrade}';
index 87af317..39a4860 100644 (file)
@@ -378,6 +378,9 @@ class mod_quiz_mod_form extends moodleform_mod {
         // -------------------------------------------------------------------------------
         $this->standard_coursemodule_elements();
 
+        // Check and act on whether setting outcomes is considered an advanced setting.
+        $mform->setAdvanced('modoutcomes', !empty($quizconfig->outcomes_adv));
+
         // -------------------------------------------------------------------------------
         $this->add_action_buttons();
     }
index 7652dfa..af458c2 100644 (file)
@@ -193,6 +193,13 @@ $quizsettings->add(new mod_quiz_admin_setting_browsersecurity('quiz/browsersecur
         get_string('showinsecurepopup', 'quiz'), get_string('configpopup', 'quiz'),
         array('value' => '-', 'adv' => true), null));
 
+// Allow user to specify if setting outcomes is an advanced setting
+if (!empty($CFG->enableoutcomes)) {
+    $quizsettings->add(new admin_setting_configcheckbox('quiz/outcomes_adv',
+        get_string('outcomesadvanced', 'quiz'), get_string('configoutcomesadvanced', 'quiz'),
+        '0'));
+}
+
 // Now, depending on whether any reports have their own settings page, add
 // the quiz setting page to the appropriate place in the tree.
 if (empty($reportsbyname)) {
index 8ea5525..21aa851 100644 (file)
@@ -68,14 +68,16 @@ function get_scorm_question_count($scormid) {
     $params[] = "cmi.interactions_%.id";
     $rs = $DB->get_recordset_select("scorm_scoes_track", $select, $params, 'element');
     $keywords = array("cmi.interactions_", ".id");
-    foreach ($rs as $record) {
-        $num = trim(str_ireplace($keywords, '', $record->element));
-        if (is_numeric($num) && $num > $count) {
-            $count = $num;
+    if ($rs->valid()) {
+        // Done as interactions start at 0 (do only if we have something to report).
+        $count++;
+        foreach ($rs as $record) {
+            $num = trim(str_ireplace($keywords, '', $record->element));
+            if (is_numeric($num) && $num > $count) {
+                $count = $num;
+            }
         }
     }
-    //done as interactions start at 0
-    $count++;
     $rs->close(); // closing recordset
     return $count;
 }
index 69766b8..3c1c49b 100644 (file)
@@ -111,14 +111,14 @@ case 'create':
     redirect($CFG->wwwroot . '/mod/wiki/edit.php?pageid='.$newpageid);
     break;
 case 'new':
-    if ((int)$wiki->forceformat == 1 && !empty($title)) {
+    // Go straight to editing if we know the page title and we're in force format mode.
+    if ((int)$wiki->forceformat == 1 && $title != get_string('newpage', 'wiki')) {
         $newpageid = $wikipage->create_page($title);
         add_to_log($course->id, 'wiki', 'add page', "view.php?pageid=".$newpageid, $newpageid, $cm->id);
         redirect($CFG->wwwroot . '/mod/wiki/edit.php?pageid='.$newpageid);
     } else {
-        // create link from moodle navigation block without pagetitle
         $wikipage->print_header();
-        // new page without page title
+        // Create a new page.
         $wikipage->print_content($title);
     }
     $wikipage->print_footer();
index e70fe95..de1eef1 100644 (file)
@@ -31,7 +31,7 @@ require_once("$CFG->dirroot/repository/lib.php");
 $subwikiid = required_param('subwiki', PARAM_INT);
 // not being used for file management, we use it to generate navbar link
 $pageid    = optional_param('pageid', 0, PARAM_INT);
-$returnurl = optional_param('returnurl', '', PARAM_URL);
+$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
 
 if (!$subwiki = wiki_get_subwiki($subwikiid)) {
     print_error('incorrectsubwikiid', 'wiki');
index afea1a3..6779d80 100644 (file)
@@ -936,6 +936,8 @@ class page_wiki_create extends page_wiki {
         $data = $this->mform->get_data();
         if (isset($data->groupinfo)) {
             $groupid = $data->groupinfo;
+        } else if (!empty($this->gid)) {
+            $groupid = $this->gid;
         } else {
             $groupid = '0';
         }
index ae80f13..b4c679a 100644 (file)
@@ -42,10 +42,14 @@ defined('MOODLE_INTERNAL') || die();
  */
 class qformat_learnwise extends qformat_default {
 
-    function provide_import() {
+    public function provide_import() {
         return true;
     }
 
+    public function export_file_extension() {
+        return '.xml';
+    }
+
     protected function readquestions($lines) {
         $questions = array();
         $currentquestion = array();
@@ -62,9 +66,9 @@ class qformat_learnwise extends qformat_default {
         return $questions;
     }
 
-    function readquestion($lines) {
+    protected function readquestion($lines) {
         $text = implode(' ', $lines);
-        $text = str_replace(array('\t','\n','\r','\''), array('','','','\\\''), $text);
+        $text = str_replace(array('\t','\n','\r'), array('','',''), $text);
 
         $startpos = strpos($text, '<question type');
         $endpos = strpos($text, '</question>');
@@ -75,8 +79,8 @@ class qformat_learnwise extends qformat_default {
         preg_match("/<question type=[\"\']([^\"\']+)[\"\']>/i", $text, $matches);
         $type = strtolower($matches[1]); // multichoice or multianswerchoice
 
-        $questiontext = $this->unhtmlentities($this->stringbetween($text, '<text>', '</text>'));
-        $questionhint = $this->unhtmlentities($this->stringbetween($text, '<hint>', '</hint>'));
+        $questiontext = textlib::entities_to_utf8($this->stringbetween($text, '<text>', '</text>'));
+        $questionhint = textlib::entities_to_utf8($this->stringbetween($text, '<hint>', '</hint>'));
         $questionaward = $this->stringbetween($text, '<award>', '</award>');
         $optionlist = $this->stringbetween($text, '<answer>', '</answer>');
 
@@ -89,10 +93,13 @@ class qformat_learnwise extends qformat_default {
 
         if ($type == 'multichoice') {
             foreach ($optionlist as $option) {
+                if (trim($option) === '') {
+                    continue;
+                }
                 $correct = $this->stringbetween($option, ' correct="', '">');
                 $answer = $this->stringbetween($option, '">', '</option>');
                 $optionscorrect[$n] = $correct;
-                $optionstext[$n] = $this->unhtmlentities($answer);
+                $optionstext[$n] = textlib::entities_to_utf8($answer);
                 ++$n;
             }
         } else if ($type == 'multianswerchoice') {
@@ -102,6 +109,9 @@ class qformat_learnwise extends qformat_default {
             $optionsaward = array();
 
             foreach ($optionlist as $option) {
+                if (trim($option) === '') {
+                    continue;
+                }
                 preg_match("/correct=\"([^\"]*)\"/i", $option, $correctmatch);
                 preg_match("/award=\"([^\"]*)\"/i", $option, $awardmatch);
 
@@ -115,7 +125,7 @@ class qformat_learnwise extends qformat_default {
                 $answer = $this->stringbetween($option, '">', '</option>');
 
                 $optionscorrect[$n] = $correct;
-                $optionstext[$n] = $this->unhtmlentities($answer);
+                $optionstext[$n] = textlib::entities_to_utf8($answer);
                 $optionsaward[$n] = $award;
                 ++$n;
             }
@@ -127,22 +137,25 @@ class qformat_learnwise extends qformat_default {
         $question = $this->defaultquestion();
         $question->qtype = MULTICHOICE;
         $question->name = $this->create_default_question_name($questiontext, get_string('questionname', 'question'));
+        $this->add_blank_combined_feedback($question);
 
         $question->questiontext = $questiontext;
+        $question->questiontextformat = FORMAT_HTML;
         $question->single = ($type == 'multichoice') ? 1 : 0;
-        $question->feedback[] = '';
 
         $question->fraction = array();
         $question->answer = array();
         for ($n = 0; $n < count($optionstext); ++$n) {
             if ($optionstext[$n]) {
-                if (!isset($numcorrect)) { // single answer
+                if (!isset($numcorrect)) {
+                    // Single answer.
                     if ($optionscorrect[$n] == 'yes') {
                         $fraction = (int) $questionaward;
                     } else {
                         $fraction = 0;
                     }
-                } else { // mulitple answers
+                } else {
+                    // Multiple answers.
                     if ($optionscorrect[$n] == 'yes') {
                         $fraction = $optionsaward[$n] / $totalaward;
                     } else {
@@ -150,15 +163,22 @@ class qformat_learnwise extends qformat_default {
                     }
                 }
                 $question->fraction[] = $fraction;
-                $question->answer[] = $optionstext[$n];
-                $question->feedback[] = ''; // no feedback in this type
+                $question->answer[] = array('text' => $optionstext[$n], 'format' => FORMAT_HTML);
+                $question->feedback[] = array('text' => '', 'format' => FORMAT_HTML); // No feedback in this type.
             }
         }
 
         return $question;
     }
 
-    function stringbetween($text, $start, $end) {
+    /**
+     * Extract the substring of $text between $start and $end.
+     * @param string $text text to analyse.
+     * @param string $start opening delimiter.
+     * @param string $end closing delimiter.
+     * @return string the requested substring.
+     */
+    protected function stringbetween($text, $start, $end) {
         $startpos = strpos($text, $start) + strlen($start);
         $endpos = strpos($text, $end);
 
@@ -166,13 +186,4 @@ class qformat_learnwise extends qformat_default {
             return substr($text, $startpos, $endpos - $startpos);
         }
     }
-
-    function unhtmlentities($string) {
-        $transtable = get_html_translation_table(HTML_ENTITIES);
-        $transtable = array_flip($transtable);
-        return strtr($string, $transtable);
-    }
-
 }
-
-
index f94cf1d..9c2229e 100644 (file)
@@ -24,4 +24,4 @@
  */
 
 $string['pluginname'] = 'Learnwise format';
-$string['plugidnname_help'] = 'This format enables the import of multiple choice questions saved in Learnwise\'s XML format.';
+$string['pluginname_help'] = 'This format enables the import of multiple choice questions saved in Learnwise\'s XML format.';
index 7e24ba3..f7551a7 100644 (file)
@@ -124,7 +124,7 @@ $options->behaviour = $quba->get_preferred_behaviour();
 $options->maxmark = $quba->get_question_max_mark($slot);
 
 // Create the settings form, and initialise the fields.
-$optionsform = new preview_options_form(question_preview_form_url($question->id, $context),
+$optionsform = new preview_options_form(question_preview_form_url($question->id, $context, $previewid),
         array('quba' => $quba, 'maxvariant' => $maxvariant));
 $optionsform->set_data($options);
 
@@ -135,7 +135,9 @@ if ($newoptions = $optionsform->get_submitted_data()) {
     if (!isset($newoptions->variant)) {
         $newoptions->variant = $options->variant;
     }
-    restart_preview($previewid, $question->id, $newoptions, $context);
+    if (isset($newoptions->saverestart)) {
+        restart_preview($previewid, $question->id, $newoptions, $context);
+    }
 }
 
 // Prepare a URL that is used in various places.
index 16adc1c..063446e 100644 (file)
@@ -44,7 +44,7 @@ class preview_options_form extends moodleform {
             question_display_options::VISIBLE => get_string('shown', 'question'),
         );
 
-        $mform->addElement('header', 'optionsheader', get_string('changeoptions', 'question'));
+        $mform->addElement('header', 'optionsheader', get_string('attemptoptions', 'question'));
 
         $behaviours = question_engine::get_behaviour_options(
                 $this->_customdata['quba']->get_preferred_behaviour());
@@ -56,6 +56,11 @@ class preview_options_form extends moodleform {
                 array('size' => '5'));
         $mform->setType('maxmark', PARAM_FLOAT);
 
+        $mform->addElement('submit', 'saverestart',
+                get_string('restartwiththeseoptions', 'question'));
+
+        $mform->addElement('header', 'optionsheader', get_string('displayoptions', 'question'));
+
         if ($this->_customdata['maxvariant'] > 1) {
             $variants = range(1, $this->_customdata['maxvariant']);
             $mform->addElement('select', 'variant', get_string('questionvariant', 'question'),
@@ -88,8 +93,8 @@ class preview_options_form extends moodleform {
         $mform->addElement('select', 'history',
                 get_string('responsehistory', 'question'), $hiddenofvisible);
 
-        $mform->addElement('submit', 'submit',
-                get_string('restartwiththeseoptions', 'question'));
+        $mform->addElement('submit', 'saveupdate',
+                get_string('updatedisplayoptions', 'question'));
     }
 }
 
@@ -286,10 +291,10 @@ function question_preview_action_url($questionid, $qubaid,
 /**
  * The the URL to use for actions relating to this preview.
  * @param int $questionid the question being previewed.
- * @param int $qubaid the id of the question usage for this preview.
- * @param question_preview_options $options the options in use.
+ * @param context $context the current moodle context.
+ * @param int $previewid optional previewid to sign post saved previewed answers.
  */
-function question_preview_form_url($questionid, $context) {
+function question_preview_form_url($questionid, $context, $previewid = null) {
     $params = array(
         'id' => $questionid,
     );
@@ -298,6 +303,9 @@ function question_preview_form_url($questionid, $context) {
     } else if ($context->contextlevel == CONTEXT_COURSE) {
         $params['courseid'] = $context->instanceid;
     }
+    if ($previewid) {
+        $params['previewid'] = $previewid;
+    }
     return new moodle_url('/question/preview.php', $params);
 }
 
index 72f0f4a..f7070d8 100644 (file)
@@ -102,6 +102,11 @@ class qtype_shortanswer_question extends question_graded_by_strategy
             $regexp .= 'i';
         }
 
+        if (function_exists('normalizer_normalize')) {
+            $regexp = normalizer_normalize($regexp, Normalizer::FORM_C);
+            $string = normalizer_normalize($string, Normalizer::FORM_C);
+        }
+
         return preg_match($regexp, trim($string));
     }
 
index 839712c..d287ff4 100644 (file)
@@ -106,6 +106,18 @@ class qtype_shortanswer_question_test extends advanced_testcase {
         // See http://moodle.org/mod/forum/discuss.php?d=120557
         $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
                 'ITÁLIE', 'Itálie', true));
+
+        if (function_exists('normalizer_normalize')) {
+            // Test ambiguous unicode representations
+            $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
+                    'départ', 'DÉPART', true));
+            $this->assertFalse((bool)qtype_shortanswer_question::compare_string_with_wildcard(
+                    'départ', 'DÉPART', false));
+            $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
+                    'd'."\xC3\xA9".'part', 'd'."\x65\xCC\x81".'part', false));
+            $this->assertTrue((bool)qtype_shortanswer_question::compare_string_with_wildcard(
+                    'd'."\xC3\xA9".'part', 'D'."\x45\xCC\x81".'PART', true));
+        }
     }
 
     public function test_is_complete_response() {
index c7abae3..7c82e66 100644 (file)
@@ -42,7 +42,7 @@ add_to_log($course->id, 'course', 'report outline', "report/outline/index.php?id
 $showlastaccess = true;
 $hiddenfields = explode(',', $CFG->hiddenuserfields);
 
-if (array_search('lastaccess', $hiddenfields) and !has_capability('moodle/user:viewhiddendetails', $context)) {
+if (array_search('lastaccess', $hiddenfields) !== false and !has_capability('moodle/user:viewhiddendetails', $context)) {
     $showlastaccess = false;
 }
 
index 969dda3..d5b68fc 100644 (file)
@@ -35,7 +35,7 @@ if (empty($CFG->usetags)) {
     print_error('tagsaredisabled', 'tag');
 }
 
-$returnurl = optional_param('returnurl', null, PARAM_TEXT);
+$returnurl = optional_param('returnurl', null, PARAM_LOCALURL);
 $keyword = optional_param('coursetag_new_tag', '', PARAM_TEXT);
 $courseid = optional_param('entryid', 0, PARAM_INT);
 $userid = optional_param('userid', 0, PARAM_INT);
index 2b340d9..3a4d0c7 100644 (file)
@@ -749,6 +749,11 @@ class theme_mymobile_core_renderer extends core_renderer {
             $select->attributes['title'] = $select->tooltip;
         }
 
+        $select->attributes['class'] = 'autosubmit';
+        if ($select->class) {
+            $select->attributes['class'] .= ' ' . $select->class;
+        }
+
         if ($select->label) {
             $output .= html_writer::label($select->label, $select->attributes['id']);
         }
@@ -767,7 +772,10 @@ class theme_mymobile_core_renderer extends core_renderer {
         $output .= html_writer::tag('noscript', html_writer::tag('div', $go), array('style' => 'inline'));
 
         $nothing = empty($select->nothing) ? false : key($select->nothing);
-        $this->page->requires->js_init_call('M.util.init_select_autosubmit', array($select->formid, $select->attributes['id'], $nothing));
+        $this->page->requires->yui_module('moodle-core-formautosubmit',
+            'M.core.init_formautosubmit',
+            array(array('selectid' => $select->attributes['id'], 'nothing' => $nothing))
+        );
 
         // then div wrapper for xhtml strictness
         $output = html_writer::tag('div', $output);
index 54f7962..8035875 100644 (file)
@@ -32,7 +32,7 @@ if (isguestuser()) {
     die();
 }
 
-$returnurl = optional_param('returnurl', '', PARAM_URL);
+$returnurl = optional_param('returnurl', '', PARAM_LOCALURL);
 
 if (empty($returnurl)) {
     $returnurl = new moodle_url('/user/files.php');
index 8100f2c..6750c66 100644 (file)
@@ -297,9 +297,13 @@ if ($user->icq && !isset($hiddenfields['icqnumber'])) {
 }
 
 if ($user->skype && !isset($hiddenfields['skypeid'])) {
-    print_row(get_string('skypeid').':','<a href="skype:'.urlencode($user->skype).'?call">'.s($user->skype).
-        ' <img src="http://mystatus.skype.com/smallicon/'.urlencode($user->skype).'" alt="'.get_string('status').'" '.
-        ' /></a>');
+    if (strpos($CFG->httpswwwroot, 'https:') === 0) {
+        // Bad luck, skype devs are lazy to set up SSL on their servers - see MDL-37233.
+        $statusicon = '';
+    } else {
+        $statusicon = ' '.html_writer::empty_tag('img', array('src'=>'http://mystatus.skype.com/smallicon/'.urlencode($user->skype), 'alt'=>get_string('status')));
+    }
+    print_row(get_string('skypeid').':','<a href="skype:'.urlencode($user->skype).'?call">'.s($user->skype).$statusicon.'</a>');
 }
 if ($user->yahoo && !isset($hiddenfields['yahooid'])) {
     print_row(get_string('yahooid').':', '<a href="http://edit.yahoo.com/config/send_webmesg?.target='.urlencode($user->yahoo).'&amp;.src=pg">'.s($user->yahoo)." <img src=\"http://opi.yahoo.com/online?u=".urlencode($user->yahoo)."&m=g&t=0\" alt=\"\"></a>");
index f30bfe7..b81d52c 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2012062503.08;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012062503.09;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes
 
-$release  = '2.3.3+ (Build: 20121220)'; // Human-friendly version name
+$release  = '2.3.3+ (Build: 20121230)'; // Human-friendly version name
 
 $branch   = '23';                       // this version's branch
 $maturity = MATURITY_STABLE;            // this version's maturity level