Merge branch 'MDL-36548_23' of git://github.com/pauln/moodle into MOODLE_23_STABLE
authorDan Poltawski <dan@moodle.com>
Tue, 20 Nov 2012 06:45:04 +0000 (14:45 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 20 Nov 2012 06:45:04 +0000 (14:45 +0800)
44 files changed:
backup/converter/moodle1/lib.php
backup/converter/moodle1/tests/lib_test.php
backup/moodle2/backup_stepslib.php
blocks/glossary_random/block_glossary_random.php
cohort/assign.php
course/index.php
course/renderer.php
enrol/flatfile/lib.php
enrol/renderer.php
grade/edit/tree/calculation.php
grade/edit/tree/grade.php
grade/report/grader/index.php
grade/report/grader/lib.php
install/lang/en_fix/langconfig.php [deleted file]
install/lang/hr/moodle.php
install/lang/lt/install.php [new file with mode: 0644]
install/lang/zh_tw/install.php
lib/editor/tinymce/readme_moodle.txt
lib/editor/tinymce/tiny_mce/3.5.1.1/plugins/spellchecker/classes/GoogleSpell.php
lib/googleapi.php
lib/grade/grade_item.php
lib/grade/tests/grade_item_test.php
lib/gradelib.php
lib/messagelib.php
lib/phpunit/classes/util.php
lib/pluginlib.php
lib/tests/gradelib_test.php [new file with mode: 0644]
lib/tests/messagelib_test.php [new file with mode: 0644]
message/lib.php
mod/assign/gradingtable.php
mod/assign/locallib.php
mod/data/preset.php
mod/lesson/essay.php
mod/lesson/format.php
mod/quiz/lib.php
mod/quiz/tests/generator/lib.php [new file with mode: 0644]
mod/scorm/mod_form.php
mod/survey/lang/en/survey.php
mod/workshop/lang/en/workshop.php
question/format/xml/format.php
question/type/match/backup/moodle2/restore_qtype_match_plugin.class.php
repository/filepicker.js
user/selector/lib.php
version.php

index e79dbfe..6c01ebf 100644 (file)
@@ -640,7 +640,8 @@ class moodle1_converter extends base_converter {
             return $files;
         }
         foreach ($matches[2] as $match) {
-            $files[] = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
+            $file = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
+            $files[] = urldecode($file);
         }
 
         return array_unique($files);
index a97720e..20aca62 100644 (file)
@@ -449,6 +449,19 @@ as it is parsed from the backup file. <br /><br /><img border="0" width="110" vs
     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />');
     }
 
