Merge branch 'MDL-29115' of git://github.com/rwijaya/moodle
authorSam Hemelryk <sam@moodle.com>
Thu, 24 Nov 2011 22:53:24 +0000 (11:53 +1300)
committerSam Hemelryk <sam@moodle.com>
Thu, 24 Nov 2011 22:53:24 +0000 (11:53 +1300)
86 files changed:
admin/cli/install.php
admin/cli/purge_caches.php [new file with mode: 0644]
admin/tool/langimport/index.php
admin/tool/langimport/lang/en/tool_langimport.php
admin/tool/upgrade.txt [new file with mode: 0644]
auth/mnet/jump.php
backup/moodle2/restore_stepslib.php
backup/restore.php
backup/util/dbops/restore_dbops.class.php
backup/util/ui/renderer.php
backup/util/ui/yui/confirmcancel/confirmcancel.js
blocks/comments/block_comments.php
comment/comment.js
enrol/externallib.php
enrol/imsenterprise/lib.php
filter/mediaplugin/filter.php
filter/upgrade.txt [new file with mode: 0644]
grade/edit/tree/grade.php
grade/edit/tree/lib.php
grade/report/user/lib.php
group/lib.php
install.php
install/stringnames.txt
lang/en/admin.php
lang/en/error.php
lang/en/install.php
lang/en/moodle.php
lang/en/webservice.php
lib/bennu/iCalendar_components.php
lib/bennu/iCalendar_parameters.php
lib/bennu/iCalendar_properties.php
lib/datalib.php
lib/dml/sqlsrv_native_moodle_database.php
lib/editor/tinymce/lib.php
lib/editor/tinymce/settings.php
lib/filestorage/file_storage.php
lib/filterlib.php
lib/form/form.js
lib/formslib.php
lib/gradelib.php
lib/installlib.php
lib/moodlelib.php
lib/questionlib.php
lib/resourcelib.php
lib/rsslib.php
lib/setup.php
lib/setuplib.php
login/token.php
mod/forum/db/access.php
mod/forum/lang/en/forum.php
mod/forum/lib.php
mod/forum/version.php
mod/lesson/lang/en/lesson.php
mod/lesson/mod_form.php
mod/quiz/styles.css
mod/scorm/datamodels/aicclib.php
mod/scorm/datamodels/scorm_12.js.php
question/format.php
question/format/xml/format.php
question/type/questiontypebase.php
question/type/shortanswer/edit_shortanswer_form.php
question/type/shortanswer/questiontype.php
question/type/truefalse/edit_truefalse_form.php
report/backups/index.php
report/configlog/index.php
report/courseoverview/index.php
report/log/index.php
report/loglive/index.php
report/questioninstances/index.php
report/security/index.php
report/stats/index.php
report/upgrade.txt
repository/lib.php
tag/coursetagslib.php
theme/base/style/blocks.css
theme/base/style/core.css
theme/base/style/user.css
theme/canvas/style/text.css
theme/image.php
theme/javascript.php
theme/sky_high/lang/en/theme_sky_high.php
theme/styles.php
user/lib.php
user/message.html
user/messageselect.php
webservice/lib.php

index 68ed892..04d1ee8 100644 (file)
@@ -133,9 +133,6 @@ $CFG->dirroot              = dirname(dirname(dirname(__FILE__)));
 $CFG->libdir               = "$CFG->dirroot/lib";
 $CFG->wwwroot              = "http://localhost";
 $CFG->httpswwwroot         = $CFG->wwwroot;
-$CFG->dataroot             = str_replace('\\', '/', dirname(dirname(dirname(dirname(__FILE__)))).'/moodledata');
-$CFG->tempdir              = $CFG->dataroot.'/temp';
-$CFG->cachedir             = $CFG->dataroot.'/temp';
 $CFG->docroot              = 'http://docs.moodle.org';
 $CFG->running_installer    = true;
 $CFG->early_install_lang   = true;
