Merge branch 'wip-mdl-28947' of git://github.com/rajeshtaneja/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 23 Nov 2011 10:37:34 +0000 (11:37 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Wed, 23 Nov 2011 10:37:34 +0000 (11:37 +0100)
65 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/util/dbops/restore_dbops.class.php
enrol/externallib.php
enrol/imsenterprise/lib.php
filter/mediaplugin/filter.php
filter/upgrade.txt [new file with mode: 0644]
grade/report/user/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/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/installlib.php
lib/moodlelib.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/scorm/datamodels/aicclib.php
mod/scorm/datamodels/scorm_12.js.php
question/format/xml/format.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
theme/base/style/blocks.css
theme/base/style/core.css
theme/base/style/user.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 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 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 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 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 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 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..26fc43c 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();
 }
 
 /**
@@ -6213,7 +6211,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]]";
             }
         }
@@ -9867,9 +9875,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 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 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 80ff9c1..b3f8858 100644 (file)
@@ -1446,10 +1446,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) . '"';
     }
 
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 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 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));