+    public function test_referenced_files_urlencoded() {
+        // This test covers MDL-36204
+        $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@$">no space</a><br />
+    <br /><a href=\'$@FILEPHP@$$@SLASH@$pics$@SLASH@$news%20with%20spaces.gif$@FORCEDOWNLOAD@$\'>with urlencoded spaces</a><br />';
+
+        $files = moodle1_converter::find_referenced_files($text);
+        $this->assertEquals(gettype($files), 'array');
+        $this->assertEquals(2, count($files));
+        $this->assertTrue(in_array('/pics/news.gif', $files));
+        $this->assertTrue(in_array('/pics/news with spaces.gif', $files));
+    }
+
     public function test_question_bank_conversion() {
         global $CFG;
 
index 7169e19..e5e1691 100644 (file)
@@ -859,7 +859,7 @@ class backup_gradebook_structure_step extends backup_structure_step {
         $grade_category   = new backup_nested_element('grade_category', array('id'), array(
                 //'courseid',
                 'parent', 'depth', 'path', 'fullname', 'aggregation', 'keephigh',
-                'dropload', 'aggregateonlygraded', 'aggregateoutcomes', 'aggregatesubcats',
+                'droplow', 'aggregateonlygraded', 'aggregateoutcomes', 'aggregatesubcats',
                 'timecreated', 'timemodified', 'hidden'));
 
         $letters = new backup_nested_element('grade_letters');
index dac296a..102a167 100644 (file)
@@ -129,11 +129,6 @@ class block_glossary_random extends block_base {
         $course = $this->page->course;
         $modinfo = get_fast_modinfo($course);
         $glossaryid = $this->config->glossary;
-        $cm = $modinfo->instances['glossary'][$glossaryid];
-
-        if (!has_capability('mod/glossary:view', get_context_instance(CONTEXT_MODULE, $cm->id))) {
-            return '';
-        }
 
         if (!isset($modinfo->instances['glossary'][$glossaryid])) {
             // we can get here if the glossary has been deleted, so
@@ -147,6 +142,12 @@ class block_glossary_random extends block_base {
             return $this->content;
         }
 
+        $cm = $modinfo->instances['glossary'][$glossaryid];
+
+        if (!has_capability('mod/glossary:view', get_context_instance(CONTEXT_MODULE, $cm->id))) {
+            return '';
+        }
+
         if (empty($this->config->cache)) {
             $this->config->cache = '';
         }
index b7f6589..3505c6c 100644 (file)
@@ -70,8 +70,8 @@ echo $OUTPUT->heading(get_string('assignto', 'cohort', format_string($cohort->na
 echo $OUTPUT->notification(get_string('removeuserwarning', 'core_cohort'));
 
 // Get the user_selector we will need.
-$potentialuserselector = new cohort_candidate_selector('addselect', array('cohortid'=>$cohort->id));
-$existinguserselector = new cohort_existing_selector('removeselect', array('cohortid'=>$cohort->id));
+$potentialuserselector = new cohort_candidate_selector('addselect', array('cohortid'=>$cohort->id, 'accesscontext'=>$context));
+$existinguserselector = new cohort_existing_selector('removeselect', array('cohortid'=>$cohort->id, 'accesscontext'=>$context));
 
 // Process incoming user assignments to the cohort
 
index e086dcf..f315d4e 100644 (file)
@@ -254,7 +254,7 @@ $parentlist = array();
 $displaylist[0] = get_string('top');
 make_categories_list($displaylist, $parentlist);
 
-echo '<table class="generalbox editcourse boxaligncenter"><tr class="header">';
+echo '<table class="generaltable editcourse boxaligncenter"><tr class="header">';
 echo '<th class="header" scope="col">'.$strcategories.'</th>';
 echo '<th class="header" scope="col">'.$strcourses.'</th>';
 echo '<th class="header" scope="col">'.$stredit.'</th>';
index 2206a5e..a980841 100644 (file)
@@ -188,17 +188,19 @@ class core_course_renderer extends plugin_renderer_base {
         // Put all options into one tag 'alloptions' to allow us to handle scrolling
         $formcontent .= html_writer::start_tag('div', array('class' => 'alloptions'));
 
-        // Activities
-        $activities = array_filter($modules,
-                create_function('$mod', 'return ($mod->archetype !== MOD_CLASS_RESOURCE);'));
+         // Activities
+        $activities = array_filter($modules, function($mod) {
+            return ($mod->archetype !== MOD_ARCHETYPE_RESOURCE && $mod->archetype !== MOD_ARCHETYPE_SYSTEM);
+        });
         if (count($activities)) {
             $formcontent .= $this->course_modchooser_title('activities');
             $formcontent .= $this->course_modchooser_module_types($activities);
         }
 
         // Resources
-        $resources = array_filter($modules,
-                create_function('$mod', 'return ($mod->archetype === MOD_CLASS_RESOURCE);'));
+        $resources = array_filter($modules, function($mod) {
+            return ($mod->archetype === MOD_ARCHETYPE_RESOURCE);
+        });
         if (count($resources)) {
             $formcontent .= $this->course_modchooser_title('resources');
             $formcontent .= $this->course_modchooser_module_types($resources);
index 73762e2..716d2a2 100644 (file)
@@ -151,7 +151,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
             if(! @unlink($filename)) {
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = get_admin();
                 $eventdata->userto            = get_admin();
@@ -169,7 +169,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
                 // Send mail to admin
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = get_admin();
                 $eventdata->userto            = get_admin();
@@ -280,7 +280,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
 
                 $eventdata = new stdClass();
                 $eventdata->modulename        = 'moodle';
-                $eventdata->component         = 'course';
+                $eventdata->component         = 'enrol_flatfile';
                 $eventdata->name              = 'flatfile_enrolment';
                 $eventdata->userfrom          = $teacher;
                 $eventdata->userto            = $user;
@@ -303,7 +303,7 @@ class enrol_flatfile_plugin extends enrol_plugin {
 
                     $eventdata = new stdClass();
                     $eventdata->modulename        = 'moodle';
-                    $eventdata->component         = 'course';
+                    $eventdata->component         = 'enrol_flatfile';
                     $eventdata->name              = 'flatfile_enrolment';
                     $eventdata->userfrom          = $user;
                     $eventdata->userto            = $teacher;
index fafbfd7..1a98a46 100644 (file)
@@ -431,7 +431,7 @@ class course_enrolment_table extends html_table implements renderable {
 
         $this->page           = optional_param(self::PAGEVAR, 0, PARAM_INT);
         $this->perpage        = optional_param(self::PERPAGEVAR, self::DEFAULTPERPAGE, PARAM_INT);
-        $this->sort           = optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHA);
+        $this->sort           = optional_param(self::SORTVAR, self::DEFAULTSORT, PARAM_ALPHANUM);
         $this->sortdirection  = optional_param(self::SORTDIRECTIONVAR, self::DEFAULTSORTDIRECTION, PARAM_ALPHA);
 
         $this->attributes = array('class'=>'userenrolment');
index a18d0c1..4ca8065 100644 (file)
@@ -48,7 +48,7 @@ require_capability('moodle/grade:manage', $context);
 
 // default return url
 $gpr = new grade_plugin_return();
-$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report.php?id='.$course->id);
+$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report/index.php?id='.$course->id);
 
 if (!$grade_item = grade_item::fetch(array('id'=>$id, 'courseid'=>$course->id))) {
     print_error('invaliditemid');
index d44b2ca..2d61e68 100644 (file)
@@ -57,7 +57,7 @@ if (!has_capability('moodle/grade:manage', $context)) {
 
 // default return url
 $gpr = new grade_plugin_return();
-$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report.php?id='.$course->id);
+$returnurl = $gpr->get_return_url($CFG->wwwroot.'/grade/report/index.php?id='.$course->id);
 
 // security checks!
 if (!empty($id)) {
index 4549995..2770aed 100644 (file)
@@ -107,7 +107,7 @@ grade_regrade_final_grades($courseid);
 
 // Perform actions
 if (!empty($target) && !empty($action) && confirm_sesskey()) {
-    grade_report_grader::process_action($target, $action);
+    grade_report_grader::do_process_action($target, $action);
 }
 
 $reportname = get_string('pluginname', 'gradereport_grader');
index 3dbac53..3b4cc42 100644 (file)
@@ -1580,13 +1580,17 @@ class grade_report_grader extends grade_report {
         return $icon;
     }
 
+    public function process_action($target, $action) {
+        return self::do_process_action($target, $action);
+    }
+
     /**
      * Processes a single action against a category, grade_item or grade.
      * @param string $target eid ({type}{id}, e.g. c4 for category4)
      * @param string $action Which action to take (edit, delete etc...)
      * @return
      */
-    public function process_action($target, $action) {
+    public static function do_process_action($target, $action) {
         // TODO: this code should be in some grade_tree static method
         $targettype = substr($target, 0, 1);
         $targetid = substr($target, 1);
diff --git a/install/lang/en_fix/langconfig.php b/install/lang/en_fix/langconfig.php
deleted file mode 100644 (file)
index d563203..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-<?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/>.
-
-/**
- * Automatically generated strings for Moodle installer
- *
- * Do not edit this file manually! It contains just a subset of strings
- * needed during the very first steps of installation. This file was
- * generated automatically by export-installer.php (which is part of AMOS
- * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
- * list of strings defined in /install/stringnames.txt.
- *
- * @package   installer
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-defined('MOODLE_INTERNAL') || die();
-
-$string['thislanguage'] = 'English (fixes)';
index 62aec64..17b22ef 100644 (file)
@@ -31,6 +31,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 $string['language'] = 'Jezik';
-$string['next'] = 'Nastavi';
+$string['next'] = 'Nastavite';
 $string['previous'] = 'Prethodni';
 $string['reload'] = 'Učitaj ponovno';
diff --git a/install/lang/lt/install.php b/install/lang/lt/install.php
new file mode 100644 (file)
index 0000000..cf54d95
--- /dev/null
@@ -0,0 +1,99 @@
+<?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/>.
+
+/**
+ * Automatically generated strings for Moodle installer
+ *
+ * Do not edit this file manually! It contains just a subset of strings
+ * needed during the very first steps of installation. This file was
+ * generated automatically by export-installer.php (which is part of AMOS
+ * {@link http://docs.moodle.org/dev/Languages/AMOS}) using the
+ * list of strings defined in /install/stringnames.txt.
+ *
+ * @package   installer
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+$string['admindirname'] = 'Administratoriaus aplankas';
+$string['availablelangs'] = 'Galimų kalbų sąrašas';
+$string['chooselanguagehead'] = 'Pasirinkite kalbą';
+$string['chooselanguagesub'] = 'Prašome pasirinkti diegimo kalbą. Ši kalba taip pat taps numatytąją svetainės kalba. Tačiau vėliau, jei norėsite, Jūs galėsite ją pakeisti.';
+$string['clialreadyconfigured'] = 'config.php failas jau egzistuoja. Prašome naudotis admin/cli/install_database.php, jei Jūs norite įdiegti šią svetainę.';
+$string['clialreadyinstalled'] = 'config.php failas jau egzistuoja. Prašome naudoti admin/cli/upgrade.php jei Jūs norite išplėtoti šią svetainę.';
+$string['cliinstallheader'] = '"Moodle" {$a} komandinės eilutės įdiegimo programa';
+$string['databasehost'] = 'Duomenų bazės serveris';
+$string['databasename'] = 'Duomenų bazės pavadinimas';
+$string['databasetypehead'] = 'Pasirinkite duomenų bazės tvarkyklę';
+$string['dataroot'] = 'Duomenų aplankas';
+$string['datarootpermission'] = 'Duomenų aplanko leidimai';
+$string['dbprefix'] = 'Lentelių priešdėlis';
+$string['dirroot'] = '"Moodle" aplankas';
+$string['environmenthead'] = 'Tikrinama Jūsų aplinka...';
+$string['environmentsub2'] = 'Kiekviena "Moodle" laida turi kai kuriuos minimalius reikalavimus PHP versijai ir tam tikrą privalomų PHP plėtinių skaičių.
+Pilnas aplinkos patikrinimas yra padaromas prieš kiekvieną įdiegimą ar plėtotę. Prašome susisiekti su serverio administratoriumi, jeigu Jūs nežinote kaip įdiegti naują versiją ar įgalinti PHP plėtinius.';
+$string['errorsinenvironment'] = 'Aplinkos patikrinimas nesėkmingas!';
+$string['installation'] = 'Įdiegimas';
+$string['langdownloaderror'] = 'Deja "{$a}" kalba negali būti parsiųsta. Diegimo procesas bus tęsiamas anglų kalba.';
+$string['memorylimithelp'] = '<p>Šiuo metu Jūsų serverio PHP atminties limitas yra {$a}.</p>
+
+<p>Vėliau tai gali sukelti atminties trūkumo problemų "Moodle", ypač jei Jūs turite daug įgalintų modulių ir/ar daug vartotojų.</p>
+
+<p>Mes rekomenduojame suderinti PHP taip, kad jis turėtų kiek galima didesnį limitą, pvz.: 40MB.
+ Tam yra keletas būdų, kuriuos Jūs galite pabandyti:</p>
+<ol>
+<li>Jei Jūs galite, sukompiliuokite iš naujo PHP panaudodami <i>--enable-memory-limit</i> raktą.
+   Tai leis nustatyti atminties limitą pačiai "Moodle".</li>
+<li>Jei Jūs turite galimybę koreguoti Jūsų "php.ini" failą, tada pakeiskite <b>memory_limit</b> nuostatą į kažką artimo 40MB. Jei Jūs negalite koreguoti, tuomet gal galite paprašyti, kad Jūsų serverio administratorius tai padarytų už Jus.</li>
+<li>Kai kuriuose PHP serveriuose Jūs galite sukurti ".htaccess" failą "Moodle" aplanke ir įrašykite į jį šią eilutę:
+<blockquote><div>php_value memory_limit 40M</div></blockquote>
+<p>Tačiau kai kuriuose serveriuose tai neleis veikti <b>visiems</b> PHP puslapiams (Jūs matysite klaidas bandydami peržiūrėti tinklapius). Taigi Jums gali tekti pašalinti ".htaccess" failą.</p></li>
+</ol>';
+$string['paths'] = 'Keliai';
+$string['pathserrcreatedataroot'] = 'Diegiklis negali sukurti duomenų katalogo ({$a->dataroot}).';
+$string['pathshead'] = 'Patvirtinkite kelius';
+$string['pathsrodataroot'] = 'Dataroot katalogas yra neįrašomas';
+$string['pathsroparentdataroot'] = 'Virškatalogis ({$a->parent}) yra neįrašomas. Diegiklis negali sukurti duomenų katalogo ({$a->dataroot}).';
+$string['pathssubadmindir'] = 'Nedaugelis interneto paslaugų tiekėjų naudoja /admin kaip specialų URL skirtą prisijungti prie valdymo skydo ar panašiai. Deja tai kelia konfliktus su įprastais Moodle administravimo puslapių talpinimo keliais. Jūs galite tai pataisyti
+pervardydami admin katalogą Jūsų diegime bei įrašydami naują pavadinimą čia. Pavyzdžiui: <em>moodleadmin</em>. Tai pakeis visas admin nuorodas Moodle diegime.';
+$string['pathssubdataroot'] = 'Jums reikia vietos kur Moodle gali išsaugoti įkeliamus failus. Šis katalogas turi būti skaitomas IR ĮRAŠOMAS web serverio naudotojo
+(dažniausiai tai \'nobody\' arba \'apache\'), tačiau jis neturi būti pasiekiamas tiesiogiai per internetą. Diegiklis pabandys sukurti katalogą, jei tokio nėra.';
+$string['pathssubdirroot'] = 'Pilnas kelias iki Moodle diegimo vietos.';
+$string['pathssubwwwroot'] = 'Pilnas internetinis adresas, kuriuo bus pasiekiamas Moodle.
+Pasiekti Moodle naudojantis keliais adresais yra neįmanoma.
+Jei Jūsų svetainėje yra keletas viešų adresų, Jūs turite visuose adresuose nustatyti pastovų peradresavimą į šį adresą.
+Jei Jūsų svetainė yra pasiekiama ir iš Intraneto, ir iš Interneto - panaudokite viešą adresą čia ir nustatykite DNS taip, kad Intraneto naudotojai taip pat galėtų matyti viešą adresą.
+Jei adresas neteisingas - prašome pakeisti URL Jūsų naršyklėje ir pradėti diegimą su nauju adresu.';
+$string['pathsunsecuredataroot'] = 'Dataroot katalogo vieta yra nesaugi.';
+$string['pathswrongadmindir'] = 'Admin katalogas neegzistuoja';
+$string['phpextension'] = '{$a} PHP plėtinys';
+$string['phpversion'] = 'PHP versija';
+$string['phpversionhelp'] = '<p>Moodle reikalauja, kad būtų įdiegta bent 4.3.0 arba 5.1.0 PHP versija (5.0.x versija turi keletą žymių problemų).
+</p>
+<p>Jūs šiuo metu naudojate {$a} versiją</p>
+<p>Jūs turite išplėtoti turimą PHP versiją iki naujesnės arba persikelti pas kitą interneto paslaugų tiekėją, turintį naujesnę PHP versiją!<br/>
+(Turint 5.0.x versiją, Jūs turėtumėte pereiti prie žemesnės 4.4.x versijos)</p>';
+$string['welcomep10'] = '{$a->installername} ({$a->installerversion})';
+$string['welcomep20'] = 'Jūs matote šį puslapį, nes sėkmingai įdiegėte ir užkrovėte <strong>{$a->packname} {$a->packversion}</strong> paketą savo kompiuteryje.
+Sveikiname!';
+$string['welcomep30'] = 'Šis <strong>{$a->installername}</strong> leidimas turi programas skirtas sukurti aplinką, kurioje <strong>Moodle</strong> veiks. Būtent:';
+$string['welcomep40'] = 'Šis paketas taip pat turi  <strong>Moodle {$a->moodlerelease} ({$a->moodleversion})</strong>.';
+$string['welcomep50'] = 'Pakete esančių programų naudojimas yra reguliuojamas atitinkamų licencijų. Pilnas <strong>{$a->installername}</strong> paketas  yra <a href="http://www.opensource.org/docs/definition_plain.html">atviro kodo</a> ir platinamas remiantis <a href="http://www.gnu.org/copyleft/gpl.html">GPL</a> licencija.';
+$string['welcomep60'] = 'Sekantys puslapiai ves Jus per keletą lengvų žingsnių, kurie padės sukonfigūruoti ir nustatyti <strong>Moodle</strong> Jūsų kompiuteryje. Jūs galite priimti nustatymus pagal nutylėjimą arba, pasirinktinai, pakeisti juos pagal savo poreikius.';
+$string['welcomep70'] = 'Spauskite "Toliau" mygtuką, norėdami tęsti <strong>Moodle</strong> nustatymą.';
+$string['wwwroot'] = 'Interneto adresas';
index 32415de..ddfffbe 100644 (file)
@@ -51,12 +51,12 @@ $string['errorsinenvironment'] = '環境檢查失敗!';
 $string['installation'] = '安裝';
 $string['langdownloaderror'] = '很不幸地,語言“{$a}”並未安裝。安裝過程將以英文繼續。';
 $string['memorylimithelp'] = '<p>PHP記憶體上限目前設定為{$a}。</p>
-<p>稍後它可能會造成Moodle記憶體的問題,尤其是您啟動了很多的模組及大量的使用者後。
-<p>建議您儘可能將PHP的上限設得高一點,比如16M。
+<p>稍後它可能會造成Moodle記憶體的問題,尤其是您啟動了很多的模組及大量的用戶之後。
+<p>建議您儘可能將PHP的上限設得高一點,比如40M。
 以下有幾種方式您可以試試:
 <ol>
 <li>如果可以的話,用<i>--enable-memory-limit</i>重新編譯PHP。讓 Moodle 自己設定記憶體上限。
-<li>如果您要使用 php.ini 檔,您可以改變<b>memory_limit</b>這個設定值,例如到16M。如果您無法使用這個檔,您可以請您的管理者幫您做
+<li>如果您要使用 php.ini 檔,您可以改變<b>memory_limit</b>這個設定值,例如到40M。如果您無法使用這個檔,您可以請您的管理者幫您做
 <li>在一些PHP伺服器上,您可以在Moodle目錄下,建立 .htaccess 檔,包含這行:<p><blockquote>php_value memory_limit 16M</blockquote></p>
 <p>然而,在一些伺服器上,這將造成<b>所有的</b> PHP 網頁無法運作(當您看這些網頁時,您就會看到錯誤) 因此,您就必須將 .htaccess 檔案移除。
 </ol>';
@@ -68,7 +68,7 @@ $string['pathsroparentdataroot'] = '上層目錄({$a->parent})是不可寫入的
 $string['pathssubadmindir'] = '有些網站主機使用/admin這個網址來瀏覽控制面版或其他功能。很不幸,這個設定和Moodle管理頁面的標準路徑產生衝突。這個問題可以解決,只需在您的安裝目錄中把admin更換名稱,然後把新名稱輸入到這裡。例如<em>moodleadmin</em>這麼做會改變Moodle中的管理連接。';
 $string['pathssubdataroot'] = '你需要有一個地方讓Moodle可以儲存上傳的檔案。這一目錄對於網頁伺服器用戶(通常是"nobody"或"apache")而言,應該是可讀的和<b>可寫的</b>。但是它必須不能經由網頁直接存取。若此目錄不存在,這安裝程式將會試著建立它。';
 $string['pathssubdirroot'] = 'Moodle安裝的完整目錄路徑。';
-$string['pathssubwwwroot'] = '可以瀏覽到Moodle的完整網址。Moodle不支援透過多個網址瀏覽,如果您的往佔有多個公開網址,您必須把這個網址以外的網址都設定為永久重新導向。如果您的網站可以透過內部網址瀏覽,有可以透過這個公開網址瀏覽,那麼請設定DNS內部使網路使用者也能使用的空該網址。如果此網址不正確,請在瀏覽器中修改URL來重新安裝,並設定另一個網址。';
+$string['pathssubwwwroot'] = '可以瀏覽到Moodle的完整網址。Moodle不支援透過多個網址瀏覽,如果您的網站有多個公開網址,您必須把這個網址以外的網址都設定為永久重新導向。如果您的網站可以透過內部網址瀏覽,有可以透過這個公開網址瀏覽,那麼請設定DNS使網內用戶也能使用這公開的網址。如果此網址不正確,請在你的瀏覽器中修改URL來重新安裝,並設定另一個網址。';
 $string['pathsunsecuredataroot'] = '資料根(Dataroot)目錄的位置不安全';
 $string['pathswrongadmindir'] = '管理目錄不存在';
 $string['phpextension'] = '{$a} PHP擴展';
index e2eb6d1..d603634 100644 (file)
@@ -50,6 +50,7 @@ Modified:
  * string processing - uses our lang framework
  * form hacks
  * MDL-27890 - allow editor to be smaller
+ * MDL-25736 - French spellchecker fixes.
 
  TODO:
  * update strings to integrate with AMOS
index afb60da..e3acf2d 100644 (file)
@@ -39,7 +39,7 @@ class GoogleSpell extends SpellChecker {
                $matches = $this->_getMatches($lang, $word);\r
 \r
                if (count($matches) > 0)\r
-                       $sug = explode("\t", utf8_encode($this->_unhtmlentities($matches[0][4])));\r
+                       $sug = explode("\t", $this->_unhtmlentities($matches[0][4]));\r
 \r
                // Remove empty\r
                foreach ($sug as $item) {\r
index 3ace654..4b1307e 100644 (file)
@@ -81,11 +81,19 @@ class google_docs {
         if ($search) {
             $url.='?q='.urlencode($search);
         }
-        $content = $this->googleoauth->get($url);
-
-        $xml = new SimpleXMLElement($content);
 
         $files = array();
+        $content = $this->googleoauth->get($url);
+        try {
+            if (strpos($content, '<?xml') !== 0) {
+                throw new moodle_exception('invalidxmlresponse');
+            }
+            $xml = new SimpleXMLElement($content);
+        } catch (Exception $e) {
+            // An error occured while trying to parse the XML, let's just return nothing. SimpleXML does not
+            // return a more specific Exception, that's why the global Exception class is caught here.
+            return $files;
+        }
         foreach ($xml->entry as $gdoc) {
             $docid  = (string) $gdoc->children('http://schemas.google.com/g/2005')->resourceId;
             list($type, $docid) = explode(':', $docid);
@@ -317,10 +325,19 @@ class google_picasa {
      * @return mixes $files Array in the format get_listing uses for folders
      */
     public function get_albums() {
+        $files = array();
         $content = $this->googleoauth->get(self::LIST_ALBUMS_URL);
-        $xml = new SimpleXMLElement($content);
 
-        $files = array();
+        try {
+            if (strpos($content, '<?xml') !== 0) {
+                throw new moodle_exception('invalidxmlresponse');
+            }
+            $xml = new SimpleXMLElement($content);
+        } catch (Exception $e) {
+            // An error occured while trying to parse the XML, let's just return nothing. SimpleXML does not
+            // return a more specific Exception, that's why the global Exception class is caught here.
+            return $files;
+        }
 
         foreach ($xml->entry as $album) {
             $gphoto = $album->children('http://schemas.google.com/photos/2007');
@@ -338,7 +355,6 @@ class google_picasa {
                 'thumbnail_height' => 160,
                 'children' => array(),
             );
-
         }
 
         return $files;
@@ -352,12 +368,20 @@ class google_picasa {
      * @return mixed $files A list of files for the file picker
      */
     public function get_photo_details($rawxml) {
+        $files = array();
 
-        $xml = new SimpleXMLElement($rawxml);
+        try {
+            if (strpos($rawxml, '<?xml') !== 0) {
+                throw new moodle_exception('invalidxmlresponse');
+            }
+            $xml = new SimpleXMLElement($rawxml);
+        } catch (Exception $e) {
+            // An error occured while trying to parse the XML, let's just return nothing. SimpleXML does not
+            // return a more specific Exception, that's why the global Exception class is caught here.
+            return $files;
+        }
         $this->lastalbumname = (string)$xml->title;
 
-        $files = array();
-
         foreach ($xml->entry as $photo) {
             $gphoto = $photo->children('http://schemas.google.com/photos/2007');
 
index 12afe84..8378b78 100644 (file)
@@ -1423,30 +1423,33 @@ class grade_item extends grade_object {
      * Refetch grades from modules, plugins.
      *
      * @param int $userid optional, limit the refetch to a single user
+     * @return bool Returns true on success or if there is nothing to do
      */
     public function refresh_grades($userid=0) {
         global $DB;
         if ($this->itemtype == 'mod') {
             if ($this->is_outcome_item()) {
                 //nothing to do
-                return;
+                return true;
             }
 
             if (!$activity = $DB->get_record($this->itemmodule, array('id' => $this->iteminstance))) {
                 debugging("Can not find $this->itemmodule activity with id $this->iteminstance");
-                return;
+                return false;
             }
 
             if (!$cm = get_coursemodule_from_instance($this->itemmodule, $activity->id, $this->courseid)) {
                 debugging('Can not find course module');
-                return;
+                return false;
             }
 
             $activity->modname    = $this->itemmodule;
             $activity->cmidnumber = $cm->idnumber;
 
-            grade_update_mod_grades($activity);
+            return grade_update_mod_grades($activity, $userid);
         }
+
+        return true;
     }
 
     /**
index dab82c2..78bfa20 100644 (file)
@@ -58,6 +58,7 @@ class grade_item_testcase extends grade_base_testcase {
         $this->sub_test_grade_item_is_course_item();
         $this->sub_test_grade_item_fetch_course_item();
         $this->sub_test_grade_item_depends_on();
+        $this->sub_test_refresh_grades();
         $this->sub_test_grade_item_is_calculated();
         $this->sub_test_grade_item_set_calculation();
         $this->sub_test_grade_item_get_calculation();
@@ -483,6 +484,13 @@ class grade_item_testcase extends grade_base_testcase {
         $this->assertEquals($res, $deps);
     }
 
+    protected function sub_test_refresh_grades() {
+        // Testing with the grade item for a mod_assignment instance.
+        $grade_item = new grade_item($this->grade_items[0], false);
+        $this->assertTrue(method_exists($grade_item, 'refresh_grades'));
+        $this->assertTrue($grade_item->refresh_grades());
+    }
+
     protected function sub_test_grade_item_is_calculated() {
         $grade_item = new grade_item($this->grade_items[1], false);
         $this->assertTrue(method_exists($grade_item, 'is_calculated'));
index 8de67cf..c59bd1c 100644 (file)
@@ -1190,7 +1190,7 @@ function grade_update_mod_grades($modinstance, $userid=0) {
         $updategradesfunc($modinstance, $userid);
 
     } else {
-        // mudule does not support grading??
+        // Module does not support grading?
     }
 
     return true;
index 1fee1b0..e2ffd29 100644 (file)
@@ -129,10 +129,11 @@ function message_send($eventdata) {
         if (isset($defaultpreferences->{$defaultpreference})) {
             $permitted = $defaultpreferences->{$defaultpreference};
         } else {
-            //MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
-            //exist in the message_provider table (thus there is no default settings for them)
-            $preferrormsg = get_string('couldnotfindpreference', 'message', $defaultpreference);
-            throw new coding_exception($preferrormsg,'blah');
+            // MDL-25114 They supplied an $eventdata->component $eventdata->name combination which doesn't
+            // exist in the message_provider table (thus there is no default settings for them).
+            $preferrormsg = "Could not load preference $defaultpreference. Make sure the component and name you supplied
+                    to message_send() are valid.";
+            throw new coding_exception($preferrormsg);
         }
 
         // Find out if user has configured this output
@@ -387,32 +388,98 @@ function message_get_my_providers() {
 function message_get_providers_for_user($userid) {
     global $DB, $CFG;
 
-    $systemcontext = get_context_instance(CONTEXT_SYSTEM);
-
     $providers = get_message_providers();
 
-    // Remove all the providers we aren't allowed to see now
-    foreach ($providers as $providerid => $provider) {
-        if (!empty($provider->capability)) {
-            if (!has_capability($provider->capability, $systemcontext, $userid)) {
-                unset($providers[$providerid]);   // Not allowed to see this
-                continue;
+    // Ensure user is not allowed to configure instantmessage if it is globally disabled.
+    if (!$CFG->messaging) {
+        foreach ($providers as $providerid => $provider) {
+            if ($provider->name == 'instantmessage') {
+                unset($providers[$providerid]);
+                break;
             }
         }
+    }
 
-        // Ensure user is not allowed to configure instantmessage if it is globally disabled.
-        if (!$CFG->messaging && $provider->name == 'instantmessage') {
+    // If the component is an enrolment plugin, check it is enabled
+    foreach ($providers as $providerid => $provider) {
+        list($type, $name) = normalize_component($provider->component);
+        if ($type == 'enrol' && !enrol_is_enabled($name)) {
             unset($providers[$providerid]);
+        }
+    }
+
+    // Now we need to check capabilities. We need to eliminate the providers
+    // where the user does not have the corresponding capability anywhere.
+    // Here we deal with the common simple case of the user having the
+    // capability in the system context. That handles $CFG->defaultuserroleid.
+    // For the remaining providers/capabilities, we need to do a more complex
+    // query involving all overrides everywhere.
+    $unsureproviders = array();
+    $unsurecapabilities = array();
+    $systemcontext = context_system::instance();
+    foreach ($providers as $providerid => $provider) {
+        if (empty($provider->capability) || has_capability($provider->capability, $systemcontext, $userid)) {
+            // The provider is relevant to this user.
             continue;
         }
 
-        // If the component is an enrolment plugin, check it is enabled
-        list($type, $name) = normalize_component($provider->component);
-        if ($type == 'enrol') {
-            if (!enrol_is_enabled($name)) {
-                unset($providers[$providerid]);
-                continue;
-            }
+        $unsureproviders[$providerid] = $provider;
+        $unsurecapabilities[$provider->capability] = 1;
+        unset($providers[$providerid]);
+    }
+
+    if (empty($unsureproviders)) {
+        // More complex checks are not required.
+        return $providers;
+    }
+
+    // Now check the unsure capabilities.
+    list($capcondition, $params) = $DB->get_in_or_equal(
+            array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+    $params['userid'] = $userid;
+
+    $sql = "SELECT DISTINCT rc.capability, 1
+
+              FROM {role_assignments} ra
+              JOIN {context} actx ON actx.id = ra.contextid
+              JOIN {role_capabilities} rc ON rc.roleid = ra.roleid
+              JOIN {context} cctx ON cctx.id = rc.contextid
+
+             WHERE ra.userid = :userid
+               AND rc.capability $capcondition
+               AND rc.permission > 0
+               AND (".$DB->sql_concat('actx.path', "'/'")." LIKE ".$DB->sql_concat('cctx.path', "'/%'").
+               " OR ".$DB->sql_concat('cctx.path', "'/'")." LIKE ".$DB->sql_concat('actx.path', "'/%'").")";
+
+    if (!empty($CFG->defaultfrontpageroleid)) {
+        $frontpagecontext = context_course::instance(SITEID);
+
+        list($capcondition2, $params2) = $DB->get_in_or_equal(
+                array_keys($unsurecapabilities), SQL_PARAMS_NAMED);
+        $params = array_merge($params, $params2);
+        $params['frontpageroleid'] = $CFG->defaultfrontpageroleid;
+        $params['frontpagepathpattern'] = $frontpagecontext->path . '/';
+
+        $sql .= "
+             UNION
+
+            SELECT DISTINCT rc.capability, 1
+
+              FROM {role_capabilities} rc
+              JOIN {context} cctx ON cctx.id = rc.contextid
+
+             WHERE rc.roleid = :frontpageroleid
+               AND rc.capability $capcondition2
+               AND rc.permission > 0
+               AND ".$DB->sql_concat('cctx.path', "'/'")." LIKE :frontpagepathpattern";
+    }
+
+    $relevantcapabilities = $DB->get_records_sql_menu($sql, $params);
+
+    // Add back any providers based on the detailed capability check.
+    foreach ($unsureproviders as $providerid => $provider) {
+        if (array_key_exists($provider->capability, $relevantcapabilities)) {
+            $providers[$providerid] = $provider;
         }
     }
 
index f9dc68d..f5a20f7 100644 (file)
@@ -625,6 +625,14 @@ class phpunit_util {
         $reset = 'reset';
         get_fast_modinfo($reset);
 
+        // Reset other singletons.
+        if (class_exists('plugin_manager')) {
+            plugin_manager::reset_caches(true);
+        }
+        if (class_exists('available_update_checker')) {
+            available_update_checker::reset_caches(true);
+        }
+
         // purge dataroot directory
         self::reset_dataroot();
 
index 5c8417e..5d78cf7 100644 (file)
@@ -88,6 +88,16 @@ class plugin_manager {
         return self::$singletoninstance;
     }
 
+    /**
+     * Reset any caches
+     * @param bool $phpunitreset
+     */
+    public static function reset_caches($phpunitreset = false) {
+        if ($phpunitreset) {
+            self::$singletoninstance = null;
+        }
+    }
+
     /**
      * Returns a tree of known plugins and information about them
      *
@@ -649,6 +659,16 @@ class available_update_checker {
         return self::$singletoninstance;
     }
 
+    /**
+     * Reset any caches
+     * @param bool $phpunitreset
+     */
+    public static function reset_caches($phpunitreset = false) {
+        if ($phpunitreset) {
+            self::$singletoninstance = null;
+        }
+    }
+
     /**
      * Returns the timestamp of the last execution of {@link fetch()}
      *
diff --git a/lib/tests/gradelib_test.php b/lib/tests/gradelib_test.php
new file mode 100644 (file)
index 0000000..659efc7
--- /dev/null
@@ -0,0 +1,51 @@
+<?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 /lib/gradelib.php.
+ *
+ * @package   core_grade
+ * @category  phpunit
+ * @copyright 2012 Andrew Davis
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+global $CFG;
+require_once($CFG->libdir . '/gradelib.php');
+
+class gradelib_testcase extends advanced_testcase {
+
+    public function test_grade_update_mod_grades() {
+
+        $this->resetAfterTest(true);
+
+        // Create a course and instance of mod_assignment.
+        $course = $this->getDataGenerator()->create_course();
+
+        $assigndata['course'] = $course->id;
+        $assigndata['name'] = 'lightwork assignment';
+        $modinstance = self::getDataGenerator()->create_module('assignment', $assigndata);
+
+        // grade_update_mod_grades() requires 2 additional properties, cmidnumber and modname.
+        $cm = get_coursemodule_from_instance('assignment', $modinstance->id, 0, false, MUST_EXIST);
+        $modinstance->cmidnumber = $cm->id;
+        $modinstance->modname = 'assignment';
+
+        $this->assertTrue(grade_update_mod_grades($modinstance));
+    }
+}
diff --git a/lib/tests/messagelib_test.php b/lib/tests/messagelib_test.php
new file mode 100644 (file)
index 0000000..dab7a64
--- /dev/null
@@ -0,0 +1,157 @@
+<?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/>.
+
+/**
+ * Tests for messagelib.php.
+ *
+ * @package    core_message
+ * @copyright  2012 The Open Universtiy
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+class messagelib_testcase extends advanced_testcase {
+
+    public function test_message_get_providers_for_user() {
+        global $CFG, $DB;
+
+        $this->resetAfterTest(true);
+
+        $generator = $this->getDataGenerator();
+
+        // Create a course category and course
+        $cat = $generator->create_category(array('parent' => 0));
+        $course = $generator->create_course(array('category' => $cat->id));
+        $quiz = $generator->create_module('quiz', array('course' => $course->id));
+        $user = $generator->create_user();
+
+        $coursecontext = context_course::instance($course->id);
+        $quizcontext = context_module::instance($quiz->cmid);
+        $frontpagecontext = context_course::instance(SITEID);
+
+        $studentrole = $DB->get_record('role', array('shortname' => 'student'));
+
+        // The user is a student in a course, and has the capability for quiz
+        // confirmation emails in one quiz in that course.
+        role_assign($studentrole->id, $user->id, $coursecontext->id);
+        assign_capability('mod/quiz:emailconfirmsubmission', CAP_ALLOW, $studentrole->id, $quizcontext->id);
+
+        // Give this message type to the front page role.
+        assign_capability('mod/quiz:emailwarnoverdue', CAP_ALLOW, $CFG->defaultfrontpageroleid, $frontpagecontext->id);
+
+        $providers = message_get_providers_for_user($user->id);
+        $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+        $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+        $this->assertTrue($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+
+        // A user is a student in a different course, they should not get confirmation.
+        $course2 = $generator->create_course(array('category' => $cat->id));
+        $user2 = $generator->create_user();
+        $coursecontext2 = context_course::instance($course2->id);
+        role_assign($studentrole->id, $user2->id, $coursecontext2->id);
+        accesslib_clear_all_caches_for_unit_testing();
+        $providers = message_get_providers_for_user($user2->id);
+        $this->assertTrue($this->message_type_present('mod_forum', 'posts', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'confirmation', $providers));
+
+        // Now remove the frontpage role id, and attempt_overdue message should go away.
+        unset_config('defaultfrontpageroleid');
+        accesslib_clear_all_caches_for_unit_testing();
+
+        $providers = message_get_providers_for_user($user->id);
+        $this->assertTrue($this->message_type_present('mod_quiz', 'confirmation', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'attempt_overdue', $providers));
+        $this->assertFalse($this->message_type_present('mod_quiz', 'submission', $providers));
+    }
+
+    public function test_message_get_providers_for_user_more() {
+        global $DB;
+
+        $this->resetAfterTest(true);
+
+        // Create a course
+        $course = $this->getDataGenerator()->create_course();
+        $coursecontext = context_course::instance($course->id);
+
+        // It would probably be better to use a quiz instance as it has capability controlled messages
+        // however mod_quiz doesn't have a data generator
+        // Instead we're going to use backup notifications and give and take away the capability at various levels
+        $assignment = $this->getDataGenerator()->create_module('assignment', array('course'=>$course->id));
+        $modulecontext = context_module::instance($assignment->id);
+
+        // Create and enrol a teacher
+        $teacherrole = $DB->get_record('role', array('shortname'=>'editingteacher'), '*', MUST_EXIST);
+        $teacher = $this->getDataGenerator()->create_user();
+        role_assign($teacherrole->id, $teacher->id, $coursecontext);
+        $enrolplugin = enrol_get_plugin('manual');
+        $enrolplugin->add_instance($course);
+        $enrolinstances = enrol_get_instances($course->id, false);
+        foreach ($enrolinstances as $enrolinstance) {
+            if ($enrolinstance->enrol === 'manual') {
+                break;
+            }
+        }
+        $enrolplugin->enrol_user($enrolinstance, $teacher->id);
+
+        // Make the teacher the current user
+        $this->setUser($teacher);
+
+        // Teacher shouldn't have the required capability so they shouldn't be able to see the backup message
+        $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+        $providers = message_get_providers_for_user($teacher->id);
+        $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+
+        // Give the user the required capability in an activity module
+        // They should now be able to see the backup message
+        assign_capability('moodle/site:config', CAP_ALLOW, $teacherrole->id, $modulecontext->id, true);
+        accesslib_clear_all_caches_for_unit_testing();
+        $modulecontext = context_module::instance($assignment->id);
+        $this->assertTrue(has_capability('moodle/site:config', $modulecontext));
+
+        $providers = message_get_providers_for_user($teacher->id);
+        $this->assertTrue($this->message_type_present('moodle', 'backup', $providers));
+
+        // Prohibit the capability for the user at the course level
+        // This overrules the CAP_ALLOW at the module level
+        // They should not be able to see the backup message
+        assign_capability('moodle/site:config', CAP_PROHIBIT, $teacherrole->id, $coursecontext->id, true);
+        accesslib_clear_all_caches_for_unit_testing();
+        $modulecontext = context_module::instance($assignment->id);
+        $this->assertFalse(has_capability('moodle/site:config', $modulecontext));
+
+        $providers = message_get_providers_for_user($teacher->id);
+        // Actually, handling PROHIBITs would be too expensive. We do not
+        // care if users with PROHIBITs see a few more preferences than they should.
+        // $this->assertFalse($this->message_type_present('moodle', 'backup', $providers));
+    }
+
+    /**
+     * Is a particular message type in the list of message types.
+     * @param string $name a message name.
+     * @param array $providers as returned by message_get_providers_for_user.
+     * @return bool whether the message type is present.
+     */
+    protected function message_type_present($component, $name, $providers) {
+        foreach ($providers as $provider) {
+            if ($provider->component == $component && $provider->name == $name) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
index 5263217..cac237b 100644 (file)
@@ -1215,7 +1215,7 @@ function message_print_search_results($frm, $showicontext=false, $currentuser=nu
                 echo html_writer::end_tag('td');
 
                 echo html_writer::start_tag('td', array('class' => 'summary'));
-                echo message_get_fragment($message->fullmessage, $keywords);
+                echo message_get_fragment($message->smallmessage, $keywords);
                 echo html_writer::start_tag('div', array('class' => 'link'));
 
                 //If the user clicks the context link display message sender on the left
@@ -1600,10 +1600,10 @@ function message_search($searchterms, $fromme=true, $tome=true, $courseid='none'
     ///    c.  Messages to and from user
 
     if ($courseid == SITEID) { /// admin is searching all messages
-        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message_read} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
-        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
 
@@ -1628,10 +1628,10 @@ function message_search($searchterms, $fromme=true, $tome=true, $courseid='none'
             $params['userid'] = $userid;
         }
 
-        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_read   = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message_read} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
-        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.fullmessage, m.timecreated
+        $m_unread = $DB->get_records_sql("SELECT m.id, m.useridto, m.useridfrom, m.smallmessage, m.fullmessage, m.timecreated
                                             FROM {message} m
                                            WHERE $searchcond", $params, 0, MESSAGE_SEARCH_MAX_RESULTS);
 
index 29be4ee..6afdcf7 100644 (file)
@@ -208,6 +208,22 @@ class assign_grading_table extends table_sql implements renderable {
 
     }
 
+    /**
+     * Before adding each row to the table make sure rownum is incremented
+     *
+     * @param array $row row of data from db used to make one row of the table.
+     * @return array one row for the table
+     */
+    function format_row($row) {
+        if ($this->rownum < 0) {
+            $this->rownum = $this->currpage * $this->pagesize;
+        } else {
+            $this->rownum += 1;
+        }
+
+        return parent::format_row($row);
+    }
+
     /**
      * Add the userid to the row class so it can be updated via ajax
      *
@@ -277,7 +293,7 @@ class assign_grading_table extends table_sql implements renderable {
 
 
     /**
-     * Format a user picture for display (and update rownum as a sideeffect)
+     * Format a user picture for display
      *
      * @param stdClass $row
      * @return string
@@ -435,11 +451,6 @@ class assign_grading_table extends table_sql implements renderable {
      */
     function col_userid(stdClass $row) {
         $edit = '';
-        if ($this->rownum < 0) {
-            $this->rownum = $this->currpage * $this->pagesize;
-        } else {
-            $this->rownum += 1;
-        }
 
         $actions = array();
 
index 1e24886..3d44847 100644 (file)
@@ -3050,7 +3050,9 @@ class assign {
         if (!$last){
             $buttonarray[] = $mform->createElement('submit', 'nosaveandnext', get_string('nosavebutnext', 'assign'));
         }
-        $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
+        if (!empty($buttonarray)) {
+            $mform->addGroup($buttonarray, 'navar', '', array(' '), false);
+        }
     }
 
 
index bd31428..e36af7b 100644 (file)
@@ -216,7 +216,7 @@ if (optional_param('sesskey', false, PARAM_BOOL) && confirm_sesskey()) {
         } else if ($action == 'finishimport') {
             $overwritesettings = optional_param('overwritesettings', false, PARAM_BOOL);
             if (!$fullname) {
-                $presetdir = $CFG->tempdir.'/forms/'.required_param('directory', PARAM_ALPHANUMEXT);
+                $presetdir = $CFG->tempdir.'/forms/'.required_param('directory', PARAM_FILE);
                 if (!file_exists($presetdir) || !is_dir($presetdir)) {
                     print_error('cannotimport');
                 }
index e60f43e..63ef013 100644 (file)
@@ -269,12 +269,12 @@ switch ($mode) {
                 // Get all the users who have taken this lesson, order by their last name
                 $ufields = user_picture::fields('u');
                 if (!empty($cm->groupingid)) {
-                    $params["groupinid"] = $cm->groupingid;
+                    $params["groupingid"] = $cm->groupingid;
                     $sql = "SELECT DISTINCT $ufields
                             FROM {lesson_attempts} a
                                 INNER JOIN {user} u ON u.id = a.userid
                                 INNER JOIN {groups_members} gm ON gm.userid = u.id
-                                INNER JOIN {groupings_groups} gg ON gm.groupid = :groupinid
+                                INNER JOIN {groupings_groups} gg ON gm.groupid = gg.groupid AND gg.groupingid = :groupingid
                             WHERE a.lessonid = :lessonid
                             ORDER BY u.lastname";
                 } else {
index 134c14b..9eb2c18 100644 (file)
@@ -262,6 +262,7 @@ function lesson_save_question_options($question, $lesson) {
             // The first answer should always be the correct answer
             $correctanswer = clone($defaultanswer);
             $correctanswer->answer = get_string('thatsthecorrectanswer', 'lesson');
+            $correctanswer->jumpto = LESSON_NEXTPAGE;
             $DB->insert_record("lesson_answers", $correctanswer);
 
             // The second answer should always be the wrong answer
index be061bb..813f046 100644 (file)
@@ -1024,6 +1024,8 @@ function quiz_process_options($quiz) {
         $quiz->feedbackboundaries[-1] = $quiz->grade + 1;
         $quiz->feedbackboundaries[$numboundaries] = 0;
         $quiz->feedbackboundarycount = $numboundaries;
+    } else {
+        $quiz->feedbackboundarycount = -1;
     }
 
     // Combing the individual settings into the review columns.
diff --git a/mod/quiz/tests/generator/lib.php b/mod/quiz/tests/generator/lib.php
new file mode 100644 (file)
index 0000000..24ed645
--- /dev/null
@@ -0,0 +1,106 @@
+<?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/>.
+
+defined('MOODLE_INTERNAL') || die();
+
+/**
+ * Quiz module test data generator class
+ *
+ * @package mod_quiz
+ * @copyright 2012 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class mod_quiz_generator extends phpunit_module_generator {
+
+    /**
+     * Create new quiz module instance.
+     * @param array|stdClass $record
+     * @param array $options (mostly course_module properties)
+     * @return stdClass activity record with extra cmid field
+     */
+    public function create_instance($record = null, array $options = null) {
+        global $CFG;
+        require_once("$CFG->dirroot/mod/quiz/locallib.php");
+
+        $this->instancecount++;
+        $i = $this->instancecount;
+
+        $record = (object)(array)$record;
+        $options = (array)$options;
+
+        if (empty($record->course)) {
+            throw new coding_exception('module generator requires $record->course');
+        }
+        if (isset($options['idnumber'])) {
+            $record->cmidnumber = $options['idnumber'];
+        } else {
+            $record->cmidnumber = '';
+        }
+
+        $alwaysvisible = mod_quiz_display_options::DURING | mod_quiz_display_options::IMMEDIATELY_AFTER |
+                mod_quiz_display_options::LATER_WHILE_OPEN | mod_quiz_display_options::AFTER_CLOSE;
+
+        $defaultquizsettings = array(
+            'name'                   => get_string('pluginname', 'data').' '.$i,
+            'intro'                  => 'Test quiz ' . $i,
+            'introformat'            => FORMAT_MOODLE,
+            'timeopen'               => 0,
+            'timeclose'              => 0,
+            'preferredbehaviour'     => 'deferredfeedback',
+            'attempts'               => 0,
+            'attemptonlast'          => 0,
+            'grademethod'            => QUIZ_GRADEHIGHEST,
+            'decimalpoints'          => 2,
+            'questiondecimalpoints'  => -1,
+            'reviewattempt'          => $alwaysvisible,
+            'reviewcorrectness'      => $alwaysvisible,
+            'reviewmarks'            => $alwaysvisible,
+            'reviewspecificfeedback' => $alwaysvisible,
+            'reviewgeneralfeedback'  => $alwaysvisible,
+            'reviewrightanswer'      => $alwaysvisible,
+            'reviewoverallfeedback'  => $alwaysvisible,
+            'questionsperpage'       => 1,
+            'shufflequestions'       => 0,
+            'shuffleanswers'         => 1,
+            'questions'              => '',
+            'sumgrades'              => 0,
+            'grade'                  => 0,
+            'timecreated'            => time(),
+            'timemodified'           => time(),
+            'timelimit'              => 0,
+            'overduehandling'        => 'autoabandon',
+            'graceperiod'            => 86400,
+            'quizpassword'           => '',
+            'subnet'                 => '',
+            'browsersecurity'        => '',
+            'delay1'                 => 0,
+            'delay2'                 => 0,
+            'showuserpicture'        => 0,
+            'showblocks'             => 0,
+            'navmethod'              => QUIZ_NAVMETHOD_FREE,
+        );
+
+        foreach ($defaultquizsettings as $name => $value) {
+            if (!isset($record->{$name})) {
+                $record->{$name} = $value;
+            }
+        }
+
+        $record->coursemodule = $this->precreate_course_module($record->course, $options);
+        $id = quiz_add_instance($record);
+        return $this->post_add_instance($id, $record->coursemodule);
+    }
+}
index 4b0837c..a9f61e6 100644 (file)
@@ -499,7 +499,7 @@ class mod_scorm_mod_form extends moodleform_mod {
         }
 
         // Turn off completion settings if the checkboxes aren't ticked
-        $autocompletion = !empty($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC;
+        $autocompletion = isset($data->completion) && $data->completion == COMPLETION_TRACKING_AUTOMATIC;
 
         if (isset($data->completionstatusrequired) && is_array($data->completionstatusrequired)) {
             $total = 0;
index ea1fbb6..028ca08 100644 (file)
@@ -256,4 +256,4 @@ $string['thanksforanswers'] = 'Thanks for answering this survey, {$a}';
 $string['time'] = 'Time';
 $string['viewsurveyresponses'] = 'View {$a} survey responses';
 $string['notyetanswered'] = 'Not yet answered';
-$string['allquestionrequireanswer'] = 'All questions are required and must be answered';
+$string['allquestionrequireanswer'] = 'All questions are required and must be answered.';
index 6efe849..4c483e2 100644 (file)
@@ -83,7 +83,7 @@ $string['configgradedecimals'] = 'Default number of digits that should be shown
 $string['configgradinggrade'] = 'Default maximum grade for assessment in workshops';
 $string['configmaxbytes'] = 'Default maximum submission file size for all workshops on the site (subject to course limits and other local settings)';
 $string['configstrategy'] = 'Default grading strategy for workshops';
-$string['createsubmission'] = 'Submit';
+$string['createsubmission'] = 'Start preparing your submission';
 $string['daysago'] = '{$a} days ago';
 $string['daysleft'] = '{$a} days left';
 $string['daystoday'] = 'today';
index 1a0e992..45ec077 100644 (file)
@@ -440,7 +440,21 @@ class qformat_xml extends qformat_default {
         $qo->name = $this->clean_question_name($this->import_text($question['#']['name'][0]['#']['text']));
         $qo->questiontextformat = $questiontext['format'];
         $qo->questiontext = $qo->questiontext['text'];
-        $qo->questiontextfiles = array();
+        $qo->questiontextfiles = $this->import_files($this->getpath($question,
+                array('#', 'questiontext', 0, '#', 'file'), array(), false));
+
+        // Backwards compatibility, deal with the old image tag.
+        $filedata = $this->getpath($question, array('#', 'image_base64', '0', '#'), null, false);
+        $filename = $this->getpath($question, array('#', 'image', '0', '#'), null, false);
+        if ($filedata && $filename) {
+            $data = new stdClass();
+            $data->content = $filedata;
+            $data->encoding = 'base64';
+            // Question file areas don't support subdirs, so convert path to filename if necessary.
+            $data->name = clean_param(str_replace('/', '_', $filename), PARAM_FILE);
+            $qo->questiontextfiles[] = $data;
+            $qo->questiontext .= ' <img src="@@PLUGINFILE@@/' . $data->name . '" />';
+        }
 
         // restore files in generalfeedback
         $qo->generalfeedback = $this->getpath($question,
index 85e248f..b58c47a 100644 (file)
@@ -137,6 +137,25 @@ class restore_qtype_match_plugin extends restore_qtype_plugin {
                     array($newquestionid, $data->questiontext, $data->answertext),
                     'id', IGNORE_MULTIPLE);
 
+            // Not able to find the answer, let's try cleaning the answertext
+            // of all the question answers in DB as slower fallback. MDL-36683 / MDL-30018.
+            if (!$sub) {
+                $params = array('question' => $newquestionid);
+                $potentialsubs = $DB->get_records('question_match_sub', array('question' => $newquestionid), '', 'id, questiontext, answertext');
+                foreach ($potentialsubs as $potentialsub) {
+                    // Clean in the same way than {@link xml_writer::xml_safe_utf8()}.
+                    $cleanquestion = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is', '', $potentialsub->questiontext); // Clean CTRL chars.
+                    $cleanquestion = preg_replace("/\r\n|\r/", "\n", $cleanquestion); // Normalize line ending.
+
+                    $cleananswer = preg_replace('/[\x-\x8\xb-\xc\xe-\x1f\x7f]/is', '', $potentialsub->answertext); // Clean CTRL chars.
+                    $cleananswer = preg_replace("/\r\n|\r/", "\n", $cleananswer); // Normalize line ending.
+
+                    if ($cleanquestion === $data->questiontext && $cleananswer == $data->answertext) {
+                        $sub = $potentialsub;
+                    }
+                }
+            }
+
             // Found, let's create the mapping
             if ($sub) {
                 $this->set_mapping('question_match_sub', $oldid, $sub->id);
index dd6a0f8..5ff517b 100644 (file)
@@ -1023,7 +1023,7 @@ M.core_filepicker.init = function(Y, options) {
             var nextpage = this.active_repo.page+1;
             var args = {
                 page: nextpage,
-                repo_id: this.active_repo.id,
+                repo_id: this.active_repo.id
             };
             var action = this.active_repo.issearchresult ? 'search' : 'list';
             this.request({
index 2319332..f757f31 100644 (file)
@@ -137,7 +137,7 @@ abstract class user_selector_base {
      * Clear the list of excluded user ids.
      */
     public function clear_exclusions() {
-        $exclude = array();
+        $this->exclude = array();
     }
 
     /**
index 295fdcc..4fc0668 100644 (file)
 defined('MOODLE_INTERNAL') || die();
 
 
-$version  = 2012062503.00;              // YYYYMMDD      = weekly release date of this DEV branch
+$version  = 2012062503.01;              // YYYYMMDD      = weekly release date of this DEV branch
                                         //         RR    = release increments - 00 in DEV branches
                                         //           .XX = incremental changes
 
-$release  = '2.3.3 (Build: 20121112)';  // Human-friendly version name
+$release  = '2.3.3+ (Build: 20121116)';  // Human-friendly version name
 
 $branch   = '23';                       // this version's branch
 $maturity = MATURITY_STABLE;            // this version's maturity level