@@ -186,7 +183,7 @@ list($options, $unrecognized) = cli_get_params(
         'chmod'             => '2777',
         'lang'              => $CFG->lang,
         'wwwroot'           => '',
-        'dataroot'          => $CFG->dataroot,
+        'dataroot'          => str_replace('\\', '/', dirname(dirname(dirname(dirname(__FILE__)))).'/moodledata'),
         'dbtype'            => $defaultdb,
         'dbhost'            => 'localhost',
         'dbname'            => 'moodle',
@@ -270,7 +267,7 @@ if ($interactive) {
 $chmod = octdec(clean_param($options['chmod'], PARAM_INT));
 if ($interactive) {
     cli_separator();
-    cli_heading('Data directories permission'); // todo localize
+    cli_heading(get_string('datarootpermission', 'install'));
     $prompt = get_string('clitypevaluedefault', 'admin', decoct($chmod));
     $error = '';
     do {
@@ -330,9 +327,12 @@ $CFG->httpswwwroot  = $CFG->wwwroot;
 
 
 //We need dataroot before lang download
-if (!empty($options['dataroot'])) {
-    $CFG->dataroot = $options['dataroot'];
+$dataroot = clean_param($options['dataroot'], PARAM_PATH);
+if ($dataroot !== $options['dataroot']) {
+    $a = (object)array('option' => 'dataroot', 'value' => $options['dataroot']);
+    cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 }
+$CFG->dataroot = $dataroot;
 if ($interactive) {
     cli_separator();
     $i=0;
@@ -380,6 +380,8 @@ if ($interactive) {
         cli_error(get_string('pathserrcreatedataroot', 'install', $a));
     }
 }
+$CFG->tempdir  = $CFG->dataroot.'/temp';
+$CFG->cachedir = $CFG->dataroot.'/cache';
 
 // download required lang packs
 if ($CFG->lang !== 'en') {
diff --git a/admin/cli/purge_caches.php b/admin/cli/purge_caches.php
new file mode 100644 (file)
index 0000000..f008da2
--- /dev/null
@@ -0,0 +1,53 @@
+<?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/>.
+
+/**
+ * @package    core
+ * @subpackage cli
+ * @copyright  2011 David Mudrak <david@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+define('CLI_SCRIPT', true);
+
+require(dirname(dirname(dirname(__FILE__))).'/config.php');
+require_once($CFG->libdir.'/clilib.php');
+
+list($options, $unrecognized) = cli_get_params(array('help' => false), array('h' => 'help'));
+
+if ($unrecognized) {
+    $unrecognized = implode("\n  ", $unrecognized);
+    cli_error(get_string('cliunknowoption', 'admin', $unrecognized), 2);
+}
+
+if ($options['help']) {
+    $help =
+"Invalidates all Moodle internal caches
+
+Options:
+-h, --help            Print out this help
+
+Example:
+\$sudo -u www-data /usr/bin/php admin/cli/purge_caches.php
+";
+
+    echo $help;
+    exit(0);
+}
+
+purge_all_caches();
+
+exit(0);
\ No newline at end of file
index 964c64c..80d8829 100644 (file)
@@ -35,17 +35,29 @@ require_once($CFG->libdir.'/componentlib.class.php');
 
 admin_externalpage_setup('toollangimport');
 
-if (!empty($CFG->skiplangupgrade)) {
-    echo $OUTPUT->header();
-    echo $OUTPUT->box(get_string('langimportdisabled', 'tool_langimport'));
-    echo $OUTPUT->footer();
-    die;
+if (empty($CFG->langotherroot)) {
+    throw new moodle_exception('missingcfglangotherroot', 'tool_langimport');
 }
 
 $mode          = optional_param('mode', 0, PARAM_INT);              // action
 $pack          = optional_param_array('pack', array(), PARAM_SAFEDIR);    // pack to install
 $uninstalllang = optional_param('uninstalllang', '', PARAM_LANG);   // installed pack to uninstall
 $confirm       = optional_param('confirm', 0, PARAM_BOOL);          // uninstallation confirmation
+$purgecaches   = optional_param('purgecaches', false, PARAM_BOOL);  // explicit caches reset
+
+if ($purgecaches) {
+    require_sesskey();
+    get_string_manager()->reset_caches();
+    redirect($PAGE->url);
+}
+
+if (!empty($CFG->skiplangupgrade)) {
+    echo $OUTPUT->header();
+    echo $OUTPUT->box(get_string('langimportdisabled', 'tool_langimport'));
+    echo $OUTPUT->single_button(new moodle_url($PAGE->url, array('purgecaches' => 1)), get_string('purgestringcaches', 'tool_langimport'));
+    echo $OUTPUT->footer();
+    die;
+}
 
 define('INSTALLATION_OF_SELECTED_LANG', 2);
 define('DELETION_OF_SELECTED_LANG', 4);
index e2fbf51..a5ead16 100644 (file)
 $string['install'] = 'Install selected language pack';
 $string['installedlangs'] = 'Installed language packs';
 $string['langimport'] = 'Language import utility';
-$string['langimportdisabled'] = 'Language import feature has been disabled. You have to update your language packs manually at the file-system level.';
+$string['langimportdisabled'] = 'Language import feature has been disabled. You have to update your language packs manually at the file-system level. Do not forget to purge string caches after you do so.';
 $string['langpackinstalled'] = 'Language pack {$a} was successfully installed';
 $string['langpackremoved'] = 'Language pack was uninstalled';
 $string['langpackupdateskipped'] = 'Update of {$a} language pack skipped';
 $string['langpackuptodate'] = 'Language pack {$a} is up-to-date';
 $string['langupdatecomplete'] = 'Language pack update completed';
+$string['missingcfglangotherroot'] = 'Missing configuration value $CFG->langotherroot';
 $string['missinglangparent'] = 'Missing parent language <em>{$a->parent}</em> of <em>{$a->lang}</em>.';
 $string['nolangupdateneeded'] = 'All your language packs are up to date, no update is needed';
 $string['pluginname'] = 'Language packs';
+$string['purgestringcaches'] = 'Purge string caches';
 $string['remotelangnotavailable'] = 'Because Moodle can not connect to download.moodle.org, we are unable to do language pack installation automatically. Please download the appropriate zip file(s) from http://download.moodle.org, copy them to your {$a} directory and unzip them manually.';
 $string['uninstall'] = 'Uninstall selected language pack';
 $string['uninstallconfirm'] = 'You are about to completely uninstall language pack {$a}, are you sure?';
diff --git a/admin/tool/upgrade.txt b/admin/tool/upgrade.txt
new file mode 100644 (file)
index 0000000..3af7d3a
--- /dev/null
@@ -0,0 +1,21 @@
+This files describes API changes in /admin/tool/* - plugins,
+information provided here is intended especially for developers.
+
+
+=== 2.2 ===
+
+API changes:
+* new admin tool plugin type introduced
+
+
+How to migrate existing admin reports:
+# move all files to new /admin/tool/yourplugin/ location
+# update all links to admin tools /$CFG->admin/report/ to /$CFG->admin/tool/
+# add language pack with at least 'pluginname' string
+# update all language strings (use 'tool_yourplugin' instead of 'report_yourplugin') - use AMOS hints in commit message
+# update all capability names
+# create db/install.php migration script - delete old settings and capabilities (see converted plugins for examples)
+# grep the plugin codebase and look for any remaining 'coursereport' occurrences
+# update CSS selectors
+
+See http://docs.moodle.org/dev/Admin_tools for more details and explanation.
index d55346c..d44d058 100644 (file)
@@ -37,6 +37,13 @@ if (!is_enabled_auth('mnet')) {
 
 // If hostid hasn't been specified, try getting it using wwwroot
 if (!$hostid) {
+    $hostwwwroot = trim($hostwwwroot);
+    $hostwwwroot = rtrim($hostwwwroot, '/');
+
+    // ensure the wwwroot starts with a http or https prefix
+    if (strtolower(substr($hostwwwroot, 0, 4)) != 'http') {
+        $hostwwwroot = 'http://'.$hostwwwroot;
+    }
     $hostid = $DB->get_field('mnet_host', 'id', array('wwwroot' => $hostwwwroot));
 }
 
index 6486489..9c0358b 100644 (file)
@@ -1993,7 +1993,7 @@ class restore_activity_grading_structure_step extends restore_structure_step {
         $newitemid = $this->get_mappingid(restore_gradingform_plugin::itemid_mapping($areaname), $data->itemid);
 
         $oldid = $data->id;
-        $data->formid = $newformid;
+        $data->definitionid = $newformid;
         $data->raterid = $this->get_mappingid('user', $data->raterid);
         $data->itemid = $newitemid;
 
index 04ed41a..2ac6b78 100644 (file)
@@ -46,7 +46,7 @@ if (!$restore->is_independent()) {
             $restore->execute();
         } catch(Exception $e) {
             $restore->cleanup();
-            throw new moodle_exception((string)$e);
+            throw $e;
         }
     } else {
         $restore->save_controller();
index f036470..608d2d6 100644 (file)
@@ -824,54 +824,58 @@ abstract class restore_dbops {
             $newuserid = $DB->insert_record('user', $user);
             self::set_backup_ids_record($restoreid, 'user', $recuser->itemid, $newuserid);
             // Let's create the user context and annotate it (we need it for sure at least for files)
-            $newuserctxid = get_context_instance(CONTEXT_USER, $newuserid)->id;
-            self::set_backup_ids_record($restoreid, 'context', $recuser->parentitemid, $newuserctxid);
-
-            // Process custom fields
-            if (isset($user->custom_fields)) { // if present in backup
-                foreach($user->custom_fields['custom_field'] as $udata) {
-                    $udata = (object)$udata;
-                    // If the profile field has data and the profile shortname-datatype is defined in server
-                    if ($udata->field_data) {
-                        if ($field = $DB->get_record('user_info_field', array('shortname'=>$udata->field_name, 'datatype'=>$udata->field_type))) {
-                        /// Insert the user_custom_profile_field
-                            $rec = new stdClass();
-                            $rec->userid  = $newuserid;
-                            $rec->fieldid = $field->id;
-                            $rec->data    = $udata->field_data;
-                            $DB->insert_record('user_info_data', $rec);
+            // but for deleted users that don't have a context anymore (MDL-30192). We are done for them
+            // and nothing else (custom fields, prefs, tags, files...) will be created.
+            if (empty($user->deleted)) {
+                $newuserctxid = $user->deleted ? 0 : get_context_instance(CONTEXT_USER, $newuserid)->id;
+                self::set_backup_ids_record($restoreid, 'context', $recuser->parentitemid, $newuserctxid);
+
+                // Process custom fields
+                if (isset($user->custom_fields)) { // if present in backup
+                    foreach($user->custom_fields['custom_field'] as $udata) {
+                        $udata = (object)$udata;
+                        // If the profile field has data and the profile shortname-datatype is defined in server
+                        if ($udata->field_data) {
+                            if ($field = $DB->get_record('user_info_field', array('shortname'=>$udata->field_name, 'datatype'=>$udata->field_type))) {
+                            /// Insert the user_custom_profile_field
+                                $rec = new stdClass();
+                                $rec->userid  = $newuserid;
+                                $rec->fieldid = $field->id;
+                                $rec->data    = $udata->field_data;
+                                $DB->insert_record('user_info_data', $rec);
+                            }
                         }
                     }
                 }
-            }
 
-            // Process tags
-            if (!empty($CFG->usetags) && isset($user->tags)) { // if enabled in server and present in backup
-                $tags = array();
-                foreach($user->tags['tag'] as $usertag) {
-                    $usertag = (object)$usertag;
-                    $tags[] = $usertag->rawname;
+                // Process tags
+                if (!empty($CFG->usetags) && isset($user->tags)) { // if enabled in server and present in backup
+                    $tags = array();
+                    foreach($user->tags['tag'] as $usertag) {
+                        $usertag = (object)$usertag;
+                        $tags[] = $usertag->rawname;
+                    }
+                    tag_set('user', $newuserid, $tags);
                 }
-                tag_set('user', $newuserid, $tags);
-            }
 
-            // Process preferences
-            if (isset($user->preferences)) { // if present in backup
-                foreach($user->preferences['preference'] as $preference) {
-                    $preference = (object)$preference;
-                    // Prepare the record and insert it
-                    $preference->userid = $newuserid;
-                    $status = $DB->insert_record('user_preferences', $preference);
+                // Process preferences
+                if (isset($user->preferences)) { // if present in backup
+                    foreach($user->preferences['preference'] as $preference) {
+                        $preference = (object)$preference;
+                        // Prepare the record and insert it
+                        $preference->userid = $newuserid;
+                        $status = $DB->insert_record('user_preferences', $preference);
+                    }
                 }
-            }
 
-            // Create user files in pool (profile, icon, private) by context
-            restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'icon', $recuser->parentitemid, $userid);
-            restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'profile', $recuser->parentitemid, $userid);
-            if ($userfiles) { // private files only if enabled in settings
-                restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'private', $recuser->parentitemid, $userid);
-            }
+                // Create user files in pool (profile, icon, private) by context
+                restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'icon', $recuser->parentitemid, $userid);
+                restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'profile', $recuser->parentitemid, $userid);
+                if ($userfiles) { // private files only if enabled in settings
+                    restore_dbops::send_files_to_pool($basepath, $restoreid, 'user', 'private', $recuser->parentitemid, $userid);
+                }
 
+            }
         }
         $rs->close();
     }
index 4ca5414..0845440 100644 (file)
@@ -232,15 +232,9 @@ class core_backup_renderer extends plugin_renderer_base {
             $html .= $this->output->heading(get_string('restoretonewcourse', 'backup'), 2, array('class'=>'header'));
             $html .= $this->backup_detail_input(get_string('restoretonewcourse', 'backup'), 'radio', 'target', backup::TARGET_NEW_COURSE, array('checked'=>'checked'));
             $html .= $this->backup_detail_pair(get_string('selectacategory', 'backup'), $this->render($categories));
-            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'), 'class'=>'newcoursecontinue')));
+            $html .= $this->backup_detail_pair('', html_writer::empty_tag('input', array('type'=>'submit', 'value'=>get_string('continue'))));
             $html .= html_writer::end_tag('div');
             $html .= html_writer::end_tag('form');
-            $config = new stdClass;
-            $config->title = get_string('confirmnewcoursecontinue', 'backup');
-            $config->question = get_string('confirmnewcoursecontinuequestion', 'backup');
-            $config->yesLabel = get_string('continue');
-            $config->noLabel = get_string('cancel');
-            $PAGE->requires->yui_module('moodle-backup-confirmcancel', 'M.core_backup.watch_newcoursecontinue_buttons', array($config));
         }
 
         if ($wholecourse && !empty($currentcourse)) {
index 05395e4..267b853 100644 (file)
@@ -27,24 +27,4 @@ M.core_backup.watch_cancel_buttons = function(config) {
     });
 }
 
-M.core_backup.watch_newcoursecontinue_buttons = function(config) {
-    Y.all('.newcoursecontinue').each(function(){
-        this._confirmationListener = this._confirmationListener || this.on('click', function(e){
-            // Prevent the default event (sumbit) from firing
-            e.preventDefault();
-            // Create the confirm box
-            var confirm = new M.core.confirm(config);
-            // If the user clicks yes
-            confirm.on('complete-yes', function(e){
-                // Detach the listener for the confirm box so it doesn't fire again.
-                this._confirmationListener.detach();
-                // Simulate the original cancel button click
-                this.simulate('click');
-            }, this);
-            // Show the confirm box
-            confirm.show();
-        }, this);
-    });
-}
-
 }, '@VERSION@', {'requires':['base','node','node-event-simulate','moodle-enrol-notification']});
\ No newline at end of file
index 60bca8d..a00cd09 100644 (file)
@@ -74,7 +74,7 @@ class block_comments extends block_base {
         $args->linktext  = get_string('showcomments');
         $args->notoggle  = true;
         $args->autostart = true;
-        $args->displaycancel = true;
+        $args->displaycancel = false;
         $comment = new comment($args);
         $comment->set_view_permission(true);
 
index 87f0e9e..dcf669e 100644 (file)
@@ -58,6 +58,7 @@ M.core_comment = {
                         return false;
                     }, this);
                 }
+                scope.toggle_textarea(false);
                 CommentHelper.confirmoverlay = new Y.Overlay({
 bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-'+this.client_id+'">'+M.str.moodle.yes+'</a> <a href="#" id="canceldelete-'+this.client_id+'">'+M.str.moodle.no+'</a></div>',
                                         visible: false
@@ -79,6 +80,7 @@ bodyContent: '<div class="comment-delete-confirm"><a href="#" id="confirmdelete-
                             var cid = scope.client_id;
                             var ta = Y.one('#dlg-content-'+cid);
                             ta.set('value', '');
+                            scope.toggle_textarea(false);
                             var container = Y.one('#comment-list-'+cid);
                             var result = scope.render([obj], true);
                             var newcomment = Y.Node.create(result.html);
index 2b69aaf..9e186ce 100644 (file)
@@ -57,7 +57,7 @@ class core_enrol_external extends external_api {
      * @return array of courses
      */
     public static function get_users_courses($userid) {
-        global $USER;
+        global $USER, $DB;
 
         // Do basic automatic PARAM checks on incoming data, using params description
         // If any problems are found then exceptions are thrown with helpful error messages
@@ -74,12 +74,17 @@ class core_enrol_external extends external_api {
                 // current user can not access this course, sorry we can not disclose who is enrolled in this course!
                 continue;
             }
+
             if ($userid != $USER->id and !has_capability('moodle/course:viewparticipants', $context)) {
                 // we need capability to view participants
                 continue;
             }
 
-            $result[] = array('id'=>$course->id, 'shortname'=>$course->shortname, 'fullname'=>$course->fullname, 'idnumber'=>$course->idnumber,'visible'=>$course->visible);
+            list($enrolledsqlselect, $enrolledparams) = get_enrolled_sql($context);
+            $enrolledsql = "SELECT COUNT(*) FROM ($enrolledsqlselect) AS enrolleduserids";
+            $enrolledusercount = $DB->count_records_sql($enrolledsql, $enrolledparams);
+
+            $result[] = array('id'=>$course->id, 'shortname'=>$course->shortname, 'fullname'=>$course->fullname, 'idnumber'=>$course->idnumber,'visible'=>$course->visible, 'enrolledusercount'=>$enrolledusercount);
         }
 
         return $result;
@@ -96,6 +101,7 @@ class core_enrol_external extends external_api {
                     'id'        => new external_value(PARAM_INT, 'id of course'),
                     'shortname' => new external_value(PARAM_RAW, 'short name of course'),
                     'fullname'  => new external_value(PARAM_RAW, 'long name of course'),
+                    'enrolledusercount' => new external_value(PARAM_INT, 'Number of enrolled users in this course'),
                     'idnumber'  => new external_value(PARAM_RAW, 'id number of course'),
                     'visible'   => new external_value(PARAM_INT, '1 means visible, 0 means hidden course'),
                 )
@@ -145,6 +151,7 @@ class core_enrol_external extends external_api {
         $withcapability = '';
         $groupid        = 0;
         $onlyactive     = false;
+        $userfields     = array();
         foreach ($options as $option) {
             switch ($option['name']) {
             case 'withcapability':
@@ -156,6 +163,12 @@ class core_enrol_external extends external_api {
             case 'onlyactive':
                 $onlyactive = !empty($option['value']);
                 break;
+            case 'userfields':
+                $thefields = explode(',', $option['value']);
+                foreach ($thefields as $f) {
+                    $userfields[] = clean_param($f, PARAM_ALPHANUMEXT);
+                }
+                break;
             }
         }
 
@@ -208,7 +221,7 @@ class core_enrol_external extends external_api {
                 continue;
             }
             context_instance_preload($user);
-            if ($userdetails = user_get_user_details($user, $course)) {
+            if ($userdetails = user_get_user_details($user, $course, $userfields)) {
                 $users[] = $userdetails;
             }
         }
@@ -249,8 +262,8 @@ class core_enrol_external extends external_api {
                     'city'        => new external_value(PARAM_NOTAGS, 'Home city of the user', VALUE_OPTIONAL),
                     'url'         => new external_value(PARAM_URL, 'URL of the user', VALUE_OPTIONAL),
                     'country'     => new external_value(PARAM_ALPHA, 'Home country code of the user, such as AU or CZ', VALUE_OPTIONAL),
-                    'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version'),
-                    'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version'),
+                    'profileimageurlsmall' => new external_value(PARAM_URL, 'User image profile URL - small version', VALUE_OPTIONAL),
+                    'profileimageurl' => new external_value(PARAM_URL, 'User image profile URL - big version', VALUE_OPTIONAL),
                     'customfields' => new external_multiple_structure(
                         new external_single_structure(
                             array(
index a04d9d5..2dec772 100644 (file)
@@ -302,7 +302,7 @@ function get_recstatus($tagdata, $tagname){
 * Process the group tag. This defines a Moodle course.
 * @param string $tagconents The raw contents of the XML element
 */
-function process_group_tag($tagcontents){
+function process_group_tag($tagcontents) {
     global $DB;
 
     // Get configs
@@ -312,24 +312,24 @@ function process_group_tag($tagcontents){
 
     // Process tag contents
     $group = new stdClass();
-    if(preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)){
+    if (preg_match('{<sourcedid>.*?<id>(.+?)</id>.*?</sourcedid>}is', $tagcontents, $matches)) {
         $group->coursecode = trim($matches[1]);
     }
-    if(preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)){
+    if (preg_match('{<description>.*?<short>(.*?)</short>.*?</description>}is', $tagcontents, $matches)) {
         $group->description = trim($matches[1]);
     }
-    if(preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)){
+    if (preg_match('{<org>.*?<orgunit>(.*?)</orgunit>.*?</org>}is', $tagcontents, $matches)) {
         $group->category = trim($matches[1]);
     }
 
     $recstatus = ($this->get_recstatus($tagcontents, 'group'));
     //echo "<p>get_recstatus for this group returned $recstatus</p>";
 
-    if(!(strlen($group->coursecode)>0)){
+    if (!(strlen($group->coursecode)>0)) {
         $this->log_line('Error at line '.$line.': Unable to find course code in \'group\' element.');
-    }else{
+    } else {
         // First, truncate the course code if desired
-        if(intval($truncatecoursecodes)>0){
+        if (intval($truncatecoursecodes)>0) {
             $group->coursecode = ($truncatecoursecodes > 0)
                      ? substr($group->coursecode, 0, intval($truncatecoursecodes))
                      : $group->coursecode;
@@ -349,66 +349,75 @@ function process_group_tag($tagcontents){
         $group->coursecode = array($group->coursecode);
 
         // Third, check if the course(s) exist
-        foreach($group->coursecode as $coursecode){
+        foreach ($group->coursecode as $coursecode) {
             $coursecode = trim($coursecode);
-            if(!$DB->get_field('course', 'id', array('idnumber'=>$coursecode))) {
-              if(!$createnewcourses) {
-                  $this->log_line("Course $coursecode not found in Moodle's course idnumbers.");
-              } else {
-                // Create the (hidden) course(s) if not found
-                $course = new stdClass();
-                $course->fullname = $group->description;
-                $course->shortname = $coursecode;
-                $course->idnumber = $coursecode;
-                $course->format = 'topics';
-                $course->visible = 0;
-                // Insert default names for teachers/students, from the current language
-                $site = get_site();
-
-                // Handle course categorisation (taken from the group.org.orgunit field if present)
-                if(strlen($group->category)>0){
-                    // If the category is defined and exists in Moodle, we want to store it in that one
-                    if($catid = $DB->get_field('course_categories', 'id', array('name'=>$group->category))){
-                        $course->category = $catid;
-                    } elseif($createnewcategories) {
-                        // Else if we're allowed to create new categories, let's create this one
-                        $newcat = new stdClass();
-                        $newcat->name = $group->category;
-                        $newcat->visible = 0;
-                        $catid = $DB->insert_record('course_categories', $newcat);
-                        $course->category = $catid;
-                        $this->log_line("Created new (hidden) category, #$catid: $newcat->name");
-                    }else{
-                        // If not found and not allowed to create, stick with default
-                        $this->log_line('Category '.$group->category.' not found in Moodle database, so using default category instead.');
+            if (!$DB->get_field('course', 'id', array('idnumber'=>$coursecode))) {
+                if (!$createnewcourses) {
+                    $this->log_line("Course $coursecode not found in Moodle's course idnumbers.");
+                } else {
+                    // Create the (hidden) course(s) if not found
+                    $courseconfig = get_config('moodlecourse'); // Load Moodle Course shell defaults
+                    $course = new stdClass();
+                    $course->fullname = $group->description;
+                    $course->shortname = $coursecode;
+                    $course->idnumber = $coursecode;
+                    $course->format = $courseconfig->format;
+                    $course->visible = $courseconfig->visible;
+                    $course->numsections = $courseconfig->numsections;
+                    $course->hiddensections = $courseconfig->hiddensections;
+                    $course->newsitems = $courseconfig->newsitems;
+                    $course->showgrades = $courseconfig->showgrades;
+                    $course->showreports = $courseconfig->showreports;
+                    $course->maxbytes = $courseconfig->maxbytes;
+                    $course->groupmode = $courseconfig->groupmode;
+                    $course->groupmodeforce = $courseconfig->groupmodeforce;
+                    $course->enablecompletion = $courseconfig->enablecompletion;
+                    $course->completionstartonenrol = $courseconfig->completionstartonenrol;
+                    // Insert default names for teachers/students, from the current language
+
+                    // Handle course categorisation (taken from the group.org.orgunit field if present)
+                    if (strlen($group->category)>0) {
+                        // If the category is defined and exists in Moodle, we want to store it in that one
+                        if ($catid = $DB->get_field('course_categories', 'id', array('name'=>$group->category))) {
+                            $course->category = $catid;
+                        } else if ($createnewcategories) {
+                            // Else if we're allowed to create new categories, let's create this one
+                            $newcat = new stdClass();
+                            $newcat->name = $group->category;
+                            $newcat->visible = 0;
+                            $catid = $DB->insert_record('course_categories', $newcat);
+                            $course->category = $catid;
+                            $this->log_line("Created new (hidden) category, #$catid: $newcat->name");
+                        } else {
+                            // If not found and not allowed to create, stick with default
+                            $this->log_line('Category '.$group->category.' not found in Moodle database, so using default category instead.');
+                            $course->category = 1;
+                        }
+                    } else {
                         $course->category = 1;
                     }
-                }else{
-                    $course->category = 1;
-                }
-                $course->timecreated = time();
-                $course->startdate = time();
-                $course->numsections = 1;
-                // Choose a sort order that puts us at the start of the list!
-                $course->sortorder = 0;
+                    $course->timecreated = time();
+                    $course->startdate = time();
+                    // Choose a sort order that puts us at the start of the list!
+                    $course->sortorder = 0;
 
-                $courseid = $DB->insert_record('course', $course);
+                    $courseid = $DB->insert_record('course', $course);
 
-                // Setup the blocks
-                $course = $DB->get_record('course', array('id' => $courseid));
-                blocks_add_default_course_blocks($course);
+                    // Setup the blocks
+                    $course = $DB->get_record('course', array('id' => $courseid));
+                    blocks_add_default_course_blocks($course);
 
-                $section = new stdClass();
-                $section->course = $course->id;   // Create a default section.
-                $section->section = 0;
-                $section->summaryformat = FORMAT_HTML;
-                $section->id = $DB->insert_record("course_sections", $section);
+                    $section = new stdClass();
+                    $section->course = $course->id;   // Create a default section.
+                    $section->section = 0;
+                    $section->summaryformat = FORMAT_HTML;
+                    $section->id = $DB->insert_record("course_sections", $section);
 
-                add_to_log(SITEID, "course", "new", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
+                    add_to_log(SITEID, "course", "new", "view.php?id=$course->id", "$course->fullname (ID $course->id)");
 
-                $this->log_line("Created course $coursecode in Moodle (Moodle ID is $course->id)");
-              }
-            }elseif($recstatus==3 && ($courseid = $DB->get_field('course', 'id', array('idnumber'=>$coursecode)))){
+                    $this->log_line("Created course $coursecode in Moodle (Moodle ID is $course->id)");
+                }
+            } else if ($recstatus==3 && ($courseid = $DB->get_field('course', 'id', array('idnumber'=>$coursecode)))) {
                 // If course does exist, but recstatus==3 (delete), then set the course as hidden
                 $DB->set_field('course', 'visible', '0', array('id'=>$courseid));
             }
index a94d298..e1e61ca 100644 (file)
@@ -171,10 +171,32 @@ class filter_mediaplugin extends moodle_text_filter {
 ///===========================
 /// utility functions
 
+/**
+ * Get mimetype of given url, useful for # alternative urls.
+ *
+ * @private
+ * @param string $url
+ * @return string $mimetype
+ */
+function filter_mediaplugin_get_mimetype($url) {
+    $matches = null;
+    if (preg_match("|^(.*)/[a-z]*file.php(\?file=)?(/[^&\?#]*)|", $url, $matches)) {
+        // remove the special moodle file serving hacks so that the *file.php is ignored
+        $url = $matches[1].$matches[3];
+    } else {
+        $url = preg_replace('/[#\?].*$/', '', $url);
+    }
+
+    $mimetype = mimeinfo('type', $url);
+
+    return $mimetype;
+}
 
 /**
  * Parse list of alternative URLs
  * @param string $url urls separated with '#', size specified as ?d=640x480 or #d=640x480
+ * @param int $defaultwidth
+ * @param int $defaultheight
  * @return array (urls, width, height)
  */
 function filter_mediaplugin_parse_alternatives($url, $defaultwidth = 0, $defaultheight = 0) {
@@ -252,7 +274,7 @@ function filter_mediaplugin_html5audio_callback(array $link) {
     $fallbacklink = null;
 
     foreach ($urls as $url) {
-        $mimetype = mimeinfo('type', $url);
+        $mimetype = filter_mediaplugin_get_mimetype($url);
         if (strpos($mimetype, 'audio/') !== 0) {
             continue;
         }
@@ -344,7 +366,7 @@ function filter_mediaplugin_html5video_callback(array $link) {
     $fallbacklink = null;
 
     foreach ($urls as $url) {
-        $mimetype = mimeinfo('type', $url);
+        $mimetype = filter_mediaplugin_get_mimetype($url);
         if (strpos($mimetype, 'video/') !== 0) {
             continue;
         }
@@ -546,7 +568,7 @@ function filter_mediaplugin_flv_callback($link) {
     $sources  = array();
 
     foreach ($urls as $url) {
-        $mimetype = mimeinfo('type', $url);
+        $mimetype = filter_mediaplugin_get_mimetype($url);
         if (strpos($mimetype, 'video/') !== 0) {
             continue;
         }
@@ -559,7 +581,7 @@ function filter_mediaplugin_flv_callback($link) {
         }
 
         if ($flashurl === null) {
-            $flashurl  = str_replace('&', '&amp;', $url);
+            $flashurl  = $url;
         }
     }
     if (!$sources) {
@@ -592,7 +614,7 @@ OET;
     // note: no need to print "this is flv link" because it is printed automatically if JS or Flash not available
 
     $output = html_writer::tag('span', $printlink, array('id'=>$id, 'class'=>'mediaplugin mediaplugin_flv'));
-    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, $flashurl, $width, $height, $autosize))); // we can not use standard JS init because this may be cached
+    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, rawurlencode($flashurl), $width, $height, $autosize))); // we can not use standard JS init because this may be cached
 
     return $output;
 }
@@ -799,7 +821,7 @@ function filter_mediaplugin_wmp_callback($link) {
         $mpsize = 'width="'.$link[4].'" height="'.($link[5] + 64).'"';
         $autosize = 'false';
     }
-    $mimetype = mimeinfo('type', $url);
+    $mimetype = filter_mediaplugin_get_mimetype($url);
 
 
 
@@ -861,7 +883,7 @@ function filter_mediaplugin_qt_callback($link) {
     } else {
         $size = 'width="'.$link[4].'" height="'.($link[5]+15).'"';
     }
-    $mimetype = mimeinfo('type', $url);
+    $mimetype = filter_mediaplugin_get_mimetype($url);
 
     // this is the safest fallback for incomplete or missing browser support for this format
     return <<<OET
diff --git a/filter/upgrade.txt b/filter/upgrade.txt
new file mode 100644 (file)
index 0000000..bb0522e
--- /dev/null
@@ -0,0 +1,11 @@
+This file describes API changes in core filter API and plugins,
+information provided here is intended especially for developers.
+
+=== 2.2 ===
+
+* legacy filters and legacy locations have been deprecated, so any
+  old filter should be updated to use the new moodle_text_filter,
+  and any filter bundled under mod/xxxx directories be moved to
+  /filter/xxxx (MDL-29995). They will stop working completely in
+  Moodle 2.3 (MDL-29996). See the glossary or data filters for
+  examples of legacy module filters and locations already updated.
index 9216dee..ab188de 100644 (file)
@@ -108,6 +108,7 @@ if ($grade = $DB->get_record('grade_grades', array('itemid' => $grade_item->id,
             $options->smiley  = false;
             $options->filter  = false;
             $options->noclean = false;
+            $options->para    = false;
             $grade->feedback  = format_text($grade->feedback, $grade->feedbackformat, $options);
         }
         $grade->feedbackformat = FORMAT_HTML;
index f890785..49af487 100644 (file)
@@ -803,7 +803,7 @@ class grade_edit_tree_column_range extends grade_edit_tree_column {
         } elseif ($item->is_external_item()) {
             $grademax = format_float($item->grademax, $item->get_decimals());
         } else {
-            $grademax = '<input type="text" size="4" id="grademax'.$item->id.'" name="grademax_'.$item->id.'" value="'.format_float($item->grademax, $item->get_decimals()).'" />';
+            $grademax = '<input type="text" size="6" id="grademax'.$item->id.'" name="grademax_'.$item->id.'" value="'.format_float($item->grademax, $item->get_decimals()).'" />';
         }
 
         $itemcell = clone($this->itemcell);
index d96b75e..bc93369 100644 (file)
@@ -651,7 +651,7 @@ class grade_report_user extends grade_report {
             // Then left join with grade_grades and look for rows with null final grade (which includes grade items with no grade_grade)
             $sql = "SELECT gi.id, COUNT(u.id) AS count
                       FROM {grade_items} gi
-                      JOIN {user} u
+                      JOIN {user} u ON u.deleted = 0
                       JOIN ($enrolledsql) je ON je.id = u.id
                       JOIN (
                                SELECT DISTINCT ra.userid
@@ -660,10 +660,9 @@ class grade_report_user extends grade_report {
                                   AND ra.contextid " . get_related_contexts_string($this->context) . "
                            ) rainner ON rainner.userid = u.id
                       LEFT JOIN {grade_grades} gg
-                           ON (gg.itemid = gi.id AND gg.userid = u.id AND gg.finalgrade IS NOT NULL AND gg.hidden = 0)
+                             ON (gg.itemid = gi.id AND gg.userid = u.id AND gg.finalgrade IS NOT NULL AND gg.hidden = 0)
                       $groupsql
                      WHERE gi.courseid = :courseid
-                           AND u.deleted = 0
                            AND gg.finalgrade IS NULL
                            $groupwheresql
                   GROUP BY gi.id";
index 6c9e52c..aef61d7 100644 (file)
@@ -316,7 +316,7 @@ function groups_delete_group($grouporid) {
 
 /**
  * Delete grouping
- * @param int $groupingid
+ * @param int $groupingorid
  * @return bool success
  */
 function groups_delete_grouping($groupingorid) {
@@ -389,7 +389,7 @@ function groups_delete_group_members($courseid, $userid=0, $showfeedback=false)
     events_trigger('groups_members_removed', $eventdata);
 
     if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groups_members');
+        echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groupmembers', 'group'), 'notifysuccess');
     }
 
     return true;
@@ -407,15 +407,10 @@ function groups_delete_groupings_groups($courseid, $showfeedback=false) {
     $groupssql = "SELECT id FROM {groups} g WHERE g.courseid = ?";
     $DB->delete_records_select('groupings_groups', "groupid IN ($groupssql)", array($courseid));
 
-    // Delete all files associated with groupings for this course
-    $context = get_context_instance(CONTEXT_COURSE, $courseid);
-
     //trigger groups events
     events_trigger('groups_groupings_groups_removed', $courseid);
 
-    if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groupings_groups');
-    }
+    // no need to show any feedback here - we delete usually first groupings and then groups
 
     return true;
 }
@@ -449,11 +444,11 @@ function groups_delete_groups($courseid, $showfeedback=false) {
 
     $DB->delete_records('groups', array('courseid'=>$courseid));
 
-    //trigger groups events
+    // trigger groups events
     events_trigger('groups_groups_deleted', $courseid);
 
     if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groups');
+        echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groups', 'group'), 'notifysuccess');
     }
 
     return true;
@@ -468,9 +463,6 @@ function groups_delete_groups($courseid, $showfeedback=false) {
 function groups_delete_groupings($courseid, $showfeedback=false) {
     global $DB, $OUTPUT;
 
-    $context = get_context_instance(CONTEXT_COURSE, $courseid);
-    $fs = get_file_storage();
-
     // delete any uses of groupings
     $sql = "DELETE FROM {groupings_groups}
              WHERE groupingid in (SELECT id FROM {groupings} g WHERE g.courseid = ?)";
@@ -488,11 +480,11 @@ function groups_delete_groupings($courseid, $showfeedback=false) {
 
     $DB->delete_records('groupings', array('courseid'=>$courseid));
 
-    //trigger groups events
+    // trigger groups events
     events_trigger('groups_groupings_deleted', $courseid);
 
     if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deleted').' groupings');
+        echo $OUTPUT->notification(get_string('deleted').' - '.get_string('groupings', 'group'), 'notifysuccess');
     }
 
     return true;
index 533c7ae..341a147 100644 (file)
@@ -166,7 +166,7 @@ $CFG->wwwroot              = install_guess_wwwroot(); // can not be changed - pp
 $CFG->httpswwwroot         = $CFG->wwwroot;
 $CFG->dataroot             = $config->dataroot;
 $CFG->tempdir              = $CFG->dataroot.'/temp';
-$CFG->cachedir             = $CFG->dataroot.'/temp';
+$CFG->cachedir             = $CFG->dataroot.'/cache';
 $CFG->admin                = $config->admin;
 $CFG->docroot              = 'http://docs.moodle.org';
 $CFG->langotherroot        = $CFG->dataroot.'/lang';
index 260fe97..68507dc 100644 (file)
@@ -26,6 +26,7 @@ databasehost,install
 databasename,install
 databasetypehead,install
 dataroot,install
+datarootpermission,install
 dbprefix,install
 dirroot,install
 downloadedfilecheckfailed,error
index f554e6b..1ce4f9e 100644 (file)
@@ -901,6 +901,7 @@ $string['slasharguments'] = 'Use slash arguments';
 $string['smartpix'] = 'Smart pix search';
 $string['soaprecommended'] = 'Installing the optional soap extension is useful for web services and some contrib modules.';
 $string['spellengine'] = 'Spell engine';
+$string['spelllanguagelist'] = 'Spell language list';
 $string['splrequired'] = 'The SPL PHP extension is now required by Moodle.';
 $string['stats'] = 'Statistics';
 $string['statsfirstrun'] = 'Maximum processing interval';
index ce65d59..f0e125a 100644 (file)
@@ -319,6 +319,7 @@ $string['invalidstatedetected'] = 'Something has gone wrong: {$a}. This should n
 $string['invalidurl'] = 'Invalid URL';
 $string['invaliduser'] = 'Invalid user';
 $string['invaliduserid'] = 'Invalid user id';
+$string['invaliduserfield'] = 'Invalid user field: {$a}';
 $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 cdaaa0c..75ba827 100644 (file)
@@ -154,6 +154,7 @@ $string['databasetypesub'] = 'Moodle supports several types of database servers.
 $string['databaseuser'] = 'Database user';
 $string['dataroot'] = 'Data directory';
 $string['datarooterror'] = 'The \'data directory\' you specified could not be found or created.  Either correct the path or create that directory manually.';
+$string['datarootpermission'] = 'Data directories permission';
 $string['datarootpublicerror'] = 'The \'data directory\' you specified is directly accessible via web, you must use different directory.';
 $string['dbconnectionerror'] = 'We could not connect to the database you specified. Please check your database settings.';
 $string['dbcreationerror'] = 'Database creation error. Could not create the given database name with the settings provided';
index 52caf4a..9cae46d 100644 (file)
@@ -531,6 +531,7 @@ $string['emaildisable'] = 'This email address is disabled';
 $string['emaildisableclick'] = 'Click here to disable all email from being sent to this address';
 $string['emaildisplay'] = 'Email display';
 $string['emaildisplaycourse'] = 'Allow only other course members to see my email address';
+$string['emaildisplayhidden'] = 'Email hidden';
 $string['emaildisplayno'] = 'Hide my email address from everyone';
 $string['emaildisplayyes'] = 'Allow everyone to see my email address';
 $string['emailenable'] = 'This email address is enabled';
index 77b73e1..2d28d61 100644 (file)
@@ -190,6 +190,11 @@ $string['webservices'] = 'Web services';
 $string['webservicesoverview'] = 'Overview';
 $string['webservicetokens'] = 'Web service tokens';
 $string['wrongusernamepassword'] = 'Wrong username or password';
+$string['wsaccessuserdeleted'] = 'Refused web service access for deleted username: {$a}';
+$string['wsaccessuserexpired'] = 'Refused web service access for password expired username: {$a}';
+$string['wsaccessusernologin'] = 'Refused web service access for nologin authentication username: {$a}';
+$string['wsaccessusersuspended'] = 'Refused web service access for suspended username: {$a}';
+$string['wsaccessuserunconfirmed'] = 'Refused web service access for unconfirmed username: {$a}';
 $string['wsauthmissing'] = 'The web service authentication plugin is missing.';
 $string['wsauthnotenabled'] = 'The web service authentication plugin is disabled.';
 $string['wsclientdoc'] = 'Moodle web service client documentation';
index 9235098..3c2936b 100644 (file)
@@ -237,6 +237,11 @@ class iCalendar_component {
         $components = array(); // Initialise a stack of components
         $this->clear_errors();
         foreach ($lines as $key => $line) {
+            // ignore empty lines
+            if (trim($line) == '') {
+                continue;
+            }
+
             // Divide the line up into label, parameters and data fields.
             if (!preg_match('#^(?P<label>[-[:alnum:]]+)(?P<params>(?:;(?:(?:[-[:alnum:]]+)=(?:[^[:cntrl:]";:,]+|"[^[:cntrl:]"]+")))*):(?P<data>.*)$#', $line, $match)) {
                 $this->parser_error('Invalid line: '.$key.', ignoring');
index 73aa42c..6614b39 100644 (file)
@@ -54,9 +54,9 @@ class iCalendar_parameter {
                 return rfc2445_is_valid_value($value, RFC2445_TYPE_CAL_ADDRESS);
             break;
 
-            // These are textual parameters, so the MUST NOT contain double quotes
+            // RFC-2445: can contain quotes.
             case 'CN':
-                return (strpos($value, '"') === false);
+                return true;
             break;
 
             // These have enumerated legal values
index bb621ce..8a557a4 100644 (file)
@@ -218,6 +218,7 @@ class iCalendar_property_calscale extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -235,6 +236,7 @@ class iCalendar_property_method extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -255,6 +257,7 @@ class iCalendar_property_prodid extends iCalendar_property {
     var $val_default = NULL;
 
     function __construct() {
+        parent::__construct();
         $this->val_default = '-//John Papaioannou/NONSGML Bennu '._BENNU_VERSION.'//EN';
 
         $this->valid_parameters = array(
@@ -270,6 +273,7 @@ class iCalendar_property_version extends iCalendar_property {
     var $val_default = '2.0';
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -290,6 +294,7 @@ class iCalendar_property_attach extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_URI;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'FMTTYPE'     => RFC2445_OPTIONAL | RFC2445_ONCE,
             'ENCODING'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -336,6 +341,7 @@ class iCalendar_property_categories extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -350,6 +356,7 @@ class iCalendar_property_class extends iCalendar_property {
     var $val_default = 'PUBLIC';
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -367,6 +374,7 @@ class iCalendar_property_comment extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -381,6 +389,7 @@ class iCalendar_property_description extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -395,6 +404,7 @@ class iCalendar_property_geo extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -435,6 +445,7 @@ class iCalendar_property_location extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -449,6 +460,7 @@ class iCalendar_property_percent_complete extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_INTEGER;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -471,6 +483,7 @@ class iCalendar_property_priority extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_INTEGER;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -494,6 +507,7 @@ class iCalendar_property_resources extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -508,6 +522,7 @@ class iCalendar_property_status extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -538,6 +553,7 @@ class iCalendar_property_summary extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -555,6 +571,7 @@ class iCalendar_property_completed extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -575,6 +592,7 @@ class iCalendar_property_dtend extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -617,6 +635,7 @@ class iCalendar_property_due extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -659,6 +678,7 @@ class iCalendar_property_dtstart extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -702,6 +722,7 @@ class iCalendar_property_duration extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DURATION;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -724,6 +745,7 @@ class iCalendar_property_freebusy extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'FBTYPE'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -758,6 +780,7 @@ class iCalendar_property_transp extends iCalendar_property {
     var $val_default = 'OPAQUE';
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -783,6 +806,7 @@ class iCalendar_property_attendee extends iCalendar_property {
     // TODO: standard has lots of detail here, make triple sure that we eventually conform
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'CN'             => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -823,6 +847,7 @@ class iCalendar_property_contact extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'ALTREP'      => RFC2445_OPTIONAL | RFC2445_ONCE,
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -837,6 +862,7 @@ class iCalendar_property_organizer extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_CAL_ADDRESS;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'CN'          => RFC2445_OPTIONAL | RFC2445_ONCE,
             'DIR'         => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -877,6 +903,7 @@ class iCalendar_property_recurrence_id extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'RANGE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -909,6 +936,7 @@ class iCalendar_property_related_to extends iCalendar_property {
     // TODO: the value of this property must reference another component's UID
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'RELTYPE'     => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -922,6 +950,7 @@ class iCalendar_property_url extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_URI;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -934,6 +963,7 @@ class iCalendar_property_uid extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -955,6 +985,7 @@ class iCalendar_property_exdate extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -984,6 +1015,7 @@ class iCalendar_property_exrule extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_RECUR;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -997,6 +1029,7 @@ class iCalendar_property_rdate extends iCalendar_property {
     var $val_multi   = true;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'TZID'        => RFC2445_OPTIONAL | RFC2445_ONCE,
             'VALUE'       => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -1026,6 +1059,7 @@ class iCalendar_property_rrule extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_RECUR;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1039,6 +1073,7 @@ class iCalendar_property_action extends iCalendar_property {
     var $val_type   = RFC2445_TYPE_TEXT;
     
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1061,6 +1096,7 @@ class iCalendar_property_repeat extends iCalendar_property {
     var $val_type   = RFC2445_TYPE_INTEGER;
     
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1072,6 +1108,7 @@ class iCalendar_property_trigger extends iCalendar_property {
     var $val_type   = RFC2445_TYPE_TEXT;
     
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'VALUE' => RFC2445_OPTIONAL | RFC2445_ONCE,
             'RELATED' => RFC2445_OPTIONAL | RFC2445_ONCE,
@@ -1100,6 +1137,7 @@ class iCalendar_property_created extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1120,6 +1158,7 @@ class iCalendar_property_dtstamp extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1140,6 +1179,7 @@ class iCalendar_property_last_modified extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_DATE_TIME;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1161,6 +1201,7 @@ class iCalendar_property_sequence extends iCalendar_property {
     var $val_default = 0;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1184,6 +1225,7 @@ class iCalendar_property_x extends iCalendar_property {
     var $val_type    = NULL;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -1214,6 +1256,7 @@ class iCalendar_property_request_status extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE'    => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -1339,6 +1382,7 @@ class iCalendar_property_tzid extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1359,6 +1403,7 @@ class iCalendar_property_tzname extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             'LANGUAGE' => RFC2445_OPTIONAL | RFC2445_ONCE,
             RFC2445_XNAME => RFC2445_OPTIONAL
@@ -1380,6 +1425,7 @@ class iCalendar_property_tzoffsetfrom extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_UTC_OFFSET;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1400,6 +1446,7 @@ class iCalendar_property_tzoffsetto extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_UTC_OFFSET;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
@@ -1424,6 +1471,7 @@ class iCalendar_property_class extends iCalendar_property {
     var $val_type    = RFC2445_TYPE_TEXT;
 
     function __construct() {
+        parent::__construct();
         $this->valid_parameters = array(
             RFC2445_XNAME => RFC2445_OPTIONAL
         );
index 8806653..e888b23 100644 (file)
@@ -1954,7 +1954,9 @@ function count_login_failures($mode, $username, $lastlogin) {
  */
 function print_object($object) {
     echo '<pre class="notifytiny">';
-    print_r($object);  // Direct to output because some objects get too big for memory otherwise!
+    // we may need a lot of memory here
+    raise_memory_limit(MEMORY_EXTRA);
+    echo s(print_r($object, true));
     echo '</pre>';
 }
 
index 6c30cf4..6156759 100644 (file)
@@ -1301,7 +1301,18 @@ class sqlsrv_native_moodle_database extends moodle_database {
         $timeoutmilli = $timeout * 1000;
 
         $fullname = $this->dbname.'-'.$this->prefix.'-session-'.$rowid;
-        $sql = "sp_getapplock '$fullname', 'Exclusive', 'Session',  $timeoutmilli";
+        // While this may work using proper {call sp_...} calls + binding +
+        // executing + consuming recordsets, the solution used for the mssql
+        // driver is working perfectly, so 100% mimic-ing that code.
+        // $sql = "sp_getapplock '$fullname', 'Exclusive', 'Session',  $timeoutmilli";
+        $sql = "BEGIN
+                    DECLARE @result INT
+                    EXECUTE @result = sp_getapplock @Resource='$fullname',
+                                                    @LockMode='Exclusive',
+                                                    @LockOwner='Session',
+                                                    @LockTimeout='$timeoutmilli'
+                    SELECT @result
+                END";
         $this->query_start($sql, null, SQL_QUERY_AUX);
         $result = sqlsrv_query($this->sqlsrv, $sql);
         $this->query_end($result);
index c3c68d8..1e46afb 100644 (file)
@@ -147,7 +147,8 @@ class tinymce_texteditor extends texteditor {
                     'theme_advanced_resizing_min_height' => 30,
                     'theme_advanced_toolbar_location' => "top",
                     'theme_advanced_statusbar_location' => "bottom",
-                    'spellchecker_rpc_url' => $CFG->wwwroot."/lib/editor/tinymce/tiny_mce/$this->version/plugins/spellchecker/rpc.php"
+                    'spellchecker_rpc_url' => $CFG->wwwroot."/lib/editor/tinymce/tiny_mce/$this->version/plugins/spellchecker/rpc.php",
+                    'spellchecker_languages' => get_config('editor_tinymce', 'spelllanguagelist')
                   );
 
         if ($xemoticon) {
index 9f406d6..db02700 100644 (file)
@@ -31,6 +31,10 @@ if ($ADMIN->fulltree) {
         'PSpell'=>'PSpell',
         'GoogleSpell'=>'Google Spell',
         'PSpellShell'=>'PSpellShell');
-    $settings->add(new admin_setting_configselect('editor_tinymce/spellengine', get_string('spellengine', 'admin'), '', 'GoogleSpell', $options));
+    $settings->add(new admin_setting_configselect('editor_tinymce/spellengine',
+            get_string('spellengine', 'admin'), '', 'GoogleSpell', $options));
+    $settings->add(new admin_setting_configtext('editor_tinymce/spelllanguagelist',
+            get_string('spelllanguagelist', 'admin'), '',
+            '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,' .
+            'Portuguese=pt,Spanish=es,Swedish=sv', PARAM_RAW));
 }
-
index 21779e8..9b22b01 100644 (file)
@@ -1002,7 +1002,7 @@ class file_storage {
         }
 
         if (!isset($file_record['filename'])) {
-            $file_record['filename'] == $file->get_filename();
+            $file_record['filename'] = $file->get_filename();
         }
 
         if (!isset($file_record['mimetype'])) {
index 3feb6de..bb5ed88 100644 (file)
@@ -136,6 +136,7 @@ class filter_manager {
             return new $filterclassname($context, $localconfig);
         }
 
+        // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
         $legacyfunctionname = basename($filtername) . '_filter';
         if (function_exists($legacyfunctionname)) {
             return new legacy_filter($legacyfunctionname, $context, $localconfig);
@@ -370,6 +371,8 @@ abstract class moodle_text_filter {
  * moodle_text_filter implementation that encapsulates an old-style filter that
  * only defines a function, not a class.
  *
+ * @deprecated since 2.2, see MDL-29995
+ * @todo will be out in 2.3, see MDL-29996
  * @package    core
  * @subpackage filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
@@ -475,6 +478,7 @@ class filterobject {
  * @return string the human-readable name for this filter.
  */
 function filter_get_name($filter) {
+    // TODO: should we be using pluginname here instead? , see MDL-29998
     list($type, $filter) = explode('/', $filter);
     switch ($type) {
         case 'filter':
@@ -485,6 +489,7 @@ function filter_get_name($filter) {
             }
             // Fall through to try the legacy location.
 
+        // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
         case 'mod':
             $strfiltername = get_string('filtername', $filter);
             if (substr($strfiltername, 0, 2) == '[[') {
@@ -508,7 +513,7 @@ function filter_get_name($filter) {
 function filter_get_all_installed() {
     global $CFG;
     $filternames = array();
-    // TODO: deprecated in 2.2, delete support for "mod" legacy filters location in 2.3. MDL-29996
+    // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
     $filterlocations = array('mod', 'filter');
     foreach ($filterlocations as $filterlocation) {
         // TODO: move get_list_of_plugins() to get_plugin_list()
index ee473eb..938fab6 100644 (file)
@@ -237,6 +237,10 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             _dependency_notchecked : function(elements, value) {
                 var lock = false;
                 elements.each(function(){
+                    if (this.getAttribute('type').toLowerCase()=='hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        return;
+                    }
                     if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
                         return;
                     }
@@ -250,6 +254,10 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             _dependency_checked : function(elements, value) {
                 var lock = false;
                 elements.each(function(){
+                    if (this.getAttribute('type').toLowerCase()=='hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        return;
+                    }
                     if (this.getAttribute('type').toLowerCase()=='radio' && this.get('value') != value) {
                         return;
                     }
@@ -272,10 +280,16 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             },
             _dependency_eq : function(elements, value) {
                 var lock = false;
+                var hidden_val = false;
                 elements.each(function(){
                     if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
                         return;
+                    } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        hidden_val = (this.get('value') == value);
+                        return;
                     } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
+                        lock = lock || hidden_val;
                         return;
                     }
                     //check for filepicker status
@@ -303,10 +317,16 @@ M.form.initFormDependencies = function(Y, formid, dependencies) {
             },
             _dependency_default : function(elements, value, ev) {
                 var lock = false;
+                var hidden_val = false;
                 elements.each(function(){
                     if (this.getAttribute('type').toLowerCase()=='radio' && !Y.Node.getDOMNode(this).checked) {
                         return;
+                    } else if (this.getAttribute('type').toLowerCase() == 'hidden' && !this.siblings('input[type=checkbox][name="' + this.get('name') + '"]').isEmpty()) {
+                        // This is the hidden input that is part of an advcheckbox.
+                        hidden_val = (this.get('value') != value);
+                        return;
                     } else if (this.getAttribute('type').toLowerCase() == 'checkbox' && !Y.Node.getDOMNode(this).checked) {
+                        lock = lock || hidden_val;
                         return;
                     }
                     //check for filepicker status
index f02469e..9b1ce04 100644 (file)
@@ -900,6 +900,30 @@ abstract class moodleform {
         return array();
     }
 
+    /**
+     * Helper used by {@link repeat_elements()}.
+     * @param int $i the index of this element.
+     * @param HTML_QuickForm_element $elementclone
+     * @param array $namecloned array of names
+     */
+    function repeat_elements_fix_clone($i, $elementclone, &$namecloned) {
+        $name = $elementclone->getName();
+        $namecloned[] = $name;
+
+        if (!empty($name)) {
+            $elementclone->setName($name."[$i]");
+        }
+
+        if (is_a($elementclone, 'HTML_QuickForm_header')) {
+            $value = $elementclone->_text;
+            $elementclone->setValue(str_replace('{no}', ($i+1), $value));
+
+        } else {
+            $value=$elementclone->getLabel();
+            $elementclone->setLabel(str_replace('{no}', ($i+1), $value));
+        }
+    }
+
     /**
      * Method to add a repeating group of elements to a form.
      *
@@ -942,19 +966,13 @@ abstract class moodleform {
         for ($i = 0; $i < $repeats; $i++) {
             foreach ($elementobjs as $elementobj){
                 $elementclone = fullclone($elementobj);
-                $name = $elementclone->getName();
-                $namecloned[] = $name;
-                if (!empty($name)) {
-                    $elementclone->setName($name."[$i]");
-                }
-                if (is_a($elementclone, 'HTML_QuickForm_header')) {
-                    $value = $elementclone->_text;
-                    $elementclone->setValue(str_replace('{no}', ($i+1), $value));
-
-                } else {
-                    $value=$elementclone->getLabel();
-                    $elementclone->setLabel(str_replace('{no}', ($i+1), $value));
+                $this->repeat_elements_fix_clone($i, $elementclone, $namecloned);
 
+                if ($elementclone instanceof HTML_QuickForm_group && !$elementclone->_appendName) {
+                    foreach ($elementclone->getElements() as $el) {
+                        $this->repeat_elements_fix_clone($i, $el, $namecloned);
+                    }
+                    $elementclone->setLabel(str_replace('{no}', $i + 1, $elementclone->getLabel()));
                 }
 
                 $mform->addElement($elementclone);
@@ -1970,7 +1988,11 @@ function validate_' . $this->_formName . '(frm) {
         } else if (is_a($element, 'HTML_QuickForm_hidden')) {
             return array();
 
-        } else if (method_exists($element, 'getPrivateName')) {
+        } else if (method_exists($element, 'getPrivateName') &&
+                !($element instanceof HTML_QuickForm_advcheckbox)) {
+            // The advcheckbox element implements a method called getPrivateName,
+            // but in a way that is not compatible with the generic API, so we
+            // have to explicitly exclude it.
             return array($element->getPrivateName());
 
         } else {
index b78fde9..69d294d 100644 (file)
@@ -1195,8 +1195,7 @@ function grade_update_mod_grades($modinstance, $userid=0) {
 /**
  * Remove grade letters for given context
  *
- * @global object
- * @param object $context
+ * @param context $context
  * @param bool $showfeedback
  */
 function remove_grade_letters($context, $showfeedback) {
@@ -1206,25 +1205,27 @@ function remove_grade_letters($context, $showfeedback) {
 
     $DB->delete_records('grade_letters', array('contextid'=>$context->id));
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('letters', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('letters', 'grades'), 'notifysuccess');
     }
 }
+
 /**
  * Remove all grade related course data - history is kept
  *
- * @global object
  * @param int $courseid
  * @param bool $showfeedback print feedback
  */
 function remove_course_grades($courseid, $showfeedback) {
     global $DB, $OUTPUT;
 
+    $fs = get_file_storage();
     $strdeleted = get_string('deleted');
 
     $course_category = grade_category::fetch_course_category($courseid);
     $course_category->delete('coursedelete');
+    $fs->delete_area_files(get_context_instance(CONTEXT_COURSE, $courseid)->id, 'grade', 'feedback');
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('grades', 'grades').', '.get_string('items', 'grades').', '.get_string('categories', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('grades', 'grades').', '.get_string('items', 'grades').', '.get_string('categories', 'grades'), 'notifysuccess');
     }
 
     if ($outcomes = grade_outcome::fetch_all(array('courseid'=>$courseid))) {
@@ -1234,7 +1235,7 @@ function remove_course_grades($courseid, $showfeedback) {
     }
     $DB->delete_records('grade_outcomes_courses', array('courseid'=>$courseid));
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('outcomes', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('outcomes', 'grades'), 'notifysuccess');
     }
 
     if ($scales = grade_scale::fetch_all(array('courseid'=>$courseid))) {
@@ -1243,12 +1244,12 @@ function remove_course_grades($courseid, $showfeedback) {
         }
     }
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('scales'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('scales'), 'notifysuccess');
     }
 
     $DB->delete_records('grade_settings', array('courseid'=>$courseid));
     if ($showfeedback) {
-        echo $OUTPUT->notification($strdeleted.' - '.get_string('settings', 'grades'));
+        echo $OUTPUT->notification($strdeleted.' - '.get_string('settings', 'grades'), 'notifysuccess');
     }
 }
 
index c6cb423..566bc8c 100644 (file)
@@ -106,7 +106,27 @@ function install_init_dataroot($dataroot, $dirpermissions) {
         return false; // we can not continue
     }
 
-    // now create the lang folder - we need it and it makes sure we can really write in dataroot
+    // create the directory for $CFG->tempdir
+    if (!is_dir("$dataroot/temp")) {
+        if (!mkdir("$dataroot/temp", $dirpermissions, true)) {
+            return false;
+        }
+    }
+    if (!is_writable("$dataroot/temp")) {
+        return false; // we can not continue
+    }
+
+    // create the directory for $CFG->cachedir
+    if (!is_dir("$dataroot/cache")) {
+        if (!mkdir("$dataroot/cache", $dirpermissions, true)) {
+            return false;
+        }
+    }
+    if (!is_writable("$dataroot/cache")) {
+        return false; // we can not continue
+    }
+
+    // create the directory for $CFG->langotherroot
     if (!is_dir("$dataroot/lang")) {
         if (!mkdir("$dataroot/lang", $dirpermissions, true)) {
             return false;
index b5ea458..d6bba11 100644 (file)
@@ -1437,8 +1437,6 @@ function purge_all_caches() {
     // hack: this script may get called after the purifier was initialised,
     // but we do not want to verify repeatedly this exists in each call
     make_cache_directory('htmlpurifier');
-
-    clearstatcache();
 }
 
 /**
@@ -4287,58 +4285,122 @@ function delete_course($courseorid, $showfeedback = true) {
  * This function does not verify any permissions.
  *
  * Please note this function also deletes all user enrolments,
- * enrolment instances and role assignments.
+ * enrolment instances and role assignments by default.
+ *
+ * $options:
+ *  - 'keep_roles_and_enrolments' - false by default
+ *  - 'keep_groups_and_groupings' - false by default
  *
  * @param int $courseid The id of the course that is being deleted
  * @param bool $showfeedback Whether to display notifications of each action the function performs.
+ * @param array $options extra options
  * @return bool true if all the removals succeeded. false if there were any failures. If this
  *             method returns false, some of the removals will probably have succeeded, and others
  *             failed, but you have no way of knowing which.
  */
-function remove_course_contents($courseid, $showfeedback = true) {
+function remove_course_contents($courseid, $showfeedback = true, array $options = null) {
     global $CFG, $DB, $OUTPUT;
     require_once($CFG->libdir.'/completionlib.php');
     require_once($CFG->libdir.'/questionlib.php');
     require_once($CFG->libdir.'/gradelib.php');
     require_once($CFG->dirroot.'/group/lib.php');
     require_once($CFG->dirroot.'/tag/coursetagslib.php');
+    require_once($CFG->dirroot.'/comment/lib.php');
+    require_once($CFG->dirroot.'/rating/lib.php');
 
-    $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
-    $context = get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST);
+    // NOTE: these concatenated strings are suboptimal, but it is just extra info...
+    $strdeleted = get_string('deleted').' - ';
 
-    $strdeleted = get_string('deleted');
+    // Some crazy wishlist of stuff we should skip during purging of course content
+    $options = (array)$options;
 
-    // Delete course completion information,
-    // this has to be done before grades and enrols
+    $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
+    $coursecontext = context_course::instance($courseid);
+    $fs = get_file_storage();
+
+    // Delete course completion information, this has to be done before grades and enrols
     $cc = new completion_info($course);
     $cc->clear_criteria();
-
-    // remove roles and enrolments
-    role_unassign_all(array('contextid'=>$context->id), true);
-    enrol_course_delete($course);
-
-    // Clean up course formats (iterate through all formats in the even the course format was ever changed)
-    $formats = get_plugin_list('format');
-    foreach ($formats as $format=>$formatdir) {
-        $formatdelete = 'format_'.$format.'_delete_course';
-        $formatlib    = "$formatdir/lib.php";
-        if (file_exists($formatlib)) {
-            include_once($formatlib);
-            if (function_exists($formatdelete)) {
-                if ($showfeedback) {
-                    echo $OUTPUT->notification($strdeleted.' '.$format);
-                }
-                $formatdelete($course->id);
-            }
-        }
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('completion', 'completion'), 'notifysuccess');
     }
 
     // Remove all data from gradebook - this needs to be done before course modules
     // because while deleting this information, the system may need to reference
     // the course modules that own the grades.
     remove_course_grades($courseid, $showfeedback);
-    remove_grade_letters($context, $showfeedback);
+    remove_grade_letters($coursecontext, $showfeedback);
+
+    // Delete course blocks in any all child contexts,
+    // they may depend on modules so delete them first
+    $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2
+    foreach ($childcontexts as $childcontext) {
+        blocks_delete_all_for_context($childcontext->id);
+    }
+    unset($childcontexts);
+    blocks_delete_all_for_context($coursecontext->id);
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('type_block_plural', 'plugin'), 'notifysuccess');
+    }
+
+    // Delete every instance of every module,
+    // this has to be done before deleting of course level stuff
+    $locations = get_plugin_list('mod');
+    foreach ($locations as $modname=>$moddir) {
+        if ($modname === 'NEWMODULE') {
+            continue;
+        }
+        if ($module = $DB->get_record('modules', array('name'=>$modname))) {
+            include_once("$moddir/lib.php");                 // Shows php warning only if plugin defective
+            $moddelete = $modname .'_delete_instance';       // Delete everything connected to an instance
+            $moddeletecourse = $modname .'_delete_course';   // Delete other stray stuff (uncommon)
+
+            if ($instances = $DB->get_records($modname, array('course'=>$course->id))) {
+                foreach ($instances as $instance) {
+                    if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) {
+                        /// Delete activity context questions and question categories
+                        question_delete_activity($cm,  $showfeedback);
+                    }
+                    if (function_exists($moddelete)) {
+                        // This purges all module data in related tables, extra user prefs, settings, etc.
+                        $moddelete($instance->id);
+                    } else {
+                        // NOTE: we should not allow installation of modules with missing delete support!
+                        debugging("Defective module '$modname' detected when deleting course contents: missing function $moddelete()!");
+                        $DB->delete_records($modname, array('id'=>$instance->id));
+                    }
 
+                    if ($cm) {
+                        // Delete cm and its context - orphaned contexts are purged in cron in case of any race condition
+                        context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
+                        $DB->delete_records('course_modules', array('id'=>$cm->id));
+                    }
+                }
+            }
+            if (function_exists($moddeletecourse)) {
+                // Execute ptional course cleanup callback
+                $moddeletecourse($course, $showfeedback);
+            }
+            if ($instances and $showfeedback) {
+                echo $OUTPUT->notification($strdeleted.get_string('pluginname', $modname), 'notifysuccess');
+            }
+        } else {
+            // Ooops, this module is not properly installed, force-delete it in the next block
+        }
+    }
+    // We have tried to delete everything the nice way - now let's force-delete any remaining module data
+    $cms = $DB->get_records('course_modules', array('course'=>$course->id));
+    foreach ($cms as $cm) {
+        if ($module = $DB->get_record('module', array('id'=>$cm->module))) {
+            try {
+                $DB->delete_records($module->name, array('id'=>$cm->instance));
+            } catch (Exception $e) {
+                // Ignore weird or missing table problems
+            }
+        }
+        context_helper::delete_instance(CONTEXT_MODULE, $cm->id);
+        $DB->delete_records('course_modules', array('id'=>$cm->id));
+    }
     // Remove all data from availability and completion tables that is associated
     // with course-modules belonging to this course. Note this is done even if the
     // features are not enabled now, in case they were enabled previously
@@ -4348,101 +4410,122 @@ function remove_course_contents($courseid, $showfeedback = true) {
     $DB->delete_records_select('course_modules_availability',
            'coursemoduleid IN (SELECT id from {course_modules} WHERE course=?)',
            array($courseid));
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('type_mod_plural', 'plugin'), 'notifysuccess');
+    }
 
-    // Delete course blocks - they may depend on modules so delete them first
-    blocks_delete_all_for_context($context->id);
+    // Cleanup the rest of plugins
+    $cleanuplugintypes = array('report', 'coursereport', 'format');
+    foreach ($cleanuplugintypes as $type) {
+        $plugins = get_plugin_list_with_function($type, 'delete_course', 'lib.php');
+        foreach ($plugins as $plugin=>$pluginfunction) {
+            $pluginfunction($course->id, $showfeedback);
+        }
+        if ($showfeedback) {
+            echo $OUTPUT->notification($strdeleted.get_string('type_'.$type.'_plural', 'plugin'), 'notifysuccess');
+        }
+    }
 
-    // Delete every instance of every module
-    if ($allmods = $DB->get_records('modules') ) {
-        foreach ($allmods as $mod) {
-            $modname = $mod->name;
-            $modfile = $CFG->dirroot .'/mod/'. $modname .'/lib.php';
-            $moddelete = $modname .'_delete_instance';       // Delete everything connected to an instance
-            $moddeletecourse = $modname .'_delete_course';   // Delete other stray stuff (uncommon)
-            $count=0;
-            if (file_exists($modfile)) {
-                include_once($modfile);
-                if (function_exists($moddelete)) {
-                    if ($instances = $DB->get_records($modname, array('course'=>$course->id))) {
-                        foreach ($instances as $instance) {
-                            if ($cm = get_coursemodule_from_instance($modname, $instance->id, $course->id)) {
-                                /// Delete activity context questions and question categories
-                                question_delete_activity($cm,  $showfeedback);
-                            }
-                            if ($moddelete($instance->id)) {
-                                $count++;
+    // Delete questions and question categories
+    question_delete_course($course, $showfeedback);
+    if ($showfeedback) {
+        echo $OUTPUT->notification($strdeleted.get_string('questions', 'question'), 'notifysuccess');
+    }
 
-                            } else {
-                                echo $OUTPUT->notification('Could not delete '. $modname .' instance '. $instance->id .' ('. format_string($instance->name) .')');
-                            }
-                            if ($cm) {
-                                // delete cm and its context in correct order
-                                delete_context(CONTEXT_MODULE, $cm->id); // some callbacks may try to fetch context, better delete first
-                                $DB->delete_records('course_modules', array('id'=>$cm->id));
-                            }
-                        }
-                    }
-                } else {
-                    //note: we should probably delete these anyway
-                    echo $OUTPUT->notification('Function '.$moddelete.'() doesn\'t exist!');
-                }
+    // Make sure there are no subcontexts left - all valid blocks and modules should be already gone
+    $childcontexts = $coursecontext->get_child_contexts(); // returns all subcontexts since 2.2
+    foreach ($childcontexts as $childcontext) {
+        $childcontext->delete();
+    }
+    unset($childcontexts);
 
-                if (function_exists($moddeletecourse)) {
-                    $moddeletecourse($course, $showfeedback);
-                }
-            }
-            if ($showfeedback) {
-                echo $OUTPUT->notification($strdeleted .' '. $count .' x '. $modname);
-            }
+    // Remove all roles and enrolments by default
+    if (empty($options['keep_roles_and_enrolments'])) {
+        // this hack is used in restore when deleting contents of existing course
+        role_unassign_all(array('contextid'=>$coursecontext->id), true);
+        enrol_course_delete($course);
+        if ($showfeedback) {
+            echo $OUTPUT->notification($strdeleted.get_string('type_enrol_plural', 'plugin'), 'notifysuccess');
         }
     }
 
     // Delete any groups, removing members and grouping/course links first.
-    groups_delete_groupings($course->id, $showfeedback);
-    groups_delete_groups($course->id, $showfeedback);
+    if (empty($options['keep_groups_and_groupings'])) {
+        groups_delete_groupings($course->id, $showfeedback);
+        groups_delete_groups($course->id, $showfeedback);
+    }
 
-    // Delete questions and question categories
-    question_delete_course($course, $showfeedback);
+    // filters be gone!
+    filter_delete_all_for_context($coursecontext->id);
+
+    // die comments!
+    comment::delete_comments($coursecontext->id);
+
+    // ratings are history too
+    $delopt = new stdclass();
+    $delopt->contextid = $coursecontext->id;
+    $rm = new rating_manager();
+    $rm->delete_ratings($delopt);
 
     // Delete course tags
     coursetag_delete_course_tags($course->id, $showfeedback);
 
-    // Delete legacy files (just in case some files are still left there after conversion to new file api)
-    fulldelete($CFG->dataroot.'/'.$course->id);
+    // Delete calendar events
+    $DB->delete_records('event', array('courseid'=>$course->id));
+    $fs->delete_area_files($coursecontext->id, 'calendar');
 
-    // cleanup course record - remove links to delted stuff
-    $oldcourse = new stdClass();
-    $oldcourse->id                = $course->id;
-    $oldcourse->summary           = '';
-    $oldcourse->modinfo           = NULL;
-    $oldcourse->legacyfiles       = 0;
-    $oldcourse->defaultgroupingid = 0;
-    $oldcourse->enablecompletion  = 0;
-    $DB->update_record('course', $oldcourse);
-
-    // Delete all related records in other tables that may have a courseid
+    // Delete all related records in other core tables that may have a courseid
     // This array stores the tables that need to be cleared, as
     // table_name => column_name that contains the course id.
     $tablestoclear = array(
-        'event' => 'courseid', // Delete events
-        'log' => 'course', // Delete logs
-        'course_sections' => 'course', // Delete any course stuff
-        'course_modules' => 'course',
-        'course_display' => 'course',
-        'backup_courses' => 'courseid', // Delete scheduled backup stuff
-        'user_lastaccess' => 'courseid',
+        'log' => 'course',               // Course logs (NOTE: this might be changed in the future)
+        'backup_courses' => 'courseid',  // Scheduled backup stuff
+        'user_lastaccess' => 'courseid', // User access info
     );
     foreach ($tablestoclear as $table => $col) {
         $DB->delete_records($table, array($col=>$course->id));
     }
 
-    // Delete all remaining stuff linked to context,
-    // such as remaining roles, files, comments, etc.
-    // Keep the context record for now.
-    delete_context(CONTEXT_COURSE, $course->id, false);
+    // delete all course backup files
+    $fs->delete_area_files($coursecontext->id, 'backup');
 
-    //trigger events
-    $course->context = $context; // you can not access context in cron event later after course is deleted
+    // cleanup course record - remove links to deleted stuff
+    $oldcourse = new stdClass();
+    $oldcourse->id               = $course->id;
+    $oldcourse->summary          = '';
+    $oldcourse->modinfo          = NULL;
+    $oldcourse->legacyfiles      = 0;
+    $oldcourse->enablecompletion = 0;
+    if (!empty($options['keep_groups_and_groupings'])) {
+        $oldcourse->defaultgroupingid = 0;
+    }
+    $DB->update_record('course', $oldcourse);
+
+    // Delete course sections and user selections
+    $DB->delete_records('course_sections', array('course'=>$course->id));
+    $DB->delete_records('course_display', array('course'=>$course->id));
+
+    // delete legacy, section and any other course files
+    $fs->delete_area_files($coursecontext->id, 'course'); // files from summary and section
+
+    // Delete all remaining stuff linked to context such as files, comments, ratings, etc.
+    if (empty($options['keep_roles_and_enrolments']) and empty($options['keep_groups_and_groupings'])) {
+        // Easy, do not delete the context itself...
+        $coursecontext->delete_content();
+
+    } else {
+        // Hack alert!!!!
+        // We can not drop all context stuff because it would bork enrolments and roles,
+        // there might be also files used by enrol plugins...
+    }
+
+    // Delete legacy files - just in case some files are still left there after conversion to new file api,
+    // also some non-standard unsupported plugins may try to store something there
+    fulldelete($CFG->dataroot.'/'.$course->id);
+
+    // Finally trigger the event
+    $course->context = $coursecontext; // you can not access context in cron event later after course is deleted
+    $course->options = $options;       // not empty if we used any crazy hack
     events_trigger('course_content_removed', $course);
 
     return true;
@@ -6213,7 +6296,17 @@ class core_string_manager implements string_manager {
             }
             if (!isset($string[$identifier])) {
                 // the string is still missing - should be fixed by developer
-                debugging("Invalid get_string() identifier: '$identifier' or component '$component'", DEBUG_DEVELOPER);
+                list($plugintype, $pluginname) = normalize_component($component);
+                if ($plugintype == 'core') {
+                    $file = "lang/en/{$component}.php";
+                } else if ($plugintype == 'mod') {
+                    $file = "mod/{$pluginname}/lang/en/{$pluginname}.php";
+                } else {
+                    $path = get_plugin_directory($plugintype, $pluginname);
+                    $file = "{$path}/lang/en/{$plugintype}_{$pluginname}.php";
+                }
+                debugging("Invalid get_string() identifier: '{$identifier}' or component '{$component}'. " .
+                        "Perhaps you are missing \$string['{$identifier}'] = ''; in {$file}?", DEBUG_DEVELOPER);
                 return "[[$identifier]]";
             }
         }
@@ -7690,7 +7783,7 @@ function plugin_callback($type, $name, $feature, $action, $options = null, $defa
 
     $component = clean_param($type . '_' . $name, PARAM_COMPONENT);
     if (empty($component)) {
-        throw coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
+        throw new coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
     }
 
     list($type, $name) = normalize_component($component);
@@ -7701,7 +7794,7 @@ function plugin_callback($type, $name, $feature, $action, $options = null, $defa
 
     $dir = get_component_directory($component);
     if (empty($dir)) {
-        throw coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
+        throw new coding_exception('Invalid component used in plugin_callback():' . $type . '_' . $name);
     }
 
     // Load library and look for function
@@ -7748,7 +7841,7 @@ function plugin_supports($type, $name, $feature, $default = NULL) {
 
     $component = clean_param($type . '_' . $name, PARAM_COMPONENT);
     if (empty($component)) {
-        throw coding_exception('Invalid component used in plugin_supports():' . $type . '_' . $name);
+        throw new coding_exception('Invalid component used in plugin_supports():' . $type . '_' . $name);
     }
 
     $function = null;
@@ -9867,9 +9960,12 @@ function remove_dir($dir, $content_only=false) {
     }
     closedir($handle);
     if ($content_only) {
+        clearstatcache(); // make sure file stat cache is properly invalidated
         return $result;
     }
-    return rmdir($dir); // if anything left the result will be false, no need for && $result
+    $result = rmdir($dir); // if anything left the result will be false, no need for && $result
+    clearstatcache(); // make sure file stat cache is properly invalidated
+    return $result;
 }
 
 /**
index 081a0e6..6e605db 100644 (file)
@@ -343,7 +343,7 @@ function question_delete_question($questionid) {
 /**
  * All question categories and their questions are deleted for this course.
  *
- * @param object $mod an object representing the activity
+ * @param stdClass $course an object representing the activity
  * @param boolean $feedback to specify if the process must output a summary of its work
  * @return boolean
  */
index 4df0be9..8988c61 100644 (file)
@@ -280,7 +280,7 @@ function resourcelib_embed_flashvideo($fullurl, $title, $clicktoopen) {
     }
     $output = '<div class="resourcecontent resourceflv">';
     $output .= html_writer::tag('span', $clicktoopen, array('id'=>$id, 'class'=>'resourcemediaplugin resourcemediaplugin_flv', 'title'=>$title));
-    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, $fullurl, $width, $height, $autosize)));
+    $output .= html_writer::script(js_writer::function_call('M.util.add_video_player', array($id, rawurlencode($fullurl), $width, $height, $autosize)));
     $output .= '</div>';
 
     return $output;
index 36973a5..5cec604 100644 (file)
@@ -409,12 +409,8 @@ function rss_full_tag($tag,$level=0,$endline=true,$content,$attributes=null) {
 }
 
 /**
- * Adds RSS Media Enclosures for "podcasting" by examining links to media files,
- * and attachments which are media files. Please note that the RSS that is
- * produced cannot be strictly valid for the linked files, since we do not know
- * the files' sizes and cannot include them in the "length" attribute. At
- * present, the validity (and therefore the podcast working in most software)
- * can only be ensured for attachments, and not for links.
+ * Adds RSS Media Enclosures for "podcasting" by including attachments that
+ * are specified in the item->attachments field.
  * Note also that iTunes does some things very badly - one thing it does is
  * refuse to download ANY of your files if you're using "file.php?file=blah"
  * and can't use the more elegant "file.php/blah" slasharguments setting. It
@@ -433,15 +429,11 @@ function rss_add_enclosures($item){
     global $CFG;
 
     $returnstring = '';
-    $rss_text = $item->description;
 
     // list of media file extensions and their respective mime types
     include_once($CFG->libdir.'/filelib.php');
     $mediafiletypes = get_mimetypes_array();
 
-    // regular expression (hopefully) matching all links to media files
-    $medialinkpattern = '@href\s*=\s*(\'|")(\S+(' . implode('|', array_keys($mediafiletypes)) . '))\1@Usie';
-
     // take into account attachments (e.g. from forum) - with these, we are able to know the file size
     if (isset($item->attachments) && is_array($item->attachments)) {
         foreach ($item->attachments as $attachment){
@@ -455,23 +447,5 @@ function rss_add_enclosures($item){
         }
     }
 
-    if (!preg_match_all($medialinkpattern, $rss_text, $matches)){
-        return $returnstring;
-    }
-
-    // loop over matches of regular expression
-    for ($i = 0; $i < count($matches[2]); $i++){
-        $url = htmlspecialchars($matches[2][$i]);
-        $extension = strtolower($matches[3][$i]);
-        if (isset($mediafiletypes[$extension]['type'])) {
-            $type = $mediafiletypes[$extension]['type'];
-        } else {
-            $type = 'document/unknown';
-        }
-
-        // the rss_*_tag functions can't deal with methods, unfortunately
-        $returnstring .= "\n<enclosure url='$url' type='$type' />\n";
-    }
-
     return $returnstring;
 }
index 8f8a255..2b51b1b 100644 (file)
@@ -816,12 +816,14 @@ if (!empty($CFG->customscripts)) {
     }
 }
 
-// in the first case, ip in allowed list will be performed first
-// for example, client IP is 192.168.1.1
-// 192.168 subnet is an entry in allowed list
-// 192.168.1.1 is banned in blocked list
-// This ip will be banned finally
-if (!empty($CFG->allowbeforeblock)) { // allowed list processed before blocked list?
+if (CLI_SCRIPT and !defined('WEB_CRON_EMULATED_CLI') and !PHPUNIT_SCRIPT) {
+    // no ip blocking
+} else if (!empty($CFG->allowbeforeblock)) { // allowed list processed before blocked list?
+    // in this case, ip in allowed list will be performed first
+    // for example, client IP is 192.168.1.1
+    // 192.168 subnet is an entry in allowed list
+    // 192.168.1.1 is banned in blocked list
+    // This ip will be banned finally
     if (!empty($CFG->allowedip)) {
         if (!remoteip_in_list($CFG->allowedip)) {
             die(get_string('ipblocked', 'admin'));
index 205cd32..c206d10 100644 (file)
@@ -1077,6 +1077,10 @@ function redirect_if_major_upgrade_required() {
  * files outside of dataroot if you supply custom paths for some settings in config.php.
  * This function does not verify that the directory is writable.
  *
+ * NOTE: this function uses current file stat cache,
+ *       please use clearstatcache() before this if you expect that the
+ *       directories may have been removed recently from a different request.
+ *
  * @param string $dir absolute directory path
  * @param boolean $create directory if does not exist
  * @param boolean $recursive create directory recursively
index 72af810..edcc829 100644 (file)
@@ -41,6 +41,13 @@ if (is_restored_user($username)) {
 }
 $user = authenticate_user_login($username, $password);
 if (!empty($user)) {
+
+    //Non admin can not authenticate if maintenance mode
+    $hassiteconfig = has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM), $user);
+    if (!empty($CFG->maintenance_enabled) and !$hassiteconfig) {
+        throw new moodle_exception('sitemaintenance', 'admin');
+    }
+
     if (isguestuser($user)) {
         throw new moodle_exception('noguest');
     }
index 58583c3..d88fe52 100644 (file)
@@ -333,5 +333,17 @@ $capabilities = array(
             'student' => CAP_ALLOW,
         )
     ),
+    'mod/forum:addquestion' => array(
+
+        'riskbitmask' => RISK_SPAM,
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_MODULE,
+        'archetypes' => array(
+            'teacher' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW
+        )
+    ),
 );
 
index 6ab1c50..19da7f9 100644 (file)
@@ -149,6 +149,7 @@ $string['exportdiscussion'] = 'Export whole discussion';
 $string['forcessubscribe'] = 'This forum forces everyone to be subscribed';
 $string['forum'] = 'Forum';
 $string['forum:addnews'] = 'Add news';
+$string['forum:addquestion'] = 'Add question';
 $string['forumauthorhidden'] = 'Author (hidden)';
 $string['forumblockingalmosttoomanyposts'] = 'You are approaching the posting threshold. You have posted {$a->numposts} times in the last {$a->blockperiod} and the limit is {$a->blockafter} posts.';
 $string['forumbodyhidden'] = 'This post cannot be viewed by you, probably because you have not posted in the discussion or the maximum editing time hasn\'t passed yet.';
index 4a0e2f7..48344e4 100644 (file)
@@ -4824,6 +4824,8 @@ function forum_user_can_post_discussion($forum, $currentgroup=null, $unused=-1,
 
     if ($forum->type == 'news') {
         $capname = 'mod/forum:addnews';
+    } else if ($forum->type == 'qanda') {
+        $capname = 'mod/forum:addquestion';
     } else {
         $capname = 'mod/forum:startdiscussion';
     }
index 06d11fa..8ebae36 100644 (file)
@@ -26,7 +26,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$module->version   = 2011110600;
+$module->version   = 2011110601;
 $module->requires  = 2011110200;  // Requires this Moodle version
 $module->cron      = 60;
 $module->component = 'mod_forum';
\ No newline at end of file
index fa1cf36..fe1cf25 100644 (file)
@@ -158,6 +158,7 @@ $string['endoflesson'] = 'End of lesson';
 $string['enteredthis'] = 'entered this.';
 $string['entername'] = 'Enter a nickname for the high scores list';
 $string['enterpassword'] = 'Please enter the password:';
+$string['emptypassword'] = 'Password cannot be empty';
 $string['eolstudentoutoftime'] = 'Attention:  You ran out of time for this lesson.  Your last answer may not have counted if it was answered after the time was up.';
 $string['eolstudentoutoftimenoanswers'] = 'You did not answer any questions.  You have received a 0 for this lesson.';
 $string['essay'] = 'Essay';
index b0dbfa7..3b2f1a2 100644 (file)
@@ -339,6 +339,9 @@ class mod_lesson_mod_form extends moodleform_mod {
         if (empty($data['maxtime']) and !empty($data['timed'])) {
             $errors['timedgrp'] = get_string('err_numeric', 'form');
         }
+        if (!empty($data['usepassword']) && empty($data['password'])) {
+            $errors['password'] = get_string('emptypassword', 'lesson');
+        }
 
         return $errors;
     }
index f963409..7ede7f1 100644 (file)
@@ -384,4 +384,29 @@ bank window's title is prominent enough*/
 .qnum label {padding-right: 0.25em;}
 
 /** settings.php */
-#adminquizreviewoptions {margin-bottom: 0.5em;}
\ No newline at end of file
+#adminquizreviewoptions {margin-bottom: 0.5em;}
+
+/* RTL Mode */
+
+#page-mod-quiz-mod.dir-rtl #reviewoptionshdr .fitem, .dir-rtl  #adminquizreviewoptions .group { width: 23%;float: right;}
+#page-mod-quiz-mod.dir-rtl #reviewoptionshdr .fitemtitle, .dir-rtl  #adminquizreviewoptions .fitemtitle {text-align: right;}
+#page-mod-quiz-mod.dir-rtl #reviewoptionshdr fieldset.fgroup span, .dir-rtl  #adminquizreviewoptions span {clear: right;float: right;}
+#page-mod-quiz-edit.dir-rtl div.quizpage span.pagetitle {float: right;}
+#page-mod-quiz-edit.dir-rtl div.quizpage .pagecontent {float: right;}
+#page-mod-quiz-edit.dir-rtl div.question {clear: right;}
+#page-mod-quiz-edit.dir-rtl div.question div.qnum {float: right;}
+#page-mod-quiz-edit.dir-rtl div.editq div.question div.content {float: right;height: 40px;}
+#page-mod-quiz-edit.dir-rtl div.question div.content div.points {left: 50px;right:auto;}
+#page-mod-quiz-edit.dir-rtl div.question div.content div.questioncontrols {float: left;left: 0.3em; right:auto;}
+#page-mod-quiz-edit.dir-rtl .editq div.question div.content .singlequestion a .questionname,
+#page-mod-quiz-edit.dir-rtl .editq div.question div.content .singlequestion a .questiontext {float: right; padding-right: 0.3em;}
+#page-mod-quiz-edit.dir-rtl div.question div.content .questiontext,
+#page-mod-quiz-edit.dir-rtl #categoryquestions .questiontext {padding-right: 0.3em;}
+#page-mod-quiz-edit.dir-rtl .editq div.questioncontentcontainer div.singlequestion img {float: right;}
+#page-mod-quiz-edit.dir-rtl .editq div.question div.content .questionpreview {float: right;}
+#page-mod-quiz-edit.dir-rtl div.question div.content div.qorder {left: 50px;right:auto;}
+#page-mod-quiz-edit.dir-rtl .reorder div.question div.content { float: right;}
+#page-mod-quiz-edit.dir-rtl .quizpagedelete {left: 0.2em;right:auto;}
+#page-mod-quiz-edit.dir-rtl div.quizcontents {clear: right;float: right;}
+#page-mod-quiz-edit.dir-rtl .questionbankwindow.block {float: left;}
+#page-question-edit.dir-rtl td.creatorname, #page-question-edit.dir-rtl td.modifiername {text-align: center;}
\ No newline at end of file
index de43634..c926c12 100644 (file)
@@ -252,7 +252,8 @@ function scorm_parse_aicc($scorm) {
             if ($ss = $DB->get_record('scorm_scoes', array('scorm'=>$scorm->id,
                                                            'identifier'=>$sco->identifier))) {
                 $id = $ss->id;
-                $DB->update_record('scorm_scoes', $sco);
+                $sco->id = $id;
+                $DB->update_record('scorm_scoes',$sco);
                 unset($oldscoes[$id]);
             } else {
                 $id = $DB->insert_record('scorm_scoes', $sco);
index 75481d8..5d6a872 100644 (file)
@@ -574,19 +574,49 @@ function SCORMapi1_2() {
             } else {
                 element = parent+'.'+property;
                 expression = new RegExp(CMIIndex,'g');
+
+                // get the generic name for this element (e.g. convert 'cmi.interactions.1.id' to 'cmi.interactions.n.id')
                 elementmodel = String(element).replace(expression,'.n.');
-                if (elementmodel != "cmi.core.session_time") {
-                    if ((typeof eval('datamodel["'+elementmodel+'"]')) != "undefined") {
-                        if (eval('datamodel["'+elementmodel+'"].mod') != 'r') {
+
+                // ignore the session time element
+                if (element != "cmi.core.session_time") {
+
+                    // check if this specific element is not defined in the datamodel,
+                    // but the generic element name is
+                    if ((eval('typeof datamodel["'+element+'"]')) == "undefined"
+                        && (eval('typeof datamodel["'+elementmodel+'"]')) != "undefined") {
+
+                        // add this specific element to the data model (by cloning
+                        // the generic element) so we can track changes to it
+                        eval('datamodel["'+element+'"]=CloneObj(datamodel["'+elementmodel+'"]);');
+                    }
+
+                    // check if the current element exists in the datamodel
+                    if ((typeof eval('datamodel["'+element+'"]')) != "undefined") {
+
+                        // make sure this is not a read only element
+                        if (eval('datamodel["'+element+'"].mod') != 'r') {
+
                             elementstring = '&'+underscore(element)+'='+encodeURIComponent(data[property]);
-                            if ((typeof eval('datamodel["'+elementmodel+'"].defaultvalue')) != "undefined") {
-                                if (eval('datamodel["'+elementmodel+'"].defaultvalue') != data[property] || eval('typeof(datamodel["'+elementmodel+'"].defaultvalue)') != typeof(data[property])) {
+
+                            // check if the element has a default value
+                            if ((typeof eval('datamodel["'+element+'"].defaultvalue')) != "undefined") {
+
+                                // check if the default value is different from the current value
+                                if (eval('datamodel["'+element+'"].defaultvalue') != data[property]
+                                    || eval('typeof(datamodel["'+element+'"].defaultvalue)') != typeof(data[property])) {
+
+                                    // append the URI fragment to the string we plan to commit
                                     datastring += elementstring;
-                                    eval('datamodel["'+elementmodel+'"].defaultvalue=data[property];');
+
+                                    // update the element default to reflect the current committed value
+                                    eval('datamodel["'+element+'"].defaultvalue=data[property];');
                                 }
                             } else {
+                                // append the URI fragment to the string we plan to commit
                                 datastring += elementstring;
-                                eval('datamodel["'+elementmodel+'"].defaultvalue=data[property];');
+                                // no default value for the element, so set it now
+                                eval('datamodel["'+element+'"].defaultvalue=data[property];');
                             }
                         }
                     }
@@ -596,6 +626,19 @@ function SCORMapi1_2() {
         return datastring;
     }
 
+    function CloneObj(obj){
+        if(obj == null || typeof(obj) != 'object') {
+            return obj;
+        }
+
+        var temp = new obj.constructor(); // changed (twice)
+        for(var key in obj) {
+            temp[key] = CloneObj(obj[key]);
+        }
+
+        return temp;
+    }
+
     function StoreData(data,storetotaltime) {
         if (storetotaltime) {
             if (cmi.core.lesson_status == 'not attempted') {
index dc3db4a..081b68b 100644 (file)
@@ -876,26 +876,4 @@ class qformat_default {
         return html_to_text(format_text($question->questiontext,
                 $question->questiontextformat, $formatoptions), 0, false);
     }
-
-    /**
-     * convert files into text output in the given format.
-     * @param array
-     * @param string encoding method
-     * @return string $string
-     */
-    protected function writefiles($files, $encoding='base64') {
-        if (empty($files)) {
-            return '';
-        }
-        $string = '';
-        foreach ($files as $file) {
-            if ($file->is_directory()) {
-                continue;
-            }
-            $string .= '<file name="' . $file->get_filename() . '" encoding="' . $encoding . '">';
-            $string .= base64_encode($file->get_content());
-            $string .= '</file>';
-        }
-        return $string;
-    }
 }
index 80ff9c1..081444d 100644 (file)
@@ -1051,6 +1051,27 @@ class qformat_xml extends qformat_default {
         return $xml;
     }
 
+    /**
+     * Generte the XML to represent some files.
+     * @param array of store array of stored_file objects.
+     * @return string $string the XML.
+     */
+    public function write_files($files) {
+        if (empty($files)) {
+            return '';
+        }
+        $string = '';
+        foreach ($files as $file) {
+            if ($file->is_directory()) {
+                continue;
+            }
+            $string .= '<file name="' . $file->get_filename() . '" encoding="base64">';
+            $string .= base64_encode($file->get_content());
+            $string .= '</file>';
+        }
+        return $string;
+    }
+
     protected function presave_process($content) {
         // Override to allow us to add xml headers and footers
         return '<?xml version="1.0" encoding="UTF-8"?>
@@ -1110,11 +1131,11 @@ class qformat_xml extends qformat_default {
         $expout .= "    </name>\n";
         $expout .= "    <questiontext {$this->format($question->questiontextformat)}>\n";
         $expout .= $this->writetext($question->questiontext, 3);
-        $expout .= $this->writefiles($question->questiontextfiles);
+        $expout .= $this->write_files($question->questiontextfiles);
         $expout .= "    </questiontext>\n";
         $expout .= "    <generalfeedback {$this->format($question->generalfeedbackformat)}>\n";
         $expout .= $this->writetext($question->generalfeedback, 3);
-        $expout .= $this->writefiles($question->generalfeedbackfiles);
+        $expout .= $this->write_files($question->generalfeedbackfiles);
         $expout .= "    </generalfeedback>\n";
         if ($question->qtype != 'multianswer') {
             $expout .= "    <defaultgrade>{$question->defaultmark}</defaultgrade>\n";
@@ -1191,7 +1212,7 @@ class qformat_xml extends qformat_default {
                     $expout .= "    <instructions " .
                             $this->format($question->options->instructionsformat) . ">\n";
                     $expout .= $this->writetext($question->options->instructions, 3);
-                    $expout .= $this->writefiles($files);
+                    $expout .= $this->write_files($files);
                     $expout .= "    </instructions>\n";
                 }
                 break;
@@ -1207,7 +1228,7 @@ class qformat_xml extends qformat_default {
                     $expout .= "    <subquestion " .
                             $this->format($subquestion->questiontextformat) . ">\n";
                     $expout .= $this->writetext($subquestion->questiontext, 3);
-                    $expout .= $this->writefiles($files);
+                    $expout .= $this->write_files($files);
                     $expout .= "      <answer>\n";
                     $expout .= $this->writetext($subquestion->answertext, 4);
                     $expout .= "      </answer>\n";
@@ -1235,7 +1256,7 @@ class qformat_xml extends qformat_default {
                 $expout .= "    <graderinfo " .
                         $this->format($question->options->graderinfoformat) . ">\n";
                 $expout .= $this->writetext($question->options->graderinfo, 3);
-                $expout .= $this->writefiles($fs->get_area_files($contextid, 'qtype_essay',
+                $expout .= $this->write_files($fs->get_area_files($contextid, 'qtype_essay',
                         'graderinfo', $question->id));
                 $expout .= "    </graderinfo>\n";
                 break;
@@ -1255,21 +1276,21 @@ class qformat_xml extends qformat_default {
                         'correctfeedback', $question->id);
                 $expout .= "    <correctfeedback>\n";
                 $expout .= $this->writetext($question->options->correctfeedback, 3);
-                $expout .= $this->writefiles($files);
+                $expout .= $this->write_files($files);
                 $expout .= "    </correctfeedback>\n";
 
                 $files = $fs->get_area_files($contextid, $component,
                         'partiallycorrectfeedback', $question->id);
                 $expout .= "    <partiallycorrectfeedback>\n";
                 $expout .= $this->writetext($question->options->partiallycorrectfeedback, 3);
-                $expout .= $this->writefiles($files);
+                $expout .= $this->write_files($files);
                 $expout .= "    </partiallycorrectfeedback>\n";
 
                 $files = $fs->get_area_files($contextid, $component,
                         'incorrectfeedback', $question->id);
                 $expout .= "    <incorrectfeedback>\n";
                 $expout .= $this->writetext($question->options->incorrectfeedback, 3);
-                $expout .= $this->writefiles($files);
+                $expout .= $this->write_files($files);
                 $expout .= "    </incorrectfeedback>\n";
 
                 foreach ($question->options->answers as $answer) {
@@ -1287,7 +1308,7 @@ class qformat_xml extends qformat_default {
                     $files = $fs->get_area_files($contextid, $component,
                             'instruction', $question->id);
                     $expout .= $this->writetext($answer->feedback);
-                    $expout .= $this->writefiles($answer->feedbackfiles);
+                    $expout .= $this->write_files($answer->feedbackfiles);
                     $expout .= "    </feedback>\n";
                     $expout .= "</answer>\n";
                 }
@@ -1312,7 +1333,7 @@ class qformat_xml extends qformat_default {
                     $expout .= "    <instructions " .
                             $this->format($question->options->instructionsformat) . ">\n";
                     $expout .= $this->writetext($question->options->instructions, 3);
-                    $expout .= $this->writefiles($files);
+                    $expout .= $this->write_files($files);
                     $expout .= "    </instructions>\n";
                 }
 
@@ -1418,10 +1439,10 @@ class qformat_xml extends qformat_default {
         $output = '';
         $output .= "    <answer fraction=\"$percent\" {$this->format($answer->answerformat)}>\n";
         $output .= $this->writetext($answer->answer, 3);
-        $output .= $this->writefiles($answer->answerfiles);
+        $output .= $this->write_files($answer->answerfiles);
         $output .= "      <feedback {$this->format($answer->feedbackformat)}>\n";
         $output .= $this->writetext($answer->feedback, 4);
-        $output .= $this->writefiles($answer->feedbackfiles);
+        $output .= $this->write_files($answer->feedbackfiles);
         $output .= "      </feedback>\n";
         $output .= $extra;
         $output .= "    </answer>\n";
@@ -1446,10 +1467,10 @@ class qformat_xml extends qformat_default {
     }
 
     /**
-     * @param unknown_type $format a FORMAT_... constant.
+     * @param int $format a FORMAT_... constant.
      * @return string the attribute to add to an XML tag.
      */
-    protected function format($format) {
+    public function format($format) {
         return 'format="' . $this->get_format($format) . '"';
     }
 
@@ -1471,7 +1492,7 @@ class qformat_xml extends qformat_default {
         if (!empty($hint->options)) {
             $output .= '      <options>' . $this->xml_escape($hint->options) . "</options>\n";
         }
-        $output .= $this->writefiles($files);
+        $output .= $this->write_files($files);
         $output .= "    </hint>\n";
         return $output;
     }
@@ -1494,7 +1515,7 @@ class qformat_xml extends qformat_default {
 
             $output .= "    <{$field} {$this->format($questionoptions->$formatfield)}>\n";
             $output .= '      ' . $this->writetext($questionoptions->$field);
-            $output .= $this->writefiles($files);
+            $output .= $this->write_files($files);
             $output .= "    </{$field}>\n";
         }
 
index d3090da..5e76deb 100644 (file)
@@ -702,6 +702,16 @@ class question_type {
         $question->createdby = $questiondata->createdby;
         $question->modifiedby = $questiondata->modifiedby;
 
+        //Fill extra question fields values
+        $extraquestionfields = $this->extra_question_fields();
+        if (is_array($extraquestionfields)) {
+            //omit table name
+            array_shift($extraquestionfields);
+            foreach($extraquestionfields as $field) {
+                $question->$field = $questiondata->options->$field;
+            }
+        }
+
         $this->initialise_question_hints($question, $questiondata);
     }
 
index 49fabf2..2c01634 100644 (file)
@@ -59,10 +59,6 @@ class qtype_shortanswer_edit_form extends question_edit_form {
         $question = $this->data_preprocessing_answers($question);
         $question = $this->data_preprocessing_hints($question);
 
-        if (!empty($question->options)) {
-            $question->usecase = $question->options->usecase;
-        }
-
         return $question;
     }
 
index b68413b..b43e3ee 100644 (file)
@@ -124,7 +124,6 @@ class qtype_shortanswer extends question_type {
 
     protected function initialise_question_instance(question_definition $question, $questiondata) {
         parent::initialise_question_instance($question, $questiondata);
-        $question->usecase = $questiondata->options->usecase;
         $this->initialise_question_answers($question, $questiondata);
     }
 
index 936035c..9e6f930 100644 (file)
@@ -49,11 +49,11 @@ class qtype_truefalse_edit_form extends question_edit_form {
                 1 => get_string('true', 'qtype_truefalse')));
 
         $mform->addElement('editor', 'feedbacktrue',
-                get_string('feedbacktrue', 'qtype_truefalse'), null, $this->editoroptions);
+                get_string('feedbacktrue', 'qtype_truefalse'), array('rows' => 10), $this->editoroptions);
         $mform->setType('feedbacktrue', PARAM_RAW);
 
         $mform->addElement('editor', 'feedbackfalse',
-                get_string('feedbackfalse', 'qtype_truefalse'), null, $this->editoroptions);
+                get_string('feedbackfalse', 'qtype_truefalse'), array('rows' => 10), $this->editoroptions);
         $mform->setType('feedbackfalse', PARAM_RAW);
 
         $mform->addElement('header', 'multitriesheader',
index f127277..5a70587 100644 (file)
@@ -27,7 +27,7 @@ require_once('../../config.php');
 require_once($CFG->libdir.'/adminlib.php');
 require_once($CFG->dirroot.'/backup/lib.php');
 
-admin_externalpage_setup('reportbackups');
+admin_externalpage_setup('reportbackups', '', null, '', array('pagelayout'=>'report'));
 
 $table = new html_table;
 $table->head = array(
index 20fd5c6..8372727 100644 (file)
@@ -32,7 +32,7 @@ $perpage = optional_param('perpage', 30, PARAM_INT);    // how many per page
 $sort    = optional_param('sort', 'timemodified', PARAM_ALPHA);
 $dir     = optional_param('dir', 'DESC', PARAM_ALPHA);
 
-admin_externalpage_setup('reportconfiglog');
+admin_externalpage_setup('reportconfiglog', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 echo $OUTPUT->heading(get_string('configlog', 'report_configlog'));
index 04faf7c..9fe5c13 100644 (file)
@@ -39,7 +39,7 @@ if (empty($CFG->enablestats)) {
     }
 }
 
-admin_externalpage_setup('reportcourseoverview');
+admin_externalpage_setup('reportcourseoverview', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 $course = get_site();
index d7cd8f6..08434eb 100644 (file)
@@ -186,7 +186,7 @@ if (!empty($chooselog)) {
 
 } else {
     if ($hostid != $CFG->mnet_localhost_id || $course->id == SITEID) {
-        admin_externalpage_setup('reportlog');
+        admin_externalpage_setup('reportlog', '', null, '', array('pagelayout'=>'report'));
         echo $OUTPUT->header();
     } else {
         $PAGE->set_title($course->shortname .': '. $strlogs);
index ba9fc30..57ae10f 100644 (file)
@@ -72,7 +72,7 @@ if ($inpopup) {
 
 
 if ($course->id == SITEID) {
-    admin_externalpage_setup('reportloglive');
+    admin_externalpage_setup('reportloglive', '', null, '', array('pagelayout'=>'report'));
     echo $OUTPUT->header();
 
 } else {
index 0273c83..9f8a3f7 100644 (file)
@@ -31,7 +31,7 @@ require_once($CFG->libdir.'/questionlib.php');
 $requestedqtype = optional_param('qtype', '', PARAM_PLUGIN);
 
 // Print the header & check permissions.
-admin_externalpage_setup('reportquestioninstances');
+admin_externalpage_setup('reportquestioninstances', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 // Log.
index 863db1c..339ddaa 100644 (file)
@@ -45,7 +45,7 @@ raise_memory_limit(MEMORY_EXTRA);
 @set_time_limit(0);
 
 // Print the header.
-admin_externalpage_setup('reportsecurity');
+admin_externalpage_setup('reportsecurity', '', null, '', array('pagelayout'=>'report'));
 echo $OUTPUT->header();
 
 echo $OUTPUT->heading(get_string('pluginname', 'report_security'));
index 9675fdc..8238cde 100644 (file)
@@ -71,7 +71,7 @@ add_to_log($course->id, "course", "report stats", "report/stats/index.php?course
 stats_check_uptodate($course->id);
 
 if ($course->id == SITEID) {
-    admin_externalpage_setup('reportstats');
+    admin_externalpage_setup('reportstats', '', null, '', array('pagelayout'=>'report'));
     echo $OUTPUT->header();
 } else {
     $strreports = get_string("reports");
index 6f9abb0..644d13d 100644 (file)
@@ -9,11 +9,27 @@ API changes:
 * new support for report settings
 
 
-How to migrate old admin reports:
-# copy all files to new /report/yourplugin/ location
+How to migrate existing admin reports:
+# move all files to new /report/yourplugin/ location
 # if settings.php exists add $settings=null;
 # if settings.php does not exist create it and link the report, index.php is not linked automatically any more
 # update require('../../config.php'); - remove one ../
+# update all others includes and requires
 # update all links to report pages by removing /admin/ or /$CFG->admin/
-# add language pack with at least pluginname string
+# add language pack with at least 'pluginname' string
+# update CSS selectors
 
+How to migrate existing course reports (optional):
+# move all files to new /report/yourplugin/ location
+# update require('../../config.php'); - remove one ../
+# update all others includes and requires
+# update all links to report pages by removing /course/ part
+# update all language strings (use 'report_yourplugin' instead of 'coursereport_yourplugin') - use AMOS hints in commit message
+# update all capability names
+# grep the plugin codebase and look for any remaining 'coursereport' occurrences
+# add new navigation hooks in lib.php - ex: report_stats_extend_navigation_course(), report_stats_extend_navigation_user()
+# add new page types in lib.php
+# create db/install.php migration script - delete old settings and capabilities (see converted plugins for examples)
+# update CSS selectors
+
+See http://docs.moodle.org/dev/General_report_plugins for more details and explanation.
index fbdcd7d..67b1a13 100644 (file)
@@ -1619,7 +1619,7 @@ abstract class repository {
                         $pass = true;
                     } else {
                         foreach ($extensions as $ext) {
-                            if (preg_match('#'.$ext.'$#', $value['title'])) {
+                            if (preg_match('#'.$ext.'$#i', $value['title'])) {
                                 $pass = true;
                             }
                         }
index cb0e10a..f022dfc 100644 (file)
@@ -414,8 +414,8 @@ function coursetag_get_tagged_courses($tagid) {
  * Course tagging function used only during the deletion of a
  * course (called by lib/moodlelib.php) to clean up associated tags
  *
- * @param $courseid
- * @param $showfeedback
+ * @param int $courseid
+ * @param bool $showfeedback
  */
 function coursetag_delete_course_tags($courseid, $showfeedback=false) {
 
@@ -428,12 +428,15 @@ function coursetag_delete_course_tags($courseid, $showfeedback=false) {
             // delete tag if there are no other tag_instance entries now
             if (!($DB->record_exists('tag_instance', array('tagid'=>$tag->tagid)))) {
                 $DB->delete_records('tag', array('id'=>$tag->tagid));
+                // Delete files
+                $fs = get_file_storage();
+                $fs->delete_area_files(get_system_context()->id, 'tag', 'description', $tag->tagid);
             }
         }
     }
 
     if ($showfeedback) {
-        echo $OUTPUT->notification(get_string('deletedcoursetags', 'tag'));
+        echo $OUTPUT->notification(get_string('deletedcoursetags', 'tag'), 'notifysuccess');
     }
 }
 
index c238b19..512f2c0 100644 (file)
@@ -12,6 +12,7 @@
 .block .content .list .c1 {margin-left:5px;display:inline;}
 .block .footer {margin-bottom: 4px;}
 .block .blockannotation {font-size:0.75em;margin: -1em 0 1em;}
+.block_navigation .block_tree li {overflow:hidden;}
 
 /** block_list blocks need column stuffs **/
 .block.list_block .unlist > li > .column {display:inline-block;}
@@ -29,6 +30,6 @@
 .block.hidden .block-hider-show {display:inline;}
 
 /** Overide for RTL layout **/
-.dir-rtl .block .header, 
+.dir-rtl .block .header,
 .dir-rtl .block h2.header {text-align:right;}
 .dir-rtl .block .header .commands { text-align: right;}
\ No newline at end of file
index 999a630..4ff7ea4 100644 (file)
@@ -77,7 +77,7 @@ a.autolink.glossary:hover {cursor: help;}
 img.resize {height: 1em;width: 1em;}
 .block img.resize,
 .breadcrumb img.resize {height: 0.9em;width: 0.8em;}
-img.icon {height:16px;vertical-align:middle;width:16px;}
+img.icon {height:16px;vertical-align:middle;width:16px;padding-right:4px;}
 img.iconsmall {height:11px;margin-right:1px;vertical-align:middle;width:11px;}
 img.iconhelp {height:17px;margin-right:4px;vertical-align:middle;width:17px;}
 img.icontoggle {height:17px;vertical-align:middle;width:50px;}
@@ -876,4 +876,4 @@ ol li,
 
 #page-admin-setting-enrolsettingsflatfile.dir-rtl .informationbox {direction: ltr;text-align: left;}
 
-#page-admin-grade-edit-scale-edit.dir-rtl .error input#id_name {margin-right: 170px;}
\ No newline at end of file
+#page-admin-grade-edit-scale-edit.dir-rtl .error input#id_name {margin-right: 170px;}
index 0745490..8276b71 100644 (file)
@@ -39,6 +39,7 @@
 .userselector select {width: 100%;}
 .userselector div {margin-top: 0.2em;}
 .userselector div label {margin-right: 0.3em;}
+#userselector_options {padding:0.3em 0;}
 #userselector_options .collapsibleregioncaption {font-weight: bold;}
 #userselector_options p {margin:0.2em 0;text-align:left;}
 .dir-rtl #userselector_options p {text-align:right;}
index 89b0014..302f0ef 100644 (file)
@@ -52,10 +52,6 @@ p {
     margin: 0 0 1em;
 }
 
-ul, ol {
-    margin: 0 1.5em 1.5em 1.5em;
-}
-
 ul {
     list-style-type: circle;
 }
index a1a2e66..63acfaf 100644 (file)
@@ -119,6 +119,9 @@ if ($rev > -1) {
     $pathinfo = pathinfo($imagefile);
     $cacheimage = "$candidatelocation/$image.".$pathinfo['extension'];
     if (!file_exists($cacheimage)) {
+        // note: cache reset might have purged our cache dir structure,
+        //       make sure we do not use stale file stat cache in the next check_dir_exists()
+        clearstatcache();
         check_dir_exists(dirname($cacheimage));
         copy($imagefile, $cacheimage);
     }
index afe5d01..3ebba8b 100644 (file)
@@ -81,6 +81,9 @@ require_once('Minify.php');
 $theme = theme_config::load($themename);
 
 if ($rev > -1) {
+    // note: cache reset might have purged our cache dir structure,
+    //       make sure we do not use stale file stat cache in the next check_dir_exists()
+    clearstatcache();
     check_dir_exists(dirname($candidate));
     $fp = fopen($candidate, 'w');
     fwrite($fp, minify($theme->javascript_files($type)));
index 5cdaa76..88f543b 100644 (file)
@@ -26,7 +26,7 @@
 $string['pluginname'] = 'Sky High';
 $string['region-side-post'] = 'Right';
 $string['region-side-pre'] = 'Left';
-$string['choosereadme'] = '<div class="clearfix"><div class="theme_screenshot"><h2>Sky High</h2><img src="sky_high/pix/screenshot.png" /><h3>Theme Discussion Forum:</h3><p><a href="http://moodle.org/mod/forum/view.php?id=46">http://moodle.org/mod/forum/view.php?id=46</a></p><h3>Theme Credits</h3><p><a href="http://docs.moodle.org/en/Theme_credits">http://docs.moodle.org/en/Theme_credits</a></p><h3>Theme Documentation:</h3><p><a href="http://docs.moodle.org/en/Themes">http://docs.moodle.org/en/Themes</a></p><h3>Report a bug:</h3><p><a href="http://tracker.moodle.org">http://tracker.moodle.org</a></p></div><div class="theme_description"><h2>About</h2><p>Serenity is a fluid-width, three-column theme for Moodle 2.0.<h2>Tweaks</h2><p>This theme is built upon both Base and Canvas, two parent themes included in the Moodle core. If you want to modify this theme, we recommend that you first duplicate it then rename it before making your changes. This will prevent your customized theme from being overwritten by future Moodle upgrades, and you\'ll still have the original files if you make a mess. More information on modifying themes can be found in the <a href="http://docs.moodle.org/en/Theme">MoodleDocs</a>.</p><h2>Credits</h2>  <p>This theme was designed by Julian Ridden (julian@moodle.com.au). It was coded and is maintained by John Stabinger of NewSchool Learning (contact@newschoollearning.com). </p><h2>License</h2><p>This, and all other themes included in the Moodle core, are licensed under the <a href="http://www.gnu.org/licenses/gpl.html">GNU General Public License</a>.</div></div>';
+$string['choosereadme'] = '<div class="clearfix"><div class="theme_screenshot"><h2>Sky High</h2><img src="sky_high/pix/screenshot.png" /><h3>Theme Discussion Forum:</h3><p><a href="http://moodle.org/mod/forum/view.php?id=46">http://moodle.org/mod/forum/view.php?id=46</a></p><h3>Theme Credits</h3><p><a href="http://docs.moodle.org/en/Theme_credits">http://docs.moodle.org/en/Theme_credits</a></p><h3>Theme Documentation:</h3><p><a href="http://docs.moodle.org/en/Themes">http://docs.moodle.org/en/Themes</a></p><h3>Report a bug:</h3><p><a href="http://tracker.moodle.org">http://tracker.moodle.org</a></p></div><div class="theme_description"><h2>About</h2><p>Sky High is a fluid-width, three-column (blog style) theme for Moodle 2.0.<h2>Tweaks</h2><p>This theme is built upon both Base and Canvas, two parent themes included in the Moodle core. If you want to modify this theme, we recommend that you first duplicate it then rename it before making your changes. This will prevent your customized theme from being overwritten by future Moodle upgrades, and you\'ll still have the original files if you make a mess. More information on modifying themes can be found in the <a href="http://docs.moodle.org/en/Theme">MoodleDocs</a>.</p><h2>Credits</h2>     <p>This theme was designed by Julian Ridden (julian@moodle.com.au). It was coded and is maintained by John Stabinger of NewSchool Learning (contact@newschoollearning.com). </p><h2>License</h2><p>This, and all other themes included in the Moodle core, are licensed under the <a href="http://www.gnu.org/licenses/gpl.html">GNU General Public License</a>.</div></div>';
 $string['configtitle'] = 'Sky High settings';
 $string['customcss'] = 'Custom CSS';
 $string['customcssdesc'] = 'Any CSS you enter here will be added to every page allowing your to easily customise this theme.';
index 909bfb5..67385e3 100644 (file)
@@ -117,6 +117,9 @@ send_cached_css($candidatesheet, $rev);
 
 function store_css(theme_config $theme, $csspath, $cssfiles) {
     $css = $theme->post_process(minify($cssfiles));
+    // note: cache reset might have purged our cache dir structure,
+    //       make sure we do not use stale file stat cache in the next check_dir_exists()
+    clearstatcache();
     check_dir_exists(dirname($csspath));
     $fp = fopen($csspath, 'w');
     fwrite($fp, $css);
index 5685942..7c2267f 100644 (file)
@@ -104,13 +104,42 @@ function user_get_users_by_id($userids) {
  * @param stdClass $user user record from mdl_user
  * @param stdClass $context context object
  * @param stdClass $course moodle course
+ * @param array $userfields required fields
  * @return array
  */
-function user_get_user_details($user, $course = null) {
+function user_get_user_details($user, $course = null, array $userfields = array()) {
     global $USER, $DB, $CFG;
     require_once($CFG->dirroot . "/user/profile/lib.php"); //custom field library
     require_once($CFG->dirroot . "/lib/filelib.php");      // file handling on description and friends
 
+    $defaultfields = array( 'id', 'username', 'fullname', 'firstname', 'lastname', 'email',
+        'address', 'phone1', 'phone2', 'icq', 'skype', 'yahoo', 'aim', 'msn', 'department',
+        'institution', 'interests', 'firstaccess', 'lastaccess', 'auth', 'confirmed',
+        'idnumber', 'lang', 'theme', 'timezone', 'mailformat', 'description', 'descriptionformat',
+        'city', 'url', 'country', 'profileimageurlsmall', 'profileimageurl', 'customfields',
+        'groups', 'roles', 'preferences', 'enrolledcourses'
+    );
+
+    if (empty($userfields)) {
+        $userfields = $defaultfields;
+    }
+
+    foreach ($userfields as $thefield) {
+        if (!in_array($thefield, $defaultfields)) {
+            throw new moodle_exception('invaliduserfield', 'error', '', $thefield);
+        }
+    }
+
+
+    // Make sure id and fullname are included
+    if (!in_array('id', $userfields)) {
+        $userfields[] = 'id';
+    }
+
+    if (!in_array('fullname', $userfields)) {
+        $userfields[] = 'fullname';
+    }
+
     if (!empty($course)) {
         $context = get_context_instance(CONTEXT_COURSE, $course->id);
         $usercontext = get_context_instance(CONTEXT_USER, $user->id);
@@ -150,42 +179,52 @@ function user_get_user_details($user, $course = null) {
     $userdetails = array();
     $userdetails['id'] = $user->id;
 
-    if ($isadmin or $currentuser) {
+    if (($isadmin or $currentuser) and in_array('username', $userfields)) {
         $userdetails['username'] = $user->username;
     }
     if ($isadmin or $canviewfullnames) {
-        $userdetails['firstname'] = $user->firstname;
-        $userdetails['lastname'] = $user->lastname;
+        if (in_array('firstname', $userfields)) {
+            $userdetails['firstname'] = $user->firstname;
+        }
+        if (in_array('lastname', $userfields)) {
+            $userdetails['lastname'] = $user->lastname;
+        }
     }
     $userdetails['fullname'] = fullname($user);
 
-    $fields = $DB->get_recordset_sql("SELECT f.*
-                                        FROM {user_info_field} f
-                                        JOIN {user_info_category} c
-                                             ON f.categoryid=c.id
-                                    ORDER BY c.sortorder ASC, f.sortorder ASC");
-    $userdetails['customfields'] = array();
-    foreach ($fields as $field) {
-        require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
-        $newfield = 'profile_field_'.$field->datatype;
-        $formfield = new $newfield($field->id, $user->id);
-        if ($formfield->is_visible() and !$formfield->is_empty()) {
-            $userdetails['customfields'][] =
-                array('name' => $formfield->field->name, 'value' => $formfield->data,
-                    'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
+    if (in_array('customfields', $userfields)) {
+        $fields = $DB->get_recordset_sql("SELECT f.*
+                                            FROM {user_info_field} f
+                                            JOIN {user_info_category} c
+                                                 ON f.categoryid=c.id
+                                        ORDER BY c.sortorder ASC, f.sortorder ASC");
+        $userdetails['customfields'] = array();
+        foreach ($fields as $field) {
+            require_once($CFG->dirroot.'/user/profile/field/'.$field->datatype.'/field.class.php');
+            $newfield = 'profile_field_'.$field->datatype;
+            $formfield = new $newfield($field->id, $user->id);
+            if ($formfield->is_visible() and !$formfield->is_empty()) {
+                $userdetails['customfields'][] =
+                    array('name' => $formfield->field->name, 'value' => $formfield->data,
+                        'type' => $field->datatype, 'shortname' => $formfield->field->shortname);
+            }
+        }
+        $fields->close();
+        // unset customfields if it's empty
+        if (empty($userdetails['customfields'])) {
+            unset($userdetails['customfields']);
         }
-    }
-    $fields->close();
-    // unset customfields if it's empty
-    if (empty($userdetails['customfields'])) {
-        unset($userdetails['customfields']);
     }
 
     // profile image
-    $profileimageurl = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f1');
-    $userdetails['profileimageurl'] = $profileimageurl->out(false);
-    $profileimageurlsmall = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f2');
-    $userdetails['profileimageurlsmall'] = $profileimageurlsmall->out(false);
+    if (in_array('profileimageurl', $userfields)) {
+        $profileimageurl = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f1');
+        $userdetails['profileimageurl'] = $profileimageurl->out(false);
+    }
+    if (in_array('profileimageurlsmall', $userfields)) {
+        $profileimageurlsmall = moodle_url::make_pluginfile_url($usercontext->id, 'user', 'icon', NULL, '/', 'f2');
+        $userdetails['profileimageurlsmall'] = $profileimageurlsmall->out(false);
+    }
 
     //hidden user field
     if ($canviewhiddenuserfields) {
@@ -193,13 +232,13 @@ function user_get_user_details($user, $course = null) {
         // address, phone1 and phone2 not appears in hidden fields list
         // but require viewhiddenfields capability
         // according to user/profile.php
-        if ($user->address) {
+        if ($user->address && in_array('address', $userfields)) {
             $userdetails['address'] = $user->address;
         }
-        if ($user->phone1) {
+        if ($user->phone1 && in_array('phone1', $userfields)) {
             $userdetails['phone1'] = $user->phone1;
         }
-        if ($user->phone2) {
+        if ($user->phone2 && in_array('phone2', $userfields)) {
             $userdetails['phone2'] = $user->phone2;
         }
     } else {
@@ -208,21 +247,26 @@ function user_get_user_details($user, $course = null) {
 
     if (isset($user->description) && (!isset($hiddenfields['description']) or $isadmin)) {
         if (!$cannotviewdescription) {
-            $user->description = file_rewrite_pluginfile_urls($user->description, 'pluginfile.php', $usercontext->id, 'user', 'profile', null);
-            $userdetails['description'] = $user->description;
-            $userdetails['descriptionformat'] = $user->descriptionformat;
+
+            if (in_array('description', $userfields)) {
+                $user->description = file_rewrite_pluginfile_urls($user->description, 'pluginfile.php', $usercontext->id, 'user', 'profile', null);
+                $userdetails['description'] = $user->description;
+            }
+            if (in_array('descriptionformat', $userfields)) {
+                $userdetails['descriptionformat'] = $user->descriptionformat;
+            }
         }
     }
 
-    if ((!isset($hiddenfields['country']) or $isadmin) && $user->country) {
+    if (in_array('country', $userfields) && (!isset($hiddenfields['country']) or $isadmin) && $user->country) {
         $userdetails['country'] = $user->country;
     }
 
-    if ((!isset($hiddenfields['city']) or $isadmin) && $user->city) {
+    if (in_array('city', $userfields) && (!isset($hiddenfields['city']) or $isadmin) && $user->city) {
         $userdetails['city'] = $user->city;
     }
 
-    if ($user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
+    if (in_array('url', $userfields) && $user->url && (!isset($hiddenfields['webpage']) or $isadmin)) {
         $url = $user->url;
         if (strpos($user->url, '://') === false) {
             $url = 'http://'. $url;
@@ -231,31 +275,31 @@ function user_get_user_details($user, $course = null) {
         $userdetails['url'] = $user->url;
     }
 
-    if ($user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
+    if (in_array('icq', $userfields) && $user->icq && (!isset($hiddenfields['icqnumber']) or $isadmin)) {
         $userdetails['icq'] = $user->icq;
     }
 
-    if ($user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
+    if (in_array('skype', $userfields) && $user->skype && (!isset($hiddenfields['skypeid']) or $isadmin)) {
         $userdetails['skype'] = $user->skype;
     }
-    if ($user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
+    if (in_array('yahoo', $userfields) && $user->yahoo && (!isset($hiddenfields['yahooid']) or $isadmin)) {
         $userdetails['yahoo'] = $user->yahoo;
     }
-    if ($user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
+    if (in_array('aim', $userfields) && $user->aim && (!isset($hiddenfields['aimid']) or $isadmin)) {
         $userdetails['aim'] = $user->aim;
     }
-    if ($user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
+    if (in_array('msn', $userfields) && $user->msn && (!isset($hiddenfields['msnid']) or $isadmin)) {
         $userdetails['msn'] = $user->msn;
     }
 
-    if (!isset($hiddenfields['firstaccess']) or $isadmin) {
+    if (in_array('firstaccess', $userfields) && (!isset($hiddenfields['firstaccess']) or $isadmin)) {
         if ($user->firstaccess) {
             $userdetails['firstaccess'] = $user->firstaccess;
         } else {
             $userdetails['firstaccess'] = 0;
         }
     }
-    if (!isset($hiddenfields['lastaccess']) or $isadmin) {
+    if (in_array('lastaccess', $userfields) && (!isset($hiddenfields['lastaccess']) or $isadmin)) {
         if ($user->lastaccess) {
             $userdetails['lastaccess'] = $user->lastaccess;
         } else {
@@ -263,14 +307,14 @@ function user_get_user_details($user, $course = null) {
         }
     }
 
-    if ($currentuser
+    if (in_array('email', $userfields) && ($currentuser
       or $canviewuseremail  // this is a capability in course context, it will be false in usercontext
       or $user->maildisplay == 1
-      or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER))) {
+      or ($user->maildisplay == 2 and enrol_sharing_course($user, $USER)))) {
         $userdetails['email'] = $user->email;;
     }
 
-    if (!empty($CFG->usetags)) {
+    if (in_array('interests', $userfields) && !empty($CFG->usetags)) {
         require_once($CFG->dirroot . '/tag/lib.php');
         if ($interests = tag_get_tags_csv('user', $user->id, TAG_RETURN_TEXT) ) {
             $userdetails['interests'] = $interests;
@@ -279,15 +323,15 @@ function user_get_user_details($user, $course = null) {
 
     //Departement/Institution are not displayed on any profile, however you can get them from editing profile.
     if ($isadmin or $currentuser) {
-        if ($user->institution) {
+        if (in_array('institution', $userfields) && $user->institution) {
             $userdetails['institution'] = $user->institution;
         }
-        if (isset($user->department)) { //isset because it's ok to have department 0
+        if (in_array('department', $userfields) && isset($user->department)) { //isset because it's ok to have department 0
             $userdetails['department'] = $user->department;
         }
     }
 
-    if (!empty($course)) {
+    if (in_array('roles', $userfields) && !empty($course)) {
         // not a big secret
         $roles = get_user_roles($context, $user->id, false);
         $userdetails['roles'] = array();
@@ -302,7 +346,7 @@ function user_get_user_details($user, $course = null) {
     }
 
     // If groups are in use and enforced throughout the course, then make sure we can meet in at least one course level group
-    if (!empty($course) && $canaccessallgroups) {
+    if (in_array('groups', $userfields) && !empty($course) && $canaccessallgroups) {
         $usergroups = groups_get_all_groups($course->id, $user->id, $course->defaultgroupingid, 'g.id, g.name,g.description');
         $userdetails['groups'] = array();
         foreach ($usergroups as $group) {
@@ -311,7 +355,7 @@ function user_get_user_details($user, $course = null) {
         }
     }
     //list of courses where the user is enrolled
-    if (!isset($hiddenfields['mycourses'])) {
+    if (in_array('enrolledcourses', $userfields) && !isset($hiddenfields['mycourses'])) {
         $enrolledcourses = array();
         if ($mycourses = enrol_get_users_courses($user->id, true)) {
             foreach ($mycourses as $mycourse) {
@@ -329,7 +373,7 @@ function user_get_user_details($user, $course = null) {
     }
 
     //user preferences
-    if ($currentuser) {
+    if (in_array('preferences', $userfields) && $currentuser) {
         $preferences = array();
         $userpreferences = get_user_preferences();
          foreach($userpreferences as $prefname => $prefvalue) {
@@ -337,6 +381,7 @@ function user_get_user_details($user, $course = null) {
          }
          $userdetails['preferences'] = $preferences;
     }
+
     return $userdetails;
 }
 
index e9e7913..e077cf1 100644 (file)
 <?php
     if (count($SESSION->emailto[$id])) {
         foreach ($SESSION->emailto[$id] as $user) {
-            echo '<tr><td>'.fullname($user,true).'</td><td>'.$user->email.'</td><td>';
+            echo '<tr><td>'.fullname($user,true).'</td>';
+            // Check to see if we should be showing the email address.
+            if ($user->maildisplay == 0) { // 0 = don't display my email to anyone.
+                echo '<td>' . get_string('emaildisplayhidden') . '</td><td>';
+            } else {
+                echo '<td>'.$user->email.'</td><td>';
+            }
             if (empty($user->email)) {
                 $error = get_string('emailempty');
             }
index a210d30..d54d26d 100644 (file)
@@ -95,7 +95,7 @@ if ($data = data_submitted()) {
     foreach ($data as $k => $v) {
         if (preg_match('/^(user|teacher)(\d+)$/',$k,$m)) {
             if (!array_key_exists($m[2],$SESSION->emailto[$id])) {
-                if ($user = $DB->get_record_select('user', "id = ?", array($m[2]), 'id,firstname,lastname,idnumber,email,mailformat,lastaccess, lang')) {
+                if ($user = $DB->get_record_select('user', "id = ?", array($m[2]), 'id,firstname,lastname,idnumber,email,mailformat,lastaccess, lang, maildisplay')) {
                     $SESSION->emailto[$id][$m[2]] = $user;
                     $count++;
                 }
index bf6f85f..86a3f4f 100644 (file)
@@ -645,7 +645,7 @@ abstract class webservice_server implements webservice_server_interface {
                 throw new webservice_access_exception(get_string('wrongusernamepassword', 'webservice'));
             }
 
-            $user = $DB->get_record('user', array('username'=>$this->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0), '*', MUST_EXIST);
+            $user = $DB->get_record('user', array('username'=>$this->username, 'mnethostid'=>$CFG->mnet_localhost_id), '*', MUST_EXIST);
 
         } else if ($this->authmethod == WEBSERVICE_AUTHMETHOD_PERMANENT_TOKEN){
             $user = $this->authenticate_by_token(EXTERNAL_TOKEN_PERMANENT);
@@ -653,6 +653,50 @@ abstract class webservice_server implements webservice_server_interface {
             $user = $this->authenticate_by_token(EXTERNAL_TOKEN_EMBEDDED);
         }
 
+        //Non admin can not authenticate if maintenance mode
+        $hassiteconfig = has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM), $user);
+        if (!empty($CFG->maintenance_enabled) and !$hassiteconfig) {
+            throw new webservice_access_exception(get_string('sitemaintenance', 'admin'));
+        }
+
+        //only confirmed user should be able to call web service
+        if (!empty($user->deleted)) {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessuserdeleted', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessuserdeleted', 'webservice', $user->username));
+        }
+
+        //only confirmed user should be able to call web service
+        if (empty($user->confirmed)) {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessuserunconfirmed', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessuserunconfirmed', 'webservice', $user->username));
+        }
+
+        //check the user is suspended
+        if (!empty($user->suspended)) {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessusersuspended', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessusersuspended', 'webservice', $user->username));
+        }
+
+        //retrieve the authentication plugin if no previously done
+        if (empty($auth)) {
+          $auth  = get_auth_plugin($user->auth);
+        }
+
+        // check if credentials have expired
+        if (!empty($auth->config->expiration) and $auth->config->expiration == 1) {
+            $days2expire = $auth->password_expire($user->username);
+            if (intval($days2expire) < 0 ) {
+                add_to_log(SITEID, '', '', '', get_string('wsaccessuserexpired', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+                throw new webservice_access_exception(get_string('wsaccessuserexpired', 'webservice', $user->username));
+            }
+        }
+
+        //check if the auth method is nologin (in this case refuse connection)
+        if ($user->auth=='nologin') {
+            add_to_log(SITEID, '', '', '', get_string('wsaccessusernologin', 'webservice', $user->username) . " - ".getremoteaddr(), 0, $user->id);
+            throw new webservice_access_exception(get_string('wsaccessusernologin', 'webservice', $user->username));
+        }
+
         // now fake user login, the session is completely empty too
         enrol_check_plugins($user);
         session_set_user($user);
@@ -694,7 +738,7 @@ abstract class webservice_server implements webservice_server_interface {
         $this->restricted_context = get_context_instance_by_id($token->contextid);
         $this->restricted_serviceid = $token->externalserviceid;
 
-        $user = $DB->get_record('user', array('id'=>$token->userid, 'deleted'=>0), '*', MUST_EXIST);
+        $user = $DB->get_record('user', array('id'=>$token->userid), '*', MUST_EXIST);
 
         // log token access
         $DB->set_field('external_tokens', 'lastaccess', time(), array('id'=>$token->id));