Merge branch 'w07_MDL-26351_20_instquotes' of git://github.com/skodak/moodle
authorEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 15 Feb 2011 11:32:39 +0000 (12:32 +0100)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Tue, 15 Feb 2011 11:32:39 +0000 (12:32 +0100)
107 files changed:
admin/cli/automated_backups.php
admin/registration/lib.php
admin/registration/register.php
admin/registration/renewregistration.php [new file with mode: 0644]
admin/settings/location.php
admin/settings/server.php
admin/uploaduser.php
auth/db/auth.php
auth/ldap/auth.php
auth/shibboleth/auth.php
backup/moodle2/backup_format_plugin.class.php [new file with mode: 0644]
backup/moodle2/backup_plan_builder.class.php
backup/moodle2/backup_plugin.class.php
backup/moodle2/backup_stepslib.php
backup/moodle2/backup_subplugin.class.php
backup/moodle2/restore_format_plugin.class.php [new file with mode: 0644]
backup/moodle2/restore_plan_builder.class.php
backup/moodle2/restore_plugin.class.php
backup/moodle2/restore_section_task.class.php
backup/moodle2/restore_stepslib.php
backup/moodle2/restore_subplugin.class.php
backup/util/dbops/backup_plan_dbops.class.php
backup/util/helper/backup_cron_helper.class.php
backup/util/plan/backup_structure_step.class.php
backup/util/structure/base_nested_element.class.php
backup/util/structure/simpletest/testbasenestedelement.php
backup/util/xml/parser/processors/simplified_parser_processor.class.php
backup/util/xml/parser/progressive_parser.class.php
backup/util/xml/parser/simpletest/fixtures/test4.xml
blocks/moodleblock.class.php
blocks/rss_client/edit_form.php
blog/lib.php
course/format/topics/format.php
course/format/weeks/format.php
course/lib.php
course/report/completion/index.php
course/view.php
enrol/category/locallib.php
enrol/renderer.php
filter/mediaplugin/filter.php
filter/mediaplugin/simpletest/testfiltermediaplugin.php [new file with mode: 0644]
install.php
install/lang/de/install.php
install/lang/pt_br/install.php
lang/en/admin.php
lang/en/hub.php
lib/accesslib.php
lib/adminlib.php
lib/db/install.xml
lib/db/upgrade.php
lib/dml/mysqli_native_moodle_database.php
lib/dml/sqlsrv_native_moodle_database.php
lib/filelib.php
lib/javascript-static.js
lib/messagelib.php
lib/minify/MOODLE_README.txt [deleted file]
lib/minify/config.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/sessionlib.php
lib/setuplib.php
lib/weblib.php
login/index.php
login/signup_form.php
message/index.php
message/lib.php
mod/data/export_form.php
mod/data/import.php
mod/data/lang/en/data.php
mod/data/lib.php
mod/forum/lib.php
mod/forum/user.php
mod/glossary/lang/en/glossary.php
mod/glossary/lib.php
mod/glossary/sql.php
mod/lesson/backup/moodle2/backup_lesson_stepslib.php
mod/lesson/backup/moodle2/restore_lesson_stepslib.php
mod/lesson/essay.php
mod/lesson/report.php
mod/quiz/backup/moodle2/backup_quiz_stepslib.php
mod/quiz/backup/moodle2/restore_quiz_stepslib.php
mod/quiz/locallib.php
mod/quiz/report/overview/report.php
mod/quiz/report/responses/report.php
mod/scorm/module.js
mod/scorm/version.php
mod/workshop/db/upgrade.php
mod/workshop/db/upgradelib.php
mod/workshop/form/accumulative/db/upgradelib.php
mod/workshop/form/comments/db/upgradelib.php
mod/workshop/form/numerrors/db/upgradelib.php
mod/workshop/form/rubric/db/upgradelib.php
mod/workshop/version.php
question/format/gift/simpletest/testgiftformat.php
question/qengine.js
question/type/numerical/questiontype.php
question/type/random/backup/moodle2/restore_qtype_random_plugin.class.php
question/type/random/questiontype.php
rating/rate_ajax.php
repository/upload/lib.php
theme/yui_combo.php
user/editlib.php
user/profile.php
user/view.php
version.php

index 8126c5a..dc80c7a 100644 (file)
@@ -92,7 +92,7 @@ mtrace("Server Time: ".date('r',$timenow)."\n\n");
 // Run automated backups if required.
 require_once($CFG->dirroot.'/backup/util/includes/backup_includes.php');
 require_once($CFG->dirroot.'/backup/util/helper/backup_cron_helper.class.php');
-backup_cron_automated_helper::run_automated_backup(backup_cron_automated_helper::RUN_IMMEDIATLY);
+backup_cron_automated_helper::run_automated_backup(backup_cron_automated_helper::RUN_IMMEDIATELY);
 
 mtrace("Automated cron backups completed correctly");
 
index 8fc5dd2..4179d79 100644 (file)
@@ -84,6 +84,33 @@ class registration_manager {
         }
     }
 
+    /**
+     * Return the site secret for a given hub
+     * site identifier is assigned to Mooch
+     * each hub has a unique and personal site secret.
+     * @param string $huburl
+     * @return string site secret
+     */
+    public function get_site_secret_for_hub($huburl) {
+        global $DB;
+
+        $existingregistration = $DB->get_record('registration_hubs',
+                    array('huburl' => $huburl));
+
+        if (!empty($existingregistration)) {
+            return $existingregistration->secret;
+        }
+
+        if ($huburl == HUB_MOODLEORGHUBURL) {
+            $siteidentifier =  get_site_identifier();
+        } else {
+            $siteidentifier = random_string(32) . $_SERVER['HTTP_HOST'];
+        }
+
+        return $siteidentifier;
+
+    }
+
     /**
      * When the site register on a hub, he must call this function
      * @param object $hub where the site is registered on
index 0454bc3..eac6400 100644 (file)
@@ -49,21 +49,6 @@ if (!confirm_sesskey()) {
     throw new moodle_exception('missingparameter');
 }
 
-/* TO DO
-  if DB config plugin table is not good for dealing with token reference and token confirmation
-  => create other DB table
-  -----------------------------------------------------------------------------
-  Local Type | Token | Local WS | Remote Type | Remote URL        | Confirmed
-  -----------------------------------------------------------------------------
-  HUB        4er4e   server    HUB-DIRECTORY  http...moodle.org      Yes
-  HUB        73j53   client    HUB-DIRECTORY  http...moodle.org      Yes
-  SITE       dfsd7   server    HUB            http...hub             Yes
-  SITE       fd8fd   client    HUB            http...hub             Yes
-  HUB        ds78s   server    SITE           http...site.com        Yes
-  HUB-DIR.   d7d8s   server    HUB            http...hub             Yes
-  -----------------------------------------------------------------------------
- */
-
 $registrationmanager = new registration_manager();
 
 $registeredhub = $registrationmanager->get_registeredhub($huburl);
@@ -132,7 +117,8 @@ if (!empty($fromform) and empty($update) and confirm_sesskey()) {
         if (empty($unconfirmedhub)) {
             //we save the token into the communication table in order to have a reference
             $unconfirmedhub = new stdClass();
-            $unconfirmedhub->token = get_site_identifier();
+            $unconfirmedhub->token = $registrationmanager->get_site_secret_for_hub($huburl);
+            $unconfirmedhub->secret = $unconfirmedhub->token;
             $unconfirmedhub->huburl = $huburl;
             $unconfirmedhub->hubname = $hubname;
             $unconfirmedhub->confirmed = 0;
diff --git a/admin/registration/renewregistration.php b/admin/registration/renewregistration.php
new file mode 100644 (file)
index 0000000..ec9fce2
--- /dev/null
@@ -0,0 +1,76 @@
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// This file is part of Moodle - http://moodle.org/                      //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//                                                                       //
+// 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    moodle
+ * @subpackage registration
+ * @author     Jerome Mouneyrac <jerome@mouneyrac.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
+ * @copyright  (C) 1999 onwards Martin Dougiamas  http://dougiamas.com
+ *
+ * The administrator is redirect to this page from the hub to renew a registration
+ * process because
+ */
+
+require('../../config.php');
+require_once($CFG->libdir . '/adminlib.php');
+require_once($CFG->dirroot . '/' . $CFG->admin . '/registration/lib.php');
+
+$url = optional_param('url', '', PARAM_URL);
+$hubname = optional_param('hubname', '', PARAM_TEXT);
+$token = optional_param('token', '', PARAM_TEXT);
+
+admin_externalpage_setup('registrationindex');
+
+//check that we are waiting a confirmation from this hub, and check that the token is correct
+$registrationmanager = new registration_manager();
+$registeredhub = $registrationmanager->get_unconfirmedhub($url);
+if (!empty($registeredhub) and $registeredhub->token == $token) {
+
+    echo $OUTPUT->header();
+    echo $OUTPUT->heading(get_string('renewregistration', 'hub'), 3, 'main');
+    $hublink = html_writer::tag('a', $hubname, array('href' => $url));
+
+    $registrationmanager->delete_registeredhub($url);
+
+    //Mooch case, need to recreate the siteidentifier
+    if ($url == HUB_MOODLEORGHUBURL) {
+        $CFG->siteidentifier = null;
+        get_site_identifier();
+    }
+
+    $deletedregmsg = get_string('previousregistrationdeleted', 'hub', $hublink);
+
+    $button = new single_button(new moodle_url('/admin/registration/index.php'),
+                    get_string('restartregistration', 'hub'));
+    $button->class = 'restartregbutton';
+
+    echo html_writer::tag('div', $deletedregmsg . $OUTPUT->render($button),
+            array('class' => 'mdl-align'));
+
+    echo $OUTPUT->footer();
+} else {
+    throw new moodle_exception('wrongtoken', 'hub',
+            $CFG->wwwroot . '/' . $CFG->admin . '/registration/index.php');
+}
+
+
index 4ef1f6f..6e33a51 100644 (file)
@@ -10,6 +10,7 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $options[99] = get_string('timezonenotforced', 'admin');
     $temp->add(new admin_setting_configselect('forcetimezone', get_string('forcetimezone', 'admin'), get_string('helpforcetimezone', 'admin'), 99, $options));
     $temp->add(new admin_settings_country_select('country', get_string('country', 'admin'), get_string('configcountry', 'admin'), 0));
+    $temp->add(new admin_setting_configtext('defaultcity', get_string('defaultcity', 'admin'), get_string('defaultcity_help', 'admin'), ''));
 
     $temp->add(new admin_setting_heading('iplookup', get_string('iplookup', 'admin'), get_string('iplookupinfo', 'admin')));
     $temp->add(new admin_setting_configfile('geoipfile', get_string('geoipfile', 'admin'), get_string('configgeoipfile', 'admin', $CFG->dataroot.'/geoip/'), $CFG->dataroot.'/geoip/GeoLiteCity.dat'));
index 052405d..7f6647f 100644 (file)
@@ -266,5 +266,6 @@ $ADMIN->add('server', $temp);
 
 
 $ADMIN->add('server', new admin_externalpage('adminregistration', get_string('registration','admin'), "$CFG->wwwroot/$CFG->admin/registration/index.php"));
+$ADMIN->add('root', new admin_externalpage('bloglevelupgrade', get_string('bloglevelupgrade', 'admin'), $CFG->wwwroot.'/'.$CFG->admin.'/blocklevelupgrade.php', 'moodle/site:config', true));
 
 } // end of speedup
index 5ee0e8a..b25f609 100755 (executable)
@@ -572,6 +572,10 @@ if ($formdata = $mform->is_cancelled()) {
             $user->timemodified = time();
             $user->timecreated = time();
 
+            if (!empty($CFG->defaultcity) and !property_exists($user, 'city')) {
+                $user->city = $CFG->defaultcity;
+            }
+
             if (isset($user->auth) && empty($user->auth)) {
                 $user->auth = 'manual';
             }
index 4f875b5..29befd9 100644 (file)
@@ -358,6 +358,10 @@ class auth_plugin_db extends auth_plugin_base {
                     echo "\t"; print_string('auth_dbreviveduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)); echo "\n";
 
                 } else {
+                    if (!empty($CFG->defaultcity) and !property_exists($user, 'city')) {
+                        $user->city = $CFG->defaultcity;
+                    }
+
                     $id = $DB->insert_record ('user',$user); // it is truly a new user
                     echo "\t"; print_string('auth_dbinsertuser','auth_db',array('name'=>$user->username, 'id'=>$id)); echo "\n";
                     // if relevant, tag for password generation
index d9cd436..d2d8b01 100644 (file)
@@ -490,6 +490,10 @@ class auth_plugin_ldap extends auth_plugin_base {
             print_error('auth_ldap_create_error', 'auth_ldap');
         }
 
+        if (!empty($CFG->defaultcity) and !property_exists($user, 'city')) {
+            $user->city = $CFG->defaultcity;
+        }
+
         $user->id = $DB->insert_record('user', $user);
 
         // Save any custom profile field information
@@ -849,6 +853,10 @@ class auth_plugin_ldap extends auth_plugin_base {
                     $user->lang = $CFG->lang;
                 }
 
+                if (!empty($CFG->defaultcity) and !property_exists($user, 'city')) {
+                    $user->city = $CFG->defaultcity;
+                }
+
                 $id = $DB->insert_record('user', $user);
                 echo "\t"; print_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)); echo "\n";
                 if (!empty($this->config->forcechangepassword)) {
index d5062bc..fa0591e 100644 (file)
@@ -242,7 +242,7 @@ class auth_plugin_shibboleth extends auth_plugin_base {
 
         // set to defaults if undefined
         if (!isset($config->auth_instructions) or empty($config->user_attribute)) {
-            $config->auth_instructions = get_string('shibboleth_instructions', 'auth', $CFG->wwwroot.'/auth/shibboleth/index.php');
+            $config->auth_instructions = get_string('auth_shib_instructions', 'auth_shibboleth', $CFG->wwwroot.'/auth/shibboleth/index.php');
         }
         if (!isset ($config->user_attribute)) {
             $config->user_attribute = '';
@@ -292,7 +292,7 @@ class auth_plugin_shibboleth extends auth_plugin_base {
         } else {
             // Check if integrated WAYF was enabled and is now turned off
             // If it was and only then, reset the Moodle alternate URL
-            if ($this->config->alt_login == 'on'){
+            if (isset($this->config->alt_login) and $this->config->alt_login == 'on'){
                 set_config('alt_login',    'off',    'auth/shibboleth');
                 set_config('alternateloginurl', '');
             }
diff --git a/backup/moodle2/backup_format_plugin.class.php b/backup/moodle2/backup_format_plugin.class.php
new file mode 100644 (file)
index 0000000..d1af1b9
--- /dev/null
@@ -0,0 +1,50 @@
+<?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    moodlecore
+ * @subpackage backup-moodle2
+ * @copyright  2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Class extending standard backup_plugin in order to implement some
+ * helper methods related with the course formats (format plugin)
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class backup_format_plugin extends backup_plugin {
+
+    protected $courseformat; // To store the format (course->format) of the instance
+
+    public function __construct($plugintype, $pluginname, $optigroup, $step) {
+
+        parent::__construct($plugintype, $pluginname, $optigroup, $step);
+
+        $this->courseformat = backup_plan_dbops::get_courseformat_from_courseid($this->task->get_courseid());
+
+    }
+
+    /**
+     * Return the condition encapsulated into sqlparam format
+     * to get evaluated by value, not by path nor processor setting
+     */
+    protected function get_format_condition() {
+        return array('sqlparam' => $this->courseformat);
+    }
+}
index a1cb25c..89c8654 100644 (file)
@@ -34,6 +34,7 @@ require_once($CFG->dirroot . '/backup/moodle2/backup_default_block_task.class.ph
 require_once($CFG->dirroot . '/backup/moodle2/backup_xml_transformer.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/backup_subplugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/backup_settingslib.php');
 require_once($CFG->dirroot . '/backup/moodle2/backup_stepslib.php');
index bd10498..da0abfa 100644 (file)
@@ -33,12 +33,16 @@ abstract class backup_plugin {
     protected $pluginname;
     protected $connectionpoint;
     protected $optigroup; // Optigroup, parent of all optigroup elements
+    protected $step;
+    protected $task;
 
-    public function __construct($plugintype, $pluginname, $optigroup) {
+    public function __construct($plugintype, $pluginname, $optigroup, $step) {
         $this->plugintype = $plugintype;
         $this->pluginname = $pluginname;
-        $this->optigroup     = $optigroup;
+        $this->optigroup  = $optigroup;
         $this->connectionpoint = '';
+        $this->step       = $step;
+        $this->task       = $step->get_task();
     }
 
     public function define_plugin_structure($connectionpoint) {
@@ -52,6 +56,22 @@ abstract class backup_plugin {
         }
     }
 
+// Protected API starts here
+
+// backup_step/structure_step/task wrappers
+
+    /**
+     * Returns the value of one (task/plan) setting
+     */
+    protected function get_setting_value($name) {
+        if (is_null($this->task)) {
+            throw new backup_step_exception('not_specified_backup_task');
+        }
+        return $this->task->get_setting_value($name);
+    }
+
+// end of backup_step/structure_step/task wrappers
+
     /**
      * Factory method that will return one backup_plugin_element (backup_optigroup_element)
      * with its name automatically calculated, based one the plugin being handled (type, name)
index 269ad4b..6b4dbf3 100644 (file)
@@ -125,13 +125,21 @@ abstract class backup_activity_structure_step extends backup_structure_step {
             $backupfile = $subpluginsdir . '/backup/moodle2/' . $classname . '.class.php';
             if (file_exists($backupfile)) {
                 require_once($backupfile);
-                $backupsubplugin = new $classname($subplugintype, $name, $optigroup);
+                $backupsubplugin = new $classname($subplugintype, $name, $optigroup, $this);
                 // Add subplugin returned structure to optigroup
                 $backupsubplugin->define_subplugin_structure($element->get_name());
             }
         }
     }
 
+    /**
+     * As far as activity backup steps are implementing backup_subplugin stuff, they need to
+     * have the parent task available for wrapping purposes (get course/context....)
+     */
+    public function get_task() {
+        return $this->task;
+    }
+
     /**
      * Wraps any activity backup structure within the common 'activity' element
      * that will include common to all activities information like id, context...
@@ -310,6 +318,9 @@ class backup_module_structure_step extends backup_structure_step {
         $availability = new backup_nested_element('availability', array('id'), array(
             'sourcecmid', 'requiredcompletion', 'gradeitemid', 'grademin', 'grademax'));
 
+        // attach format plugin structure to $module element, only one allowed
+        $this->add_plugin_structure('format', $module, false);
+
         // Define the tree
         $module->add_child($availinfo);
         $availinfo->add_child($availability);
@@ -346,6 +357,9 @@ class backup_section_structure_step extends backup_structure_step {
         $section = new backup_nested_element('section', array('id'), array(
             'number', 'name', 'summary', 'summaryformat', 'sequence', 'visible'));
 
+        // attach format plugin structure to $section element, only one allowed
+        $this->add_plugin_structure('format', $section, false);
+
         // Define sources
 
         $section->set_source_table('course_sections', array('id' => backup::VAR_SECTIONID));
@@ -395,6 +409,9 @@ class backup_course_structure_step extends backup_structure_step {
 
         $module = new backup_nested_element('module', array(), array('modulename'));
 
+        // attach format plugin structure to $course element, only one allowed
+        $this->add_plugin_structure('format', $course, false);
+
         // Build the tree
 
         $course->add_child($category);
index 75fc21d..ca3e953 100644 (file)
@@ -34,12 +34,16 @@ abstract class backup_subplugin {
     protected $subpluginname;
     protected $connectionpoint;
     protected $optigroup; // Optigroup, parent of all optigroup elements
+    protected $step;
+    protected $task;
 
-    public function __construct($subplugintype, $subpluginname, $optigroup) {
+    public function __construct($subplugintype, $subpluginname, $optigroup, $step) {
         $this->subplugintype = $subplugintype;
         $this->subpluginname = $subpluginname;
         $this->optigroup     = $optigroup;
         $this->connectionpoint = '';
+        $this->step          = $step;
+        $this->task          = $step->get_task();
     }
 
     public function define_subplugin_structure($connectionpoint) {
@@ -53,6 +57,22 @@ abstract class backup_subplugin {
         }
     }
 
+// Protected API starts here
+
+// backup_step/structure_step/task wrappers
+
+    /**
+     * Returns the value of one (task/plan) setting
+     */
+    protected function get_setting_value($name) {
+        if (is_null($this->task)) {
+            throw new backup_step_exception('not_specified_backup_task');
+        }
+        return $this->task->get_setting_value($name);
+    }
+
+// end of backup_step/structure_step/task wrappers
+
     /**
      * Factory method that will return one backup_subplugin_element (backup_optigroup_element)
      * with its name automatically calculated, based one the subplugin being handled (type, name)
diff --git a/backup/moodle2/restore_format_plugin.class.php b/backup/moodle2/restore_format_plugin.class.php
new file mode 100644 (file)
index 0000000..454d3a8
--- /dev/null
@@ -0,0 +1,33 @@
+<?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    moodlecore
+ * @subpackage backup-moodle2
+ * @copyright  2011 onwards Eloy Lafuente (stronk7) {@link http://stronk7.com}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Class extending standard restore_plugin in order to implement some
+ * helper methods related with the course formats (format plugin)
+ *
+ * TODO: Finish phpdocs
+ */
+abstract class restore_format_plugin extends restore_plugin {
+    // Love these classes. :-) Nothing special to customize here for now
+}
index 46ea999..f685d38 100644 (file)
@@ -33,8 +33,10 @@ require_once($CFG->dirroot . '/backup/moodle2/restore_block_task.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/restore_default_block_task.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/restore_plugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/restore_qtype_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/restore_format_plugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/backup_plugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/backup_qtype_plugin.class.php');
+require_once($CFG->dirroot . '/backup/moodle2/backup_format_plugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/restore_subplugin.class.php');
 require_once($CFG->dirroot . '/backup/moodle2/restore_settingslib.php');
 require_once($CFG->dirroot . '/backup/moodle2/restore_stepslib.php');
index 3f9f5bb..8103ada 100644 (file)
@@ -202,6 +202,18 @@ abstract class restore_plugin {
         return $this->step->apply_date_offset($value);
     }
 
+    /**
+     * Returns the value of one (task/plan) setting
+     */
+    protected function get_setting_value($name) {
+        if (is_null($this->task)) {
+            throw new restore_step_exception('not_specified_restore_task');
+        }
+        return $this->task->get_setting_value($name);
+    }
+
+// end of restore_step/structure_step/task wrappers
+
     /**
      * Simple helper function that returns the name for the restore_path_element
      * It's not mandatory to use it but recommended ;-)
index 75bf7c3..5c03bcb 100644 (file)
@@ -32,12 +32,14 @@ class restore_section_task extends restore_task {
 
     protected $info; // info related to section gathered from backup file
     protected $contextid; // course context id
+    protected $sectionid; // new (target) id of the course section
 
     /**
      * Constructor - instantiates one object of this class
      */
     public function __construct($name, $info, $plan = null) {
         $this->info = $info;
+        $this->sectionid = 0;
         parent::__construct($name, $plan);
     }
 
@@ -49,10 +51,18 @@ class restore_section_task extends restore_task {
         return $this->get_basepath() . '/sections/section_' . $this->info->sectionid;
     }
 
+    public function set_sectionid($sectionid) {
+        $this->sectionid = $sectionid;
+    }
+
     public function get_contextid() {
         return $this->contextid;
     }
 
+    public function get_sectionid() {
+        return $this->sectionid;
+    }
+
     /**
      * Create all the steps that will be part of this task
      */
index 2a3fe48..b103850 100644 (file)
@@ -660,8 +660,8 @@ class restore_groups_structure_step extends restore_structure_step {
         $params = array('courseid' => $this->get_courseid(), 'grname' => $data->name);
         if (!empty($data->description)) {
             $description_clause = ' AND ' .
-                                  $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':desc');
-           $params['desc'] = $data->description;
+                                  $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':description');
+           $params['description'] = $data->description;
         }
         if (!$groupdb = $DB->get_record_sql("SELECT *
                                                FROM {groups}
@@ -708,8 +708,8 @@ class restore_groups_structure_step extends restore_structure_step {
         $params = array('courseid' => $this->get_courseid(), 'grname' => $data->name);
         if (!empty($data->description)) {
             $description_clause = ' AND ' .
-                                  $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':desc');
-           $params['desc'] = $data->description;
+                                  $DB->sql_compare_text('description') . ' = ' . $DB->sql_compare_text(':description');
+           $params['description'] = $data->description;
         }
         if (!$groupingdb = $DB->get_record_sql("SELECT *
                                                   FROM {groupings}
@@ -920,7 +920,12 @@ class restore_process_categories_and_questions extends restore_execution_step {
 class restore_section_structure_step extends restore_structure_step {
 
     protected function define_structure() {
-        return array(new restore_path_element('section', '/section'));
+        $section = new restore_path_element('section', '/section');
+
+        // Apply for 'format' plugins optional paths at section level
+        $this->add_plugin_structure('format', $section);
+
+        return array($section);
     }
 
     public function process_section($data) {
@@ -962,6 +967,10 @@ class restore_section_structure_step extends restore_structure_step {
         // Annotate the section mapping, with restorefiles option if needed
         $this->set_mapping('course_section', $oldid, $newitemid, $restorefiles);
 
+        // set the new course_section id in the task
+        $this->task->set_sectionid($newitemid);
+
+
         // Commented out. We never modify course->numsections as far as that is used
         // by a lot of people to "hide" sections on purpose (so this remains as used to be in Moodle 1.x)
         // Note: We keep the code here, to know about and because of the possibility of making this
@@ -991,12 +1000,15 @@ class restore_course_structure_step extends restore_structure_step {
 
     protected function define_structure() {
 
-        $course = new restore_path_element('course', '/course', true); // Grouped
+        $course = new restore_path_element('course', '/course');
         $category = new restore_path_element('category', '/course/category');
         $tag = new restore_path_element('tag', '/course/tags/tag');
-        $allowed = new restore_path_element('allowed', '/course/allowed_modules/module');
+        $allowed_module = new restore_path_element('allowed_module', '/course/allowed_modules/module');
 
-        return array($course, $category, $tag, $allowed);
+        // Apply for 'format' plugins optional paths at course level
+        $this->add_plugin_structure('format', $course);
+
+        return array($course, $category, $tag, $allowed_module);
     }
 
     /**
@@ -1026,9 +1038,6 @@ class restore_course_structure_step extends restore_structure_step {
         $data->shortname= $shortname;
         $data->idnumber = '';
 
-        // Category is set by UI when choosing the destination
-        unset($data->category);
-
         $data->startdate= $this->apply_date_offset($data->startdate);
         if ($data->defaultgroupingid) {
             $data->defaultgroupingid = $this->get_mappingid('grouping', $data->defaultgroupingid);
@@ -1050,34 +1059,56 @@ class restore_course_structure_step extends restore_structure_step {
         // Course record ready, update it
         $DB->update_record('course', $data);
 
-        // Course tags
-        if (!empty($CFG->usetags) && isset($coursetags)) { // if enabled in server and present in backup
+        // Role name aliases
+        restore_dbops::set_course_role_names($this->get_restoreid(), $this->get_courseid());
+    }
+
+    public function process_category($data) {
+        // Nothing to do with the category. UI sets it before restore starts
+    }
+
+    public function process_tag($data) {
+        global $CFG, $DB;
+
+        $data = (object)$data;
+
+        if (!empty($CFG->usetags)) { // if enabled in server
+            // TODO: This is highly inneficient. Each time we add one tag
+            // we fetch all the existing because tag_set() deletes them
+            // so everything must be reinserted on each call
             $tags = array();
-            foreach ($coursetags as $coursetag) {
-                $coursetag = (object)$coursetag;
-                $tags[] = $coursetag->rawname;
+            $existingtags = tag_get_tags('course', $this->get_courseid());
+            // Re-add all the existitng tags
+            foreach ($existingtags as $existingtag) {
+                $tags[] = $existingtag->rawname;
             }
+            // Add the one being restored
+            $tags[] = $data->rawname;
+            // Send all the tags back to the course
             tag_set('course', $this->get_courseid(), $tags);
         }
-        // Course allowed modules
-        if (!empty($data->restrictmodules) && !empty($coursemodules)) {
+    }
+
+    public function process_allowed_module($data) {
+        global $CFG, $DB;
+
+        $data = (object)$data;
+
+        // only if enabled by admin setting
+        if (!empty($CFG->restrictmodulesfor) && $CFG->restrictmodulesfor == 'all') {
             $available = get_plugin_list('mod');
-            foreach ($coursemodules as $coursemodule) {
-                $mname = $coursemodule['modulename'];
-                if (array_key_exists($mname, $available)) {
-                    if ($module = $DB->get_record('modules', array('name' => $mname, 'visible' => 1))) {
-                        $rec = new stdclass();
-                        $rec->course = $this->get_courseid();
-                        $rec->module = $module->id;
-                        if (!$DB->record_exists('course_allowed_modules', (array)$rec)) {
-                            $DB->insert_record('course_allowed_modules', $rec);
-                        }
+            $mname = $data->modulename;
+            if (array_key_exists($mname, $available)) {
+                if ($module = $DB->get_record('modules', array('name' => $mname, 'visible' => 1))) {
+                    $rec = new stdclass();
+                    $rec->course = $this->get_courseid();
+                    $rec->module = $module->id;
+                    if (!$DB->record_exists('course_allowed_modules', (array)$rec)) {
+                        $DB->insert_record('course_allowed_modules', $rec);
                     }
                 }
             }
         }
-        // Role name aliases
-        restore_dbops::set_course_role_names($this->get_restoreid(), $this->get_courseid());
     }
 
     protected function after_execute() {
@@ -1786,7 +1817,7 @@ class restore_activity_grades_structure_step extends restore_structure_step {
         $data->timecreated  = $this->apply_date_offset($data->timecreated);
         $data->timemodified = $this->apply_date_offset($data->timemodified);
 
-        $gradeitem = new grade_item($data);
+        $gradeitem = new grade_item($data, false);
         $gradeitem->insert('restore');
 
         //sortorder is automatically assigned when inserting. Re-instate the previous sortorder
@@ -1809,7 +1840,7 @@ class restore_activity_grades_structure_step extends restore_structure_step {
         // TODO: Ask, all the rest of locktime/exported... work with time... to be rolled?
         $data->overridden = $this->apply_date_offset($data->overridden);
 
-        $grade = new grade_grade($data);
+        $grade = new grade_grade($data, false);
         $grade->insert('restore');
         // no need to save any grade_grade mapping
     }
@@ -1963,11 +1994,15 @@ class restore_module_structure_step extends restore_structure_step {
 
         $paths = array();
 
-        $paths[] = new restore_path_element('module', '/module');
+        $module = new restore_path_element('module', '/module');
+        $paths[] = $module;
         if ($CFG->enableavailability) {
             $paths[] = new restore_path_element('availability', '/module/availability_info/availability');
         }
 
+        // Apply for 'format' plugins optional paths at module level
+        $this->add_plugin_structure('format', $module);
+
         return $paths;
     }
 
index 7f2b175..d22a0d9 100644 (file)
@@ -151,6 +151,18 @@ abstract class restore_subplugin {
         return $this->step->apply_date_offset($value);
     }
 
+    /**
+     * Returns the value of one (task/plan) setting
+     */
+    protected function get_setting_value($name) {
+        if (is_null($this->task)) {
+            throw new restore_step_exception('not_specified_restore_task');
+        }
+        return $this->task->get_setting_value($name);
+    }
+
+// end of restore_step/structure_step/task wrappers
+
     /**
      * Simple helper function that returns the name for the restore_path_element
      * It's not mandatory to use it but recommended ;-)
index 9959236..fe5d941 100644 (file)
@@ -119,6 +119,15 @@ abstract class backup_plan_dbops extends backup_dbops {
         return $sectionsarr;
     }
 
+    /**
+     * Given one course id, return its format in DB
+     */
+    public static function get_courseformat_from_courseid($courseid) {
+        global $DB;
+
+        return $DB->get_field('course', 'format', array('id' => $courseid));
+    }
+
     /**
      * Return the wwwroot of the $CFG->mnet_localhost_id host
      * caching it along the request
@@ -138,34 +147,49 @@ abstract class backup_plan_dbops extends backup_dbops {
     * Returns the default backup filename, based in passed params.
     *
     * Default format is (see MDL-22145)
-    *   backup word - format - type - name - date - info . mbz
+    * backup word - format - type - name - date - info . mbz
     * where name is variable (course shortname, section name/id, activity modulename + cmid)
-    * and info can be (nu = no user info, an = anonymized)
+    * and info can be (nu = no user info, an = anonymized). The last param $useidasname,
+    * defaulting to false, allows to replace the course shortname by the course id (used
+    * by automated backups, to avoid non-ascii chars in OS filesystem)
+    *
+    * @param string $format One of backup::FORMAT_
+    * @param string $type One of backup::TYPE_
+    * @param int $courseid/$sectionid/$cmid
+    * @param bool $users Should be true is users were included in the backup
+    * @param bool $anonymised Should be true is user information was anonymized.
+    * @param bool $useidasname true to use id, false to use strings (default)
+    * @return string The filename to use
     */
-    public static function get_default_backup_filename($format, $type, $id, $users, $anonymised) {
+    public static function get_default_backup_filename($format, $type, $id, $users, $anonymised, $useidasname = false) {
         global $DB;
 
         // Calculate backup word
         $backupword = str_replace(' ', '_', moodle_strtolower(get_string('backupfilename')));
         $backupword = trim(clean_filename($backupword), '_');
 
-        // Calculate proper name element (based on type)
-        switch ($type) {
-            case backup::TYPE_1COURSE:
-                $shortname = $DB->get_field('course', 'shortname', array('id' => $id));
-                break;
-            case backup::TYPE_1SECTION:
-                if (!$shortname = $DB->get_field('course_sections', 'name', array('id' => $id))) {
-                    $shortname = $DB->get_field('course_sections', 'section', array('id' => $id));
-                }
-                break;
-            case backup::TYPE_1ACTIVITY:
-                $cm = get_coursemodule_from_id(null, $id);
-                $shortname = $cm->modname . $id;
-                break;
+        $shortname = '';
+        // Not $useidasname, lets calculate it, else $id will be used
+        if (!$useidasname) {
+            // Calculate proper name element (based on type)
+            switch ($type) {
+                case backup::TYPE_1COURSE:
+                    $shortname = $DB->get_field('course', 'shortname', array('id' => $id));
+                    break;
+                case backup::TYPE_1SECTION:
+                    if (!$shortname = $DB->get_field('course_sections', 'name', array('id' => $id))) {
+                        $shortname = $DB->get_field('course_sections', 'section', array('id' => $id));
+                    }
+                    break;
+                case backup::TYPE_1ACTIVITY:
+                    $cm = get_coursemodule_from_id(null, $id);
+                    $shortname = $cm->modname . $id;
+                    break;
+            }
+            $shortname = str_replace(' ', '_', $shortname);
+            $shortname = moodle_strtolower(trim(clean_filename($shortname), '_'));
         }
-        $shortname = str_replace(' ', '_', $shortname);
-        $shortname = moodle_strtolower(trim(clean_filename($shortname), '_'));
+
         $name = empty($shortname) ? $id : $shortname;
 
         // Calculate date
index 6044872..2e17bb9 100644 (file)
@@ -49,8 +49,8 @@ abstract class backup_cron_automated_helper {
 
     /** Run if required by the schedule set in config. Default. **/
     const RUN_ON_SCHEDULE = 0;
-    /** Run immediatly. **/
-    const RUN_IMMEDIATLY = 1;
+    /** Run immediately. **/
+    const RUN_IMMEDIATELY = 1;
 
     const AUTO_BACKUP_DISABLED = 0;
     const AUTO_BACKUP_ENABLED = 1;
@@ -75,7 +75,7 @@ abstract class backup_cron_automated_helper {
             return $state;
         } else if ($state === backup_cron_automated_helper::STATE_RUNNING) {
             mtrace('RUNNING');
-            if ($rundirective == self::RUN_IMMEDIATLY) {
+            if ($rundirective == self::RUN_IMMEDIATELY) {
                 mtrace('automated backups are already. If this script is being run by cron this constitues an error. You will need to increase the time between executions within cron.');
             } else {
                 mtrace("automated backup are already running. Execution delayed");
@@ -130,12 +130,12 @@ abstract class backup_cron_automated_helper {
                     $DB->update_record('backup_courses', $backupcourse);
                     mtrace('Skipping unchanged course '.$course->fullname);
                     $skipped = true;
-                } else if (($backupcourse->nextstarttime >= 0 && $backupcourse->nextstarttime < $now) || $rundirective == self::RUN_IMMEDIATLY) {
+                } else if (($backupcourse->nextstarttime >= 0 && $backupcourse->nextstarttime < $now) || $rundirective == self::RUN_IMMEDIATELY) {
                     mtrace('Backing up '.$course->fullname, '...');
 
                     //We have to send a email because we have included at least one backup
                     $emailpending = true;
-                    
+
                     //Only make the backup if laststatus isn't 2-UNFINISHED (uncontrolled error)
                     if ($backupcourse->laststatus != 2) {
                         //Set laststarttime
@@ -270,7 +270,7 @@ abstract class backup_cron_automated_helper {
         $config = get_config('backup');
         $midnight = usergetmidnight($now, $timezone);
         $date = usergetdate($now, $timezone);
-        
+
         //Get number of days (from today) to execute backups
         $automateddays = substr($config->backup_auto_weekdays,$date['wday']) . $config->backup_auto_weekdays;
         $daysfromtoday = strpos($automateddays, "1");
@@ -335,7 +335,7 @@ abstract class backup_cron_automated_helper {
             $users = $bc->get_plan()->get_setting('users')->get_value();
             $anonymised = $bc->get_plan()->get_setting('anonymize')->get_value();
             $bc->get_plan()->get_setting('filename')->set_value(backup_plan_dbops::get_default_backup_filename($format, $type, $id, $users, $anonymised));
-            
+
             $bc->set_status(backup::STATUS_AWAITING);
 
             $outcome = $bc->execute_plan();
@@ -347,7 +347,7 @@ abstract class backup_cron_automated_helper {
                 $dir = null;
             }
             if (!empty($dir) && $storage !== 0) {
-                $filename = self::get_external_filename($course->id, $format, $type, $users, $anonymised);
+                $filename = backup_plan_dbops::get_default_backup_filename($format, $type, $course->id, $users, $anonymised, true);
                 $outcome = $file->copy_content_to($dir.'/'.$filename);
                 if ($outcome && $storage === 1) {
                     $file->delete();
@@ -366,38 +366,6 @@ abstract class backup_cron_automated_helper {
         return true;
     }
 
-    /**
-     * Gets the filename to use for the backup when it is being moved to an
-     * external location.
-     *
-     * Note: we use the course id in the filename rather than the course shortname
-     * because it may contain UTF-8 characters that could cause problems for the
-     * recieving filesystem.
-     *
-     * @param int $courseid
-     * @param string $format One of backup::FORMAT_
-     * @param string $type One of backup::TYPE_
-     * @param bool $users Should be true is users were included in the backup
-     * @param bool $anonymised Should be true is user information was anonymized.
-     * @return string The filename to use
-     */
-    public static function get_external_filename($courseid, $format, $type, $users, $anonymised) {
-        $backupword = str_replace(' ', '_', moodle_strtolower(get_string('backupfilename')));
-        $backupword = trim(clean_filename($backupword), '_');
-        // Calculate date
-        $backupdateformat = str_replace(' ', '_', get_string('backupnameformat', 'langconfig'));
-        $date = userdate(time(), $backupdateformat, 99, false);
-        $date = moodle_strtolower(trim(clean_filename($date), '_'));
-        // Calculate info
-        $info = '';
-        if (!$users) {
-            $info = 'nu';
-        } else if ($anonymised) {
-            $info = 'an';
-        }
-        return $backupword.'-'.$format.'-'.$type.'-'.$courseid.'-'.$date.'-'.$info.'.mbz';
-    }
-
     /**
      * Removes deleted courses fromn the backup_courses table so that we don't
      * waste time backing them up.
@@ -488,32 +456,39 @@ abstract class backup_cron_automated_helper {
             $filearea = 'automated';
             $itemid = 0;
             $files = array();
+            // Store all the matching files into timemodified => stored_file array
             foreach ($fs->get_area_files($context->id, $component, $filearea, $itemid) as $file) {
                 if (strpos($file->get_filename(), $backupword) !== 0) {
                     continue;
                 }
                 $files[$file->get_timemodified()] = $file;
             }
-            arsort($files);
+            if (count($files) <= $keep) {
+                // There are less matching files than the desired number to keep
+                // do there is nothing to clean up.
+                return 0;
+            }
+            // Sort by keys descending (newer to older filemodified)
+            krsort($files);
             $remove = array_splice($files, $keep);
             foreach ($remove as $file) {
                 $file->delete();
             }
-            //mtrace('Removed '.count($remove).' old backup file(s) from the data directory');
+            //mtrace('Removed '.count($remove).' old backup file(s) from the automated filearea');
         }
 
         // Clean up excess backups in the specified external directory
         if (!empty($dir) && ($storage == 1 || $storage == 2)) {
-            // Calculate backup filename regex
-            
+            // Calculate backup filename regex, ignoring the date/time/info parts that can be
+            // variable, depending of languages, formats and automated backup settings
             $filename = $backupword . '-' . backup::FORMAT_MOODLE . '-' . backup::TYPE_1COURSE . '-' .$course->id . '-';
+            $regex = '#^'.preg_quote($filename, '#').'.*\.mbz$#';
 
-            $regex = '#^'.preg_quote($filename, '#').'(\d{8})\-(\d{4})\-[a-z]{2}\.mbz$#S';
-
+            // Store all the matching files into fullpath => timemodified array
             $files = array();
             foreach (scandir($dir) as $file) {
                 if (preg_match($regex, $file, $matches)) {
-                    $files[$file] = $matches[1].$matches[2];
+                    $files[$file] = filemtime($dir . '/' . $file);
                 }
             }
             if (count($files) <= $keep) {
@@ -521,14 +496,15 @@ abstract class backup_cron_automated_helper {
                 // do there is nothing to clean up.
                 return 0;
             }
+            // Sort by values descending (newer to older filemodified)
             arsort($files);
             $remove = array_splice($files, $keep);
             foreach (array_keys($remove) as $file) {
-                unlink($dir.'/'.$file);
+                unlink($dir . '/' . $file);
             }
             //mtrace('Removed '.count($remove).' old backup file(s) from external directory');
         }
 
         return true;
     }
-}
\ No newline at end of file
+}
index 29b76ac..f62fee9 100644 (file)
@@ -101,6 +101,14 @@ abstract class backup_structure_step extends backup_step {
         $structure->destroy();
     }
 
+    /**
+     * As far as backup structure steps are implementing backup_plugin stuff, they need to
+     * have the parent task available for wrapping purposes (get course/context....)
+     */
+    public function get_task() {
+        return $this->task;
+    }
+
 // Protected API starts here
 
     /**
@@ -133,7 +141,7 @@ abstract class backup_structure_step extends backup_step {
             $backupfile = $plugindir . '/backup/moodle2/' . $classname . '.class.php';
             if (file_exists($backupfile)) {
                 require_once($backupfile);
-                $backupplugin = new $classname($plugintype, $name, $optigroup);
+                $backupplugin = new $classname($plugintype, $name, $optigroup, $this);
                 // Add plugin returned structure to optigroup
                 $backupplugin->define_plugin_structure($element->get_name());
             }
index 1cd23b0..b48defc 100644 (file)
@@ -94,6 +94,10 @@ abstract class base_nested_element extends base_final_element {
     }
 
     protected function check_and_set_used($element) {
+        // First of all, check the element being added doesn't conflict with own final elements
+        if (array_key_exists($element->get_name(), $this->final_elements)) {
+            throw new base_element_struct_exception('baseelementchildnameconflict', $element->get_name());
+        }
         $grandparent = $this->get_grandoptigroupelement_or_grandparent();
         if ($existing = array_intersect($grandparent->get_used(), $element->get_used())) { // Check the element isn't being used already
             throw new base_element_struct_exception('baseelementexisting', implode($existing));
index 0f5ed64..206fab0 100644 (file)
@@ -393,5 +393,16 @@ class base_nested_element_test extends UnitTestCase {
             $this->assertTrue($e instanceof base_element_parent_exception);
         }
 
+        // Add child element already used by own final elements
+        $nested = new mock_base_nested_element('PARENT1', null, array('FINAL1', 'FINAL2'));
+        $child = new mock_base_nested_element('FINAL2', null, array('FINAL3', 'FINAL4'));
+        try {
+            $nested->add_child($child);
+            $this->fail("Expecting base_element_struct_exception exception, none occurred");
+        } catch (Exception $e) {
+            $this->assertTrue($e instanceof base_element_struct_exception);
+            $this->assertEqual($e->errorcode, 'baseelementchildnameconflict');
+            $this->assertEqual($e->a, 'FINAL2');
+        }
     }
 }
index 9dbe9b8..e87ac9d 100644 (file)
@@ -104,13 +104,18 @@ abstract class simplified_parser_processor extends progressive_parser_processor
                     $alltagswhitespace = false;
                     continue;
                 }
+
                 // If the path including the tag name matches another selected path
-                // (registered or parent) delete it, another chunk will contain that info
+                // (registered or parent) and is null or begins with linefeed, we know it's part
+                // of another chunk, delete it, another chunk will contain that info
                 if ($this->path_is_selected($path . '/' . $key) ||
                     $this->path_is_selected_parent($path . '/' . $key)) {
-                    unset($data['tags'][$key]);
-                    continue;
+                    if (!isset($value['cdata']) || substr($value['cdata'], 0, 1) === "\n") {
+                        unset($data['tags'][$key]);
+                        continue;
+                    }
                 }
+
                 // Convert to simple name => value array
                 $data['tags'][$key] = isset($value['cdata']) ? $value['cdata'] : null;
 
index 11f3c9c..91bc85f 100644 (file)
@@ -201,7 +201,12 @@ class progressive_parser {
         // Entering a new inner level, publish all the information available
         if ($this->level > $this->prevlevel) {
             if (!empty($this->currtag) && (!empty($this->currtag['attrs']) || !empty($this->currtag['cdata']))) {
-                $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+                // We always add the last not-empty repetition. Empty ones are ignored.
+                if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') {
+                    // Do nothing, the tag already exists and the repetition is empty
+                } else {
+                    $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+                }
             }
             if (!empty($this->topush['tags'])) {
                 $this->publish($this->topush);
@@ -233,7 +238,12 @@ class progressive_parser {
         // Ending rencently started tag, add value to current tag
         if ($this->level == $this->prevlevel) {
             $this->currtag['cdata'] = $this->postprocess_cdata($this->accum);
-            $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+            // We always add the last not-empty repetition. Empty ones are ignored.
+            if (isset($this->topush['tags'][$this->currtag['name']]) && trim($this->currtag['cdata']) === '') {
+                // Do nothing, the tag already exists and the repetition is empty
+            } else {
+                $this->topush['tags'][$this->currtag['name']] = $this->currtag;
+            }
             $this->currtag = array();
         }
 
index f5865c4..4a4fa67 100755 (executable)
       <name>4</name><!-- Only last will be processed. We don't allow repeated final tags in our parser -->
       <value>4</value>
       <value>5</value><!-- Only last will be processed. We don't allow repeated final tags in our parser -->
+      <value>
+
+      </value><!-- If one tag already is set and the repeated is empty, the original value is kept -->
     </othertest>
   </glossary>
 </activity>
index 89f2e14..46f2bba 100644 (file)
@@ -209,7 +209,7 @@ class block_base {
     }
 
     /**
-     * Return a block_contents oject representing the full contents of this block.
+     * Return a block_contents object representing the full contents of this block.
      *
      * This internally calls ->get_content(), and then adds the editing controls etc.
      *
@@ -217,7 +217,7 @@ class block_base {
      * {@link html_attributes()}, {@link formatted_contents()} or {@link get_content()},
      * {@link hide_header()}, {@link (get_edit_controls)}, etc.
      *
-     * @return block_contents a represntation of the block, for rendering.
+     * @return block_contents a representation of the block, for rendering.
      * @since Moodle 2.0.
      */
     public function get_content_for_output($output) {
@@ -243,10 +243,11 @@ class block_base {
 
         if ($this->page->user_is_editing()) {
             $bc->controls = $this->page->blocks->edit_controls($this);
-        }
-
-        if ($this->is_empty() && !$bc->controls) {
-            return null;
+        } else {
+            // we must not use is_empty on hidden blocks
+            if ($this->is_empty() && !$bc->controls) {
+                return null;
+            }
         }
 
         if (empty($CFG->allowuserblockhiding) ||
@@ -277,8 +278,8 @@ class block_base {
      */
     protected function formatted_contents($output) {
         $this->get_content();
+        $this->get_required_javascript();
         if (!empty($this->content->text)) {
-            $this->get_required_javascript();
             return $this->content->text;
         } else {
             return '';
@@ -620,7 +621,7 @@ class block_base {
 
     /**
      * Can be overridden by the block to prevent the block from being dockable.
-     * 
+     *
      * @return bool
      */
     public function instance_can_be_docked() {
@@ -687,8 +688,8 @@ class block_list extends block_base {
 
     protected function formatted_contents($output) {
         $this->get_content();
+        $this->get_required_javascript();
         if (!empty($this->content->items)) {
-            $this->get_required_javascript();
             return $output->list_block_contents($this->content->icons, $this->content->items);
         } else {
             return '';
index 4a6f233..aaa3675 100644 (file)
@@ -49,11 +49,12 @@ class block_rss_client_edit_form extends block_edit_form {
         }
 
         $rssfeeds = $DB->get_records_sql_menu('
-                SELECT id, CASE WHEN preferredtitle = ? THEN title ELSE preferredtitle END AS acutaltitle
+                SELECT id,
+                       CASE WHEN preferredtitle = ? THEN ' . $DB->sql_compare_text('title', 64) .' ELSE preferredtitle END
                 FROM {block_rss_client}
                 WHERE userid = ? OR shared = 1
-                ORDER BY acutaltitle',
-                array($DB->sql_empty(), $USER->id));
+                ORDER BY CASE WHEN preferredtitle = ? THEN ' . $DB->sql_compare_text('title', 64) . ' ELSE preferredtitle END ',
+                array($DB->sql_empty(), $USER->id, $DB->sql_empty()));
         if ($rssfeeds) {
             $select = $mform->addElement('select', 'config_rssid', get_string('choosefeedlabel', 'block_rss_client'), $rssfeeds);
             $select->setMultiple(true);
index 781f4a0..a8bd8f7 100644 (file)
@@ -150,6 +150,8 @@ function blog_sync_external_entries($externalblog) {
     $rssfile = new moodle_simplepie_file($externalblog->url);
     $filetest = new SimplePie_Locator($rssfile);
 
+    $textlib = textlib_get_instance(); // Going to use textlib services
+
     if (!$filetest->is_feed($rssfile)) {
         $externalblog->failedlastsync = 1;
         $DB->update_record('blog_external', $externalblog);
@@ -197,7 +199,13 @@ function blog_sync_external_entries($externalblog) {
         $newentry->uniquehash = $entry->get_permalink();
         $newentry->publishstate = 'site';
         $newentry->format = FORMAT_HTML;
-        $newentry->subject = $entry->get_title();
+        // Clean subject of html, just in case
+        $newentry->subject = clean_param($entry->get_title(), PARAM_TEXT);
+        // Observe 128 max chars in DB
+        // TODO: +1 to raise this to 255
+        if ($textlib->strlen($newentry->subject) > 128) {
+            $newentry->subject = $textlib->substr($newentry->subject, 0, 125) . '...';
+        }
         $newentry->summary = $entry->get_description();
 
         //used to decide whether to insert or update
index 905892d..aa1004f 100644 (file)
@@ -35,11 +35,7 @@ $topic = optional_param('topic', -1, PARAM_INT);
 if ($topic != -1) {
     $displaysection = course_set_display($course->id, $topic);
 } else {
-    if (isset($USER->display[$course->id])) {
-        $displaysection = $USER->display[$course->id];
-    } else {
-        $displaysection = course_set_display($course->id, 0);
-    }
+    $displaysection = course_get_display($course->id);
 }
 
 $context = get_context_instance(CONTEXT_COURSE, $course->id);
index c43def7..68e66a4 100644 (file)
@@ -34,11 +34,7 @@ defined('MOODLE_INTERNAL') || die();
     if ($week != -1) {
         $displaysection = course_set_display($course->id, $week);
     } else {
-        if (isset($USER->display[$course->id])) {
-            $displaysection = $USER->display[$course->id];
-        } else {
-            $displaysection = course_set_display($course->id, 0);
-        }
+        $displaysection = course_get_display($course->id);
     }
 
     $streditsummary  = get_string('editsummary');
index 492275d..aad31ff 100644 (file)
@@ -1239,26 +1239,71 @@ function get_all_sections($courseid) {
     return $coursesections[$courseid];
 }
 
-function course_set_display($courseid, $display=0) {
+/**
+ * Returns the course section to display or 0 meaning show all sections. Returns 0 for guests.
+ * It also sets the $USER->display cache to array($courseid=>return value)
+ *
+ * @param int $courseid The course id
+ * @return int Course section to display, 0 means all
+ */
+function course_get_display($courseid) {
+    global $USER, $DB;
+
+    if (!isloggedin() or isguestuser()) {
+        //do not get settings in db for guests
+        return 0; //return the implicit setting
+    }
+
+    if (!isset($USER->display[$courseid])) {
+        if (!$display = $DB->get_field('course_display', 'display', array('userid' => $USER->id, 'course'=>$courseid))) {
+            $display = 0; // all sections option is not stored in DB, this makes the table much smaller
+        }
+        //use display cache for one course only - we need to keep session small
+        $USER->display = array($courseid => $display);
+    }
+
+    return $USER->display[$courseid];
+}
+
+/**
+ * Show one section only or all sections.
+ *
+ * @param int $courseid The course id
+ * @param mixed $display show only this section, 0 or 'all' means show all sections
+ * @return int Course section to display, 0 means all
+ */
+function course_set_display($courseid, $display) {
     global $USER, $DB;
 
-    if ($display == "all" or empty($display)) {
+    if ($display === 'all' or empty($display)) {
         $display = 0;
     }
 
     if (!isloggedin() or isguestuser()) {
         //do not store settings in db for guests
-    } else if ($DB->record_exists("course_display", array("userid" => $USER->id, "course"=>$courseid))) {
-        $DB->set_field("course_display", "display", $display, array("userid"=>$USER->id, "course"=>$courseid));
+        return 0;
+    }
+
+    if ($display == 0) {
+        //show all, do not store anything in database
+        $DB->delete_records('course_display', array('userid' => $USER->id, 'course' => $courseid));
+
     } else {
-        $record = new stdClass();
-        $record->userid = $USER->id;
-        $record->course = $courseid;
-        $record->display = $display;
-        $DB->insert_record("course_display", $record);
+        if ($DB->record_exists('course_display', array('userid' => $USER->id, 'course' => $courseid))) {
+            $DB->set_field('course_display', 'display', $display, array('userid' => $USER->id, 'course' => $courseid));
+        } else {
+            $record = new stdClass();
+            $record->userid = $USER->id;
+            $record->course = $courseid;
+            $record->display = $display;
+            $DB->insert_record('course_display', $record);
+        }
     }
 
-    return $USER->display[$courseid] = $display;  // Note: = not ==
+    //use display cache for one course only - we need to keep session small
+    $USER->display = array($courseid => $display);
+
+    return $display;
 }
 
 /**
@@ -2837,7 +2882,7 @@ function move_section($course, $section, $move) {
     $DB->set_field("course_sections", "section", $section, array("id"=>$sectiondestrecord->id));
 
     // if the focus is on the section that is being moved, then move the focus along
-    if (isset($USER->display[$course->id]) and ($USER->display[$course->id] == $section)) {
+    if (course_get_display($course->id) == $section) {
         course_set_display($course->id, $sectiondest);
     }
 
@@ -2890,7 +2935,7 @@ function move_section_to($course, $section, $destination) {
     }
 
     // if the focus is on the section that is being moved, then move the focus along
-    if (isset($USER->display[$course->id]) and ($USER->display[$course->id] == $section)) {
+    if (course_get_display($course->id) == $section) {
         course_set_display($course->id, $destination);
     }
     return true;
index 9f3df05..470722d 100644 (file)
@@ -474,7 +474,7 @@ if(!$csv) {
 
                 // Display icon
                 $icon = $OUTPUT->pix_url('icon', $criterion->module);
-                $iconlink = $CFG->wwwroot.'/mod/'.$criterion->module.'/view.php?id='.$activity->id;
+                $iconlink = $CFG->wwwroot.'/mod/'.$criterion->module.'/view.php?id='.$criterion->moduleinstance;
                 $icontitle = $activity->name;
                 $iconalt = get_string('modulename', $criterion->module);
                 break;
index e21aada..28ed124 100644 (file)
                     echo $OUTPUT->notification('An error occurred while moving a section');
                 }
                 // Clear the navigation cache at this point so that the affects
-                // are seen immediatly on the navigation.
+                // are seen immediately on the navigation.
                 $PAGE->navigation->clear_cache();
             }
         }
index 060e733..f2690bf 100644 (file)
@@ -263,16 +263,22 @@ function enrol_category_sync_full() {
 
     // first of all add necessary enrol instances to all courses
     $parentcat = $DB->sql_concat("cat.path", "'/%'");
-    $sql = "SELECT DISTINCT c.*
+    // need whole course records to be used by add_instance(), use inner view (ci) to
+    // get distinct records only.
+    // TODO: Moodle 2.1. Improve enrol API to accept courseid / courserec
+    $sql = "SELECT c.*
               FROM {course} c
-              JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)
-              JOIN (SELECT DISTINCT cctx.path
-                      FROM {course_categories} cc
-                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
-                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
-                   ) cat ON (ctx.path LIKE $parentcat)
-         LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
-             WHERE e.id IS NULL";
+              JOIN (
+                SELECT DISTINCT c.id
+                  FROM {course} c
+                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)
+                  JOIN (SELECT DISTINCT cctx.path
+                          FROM {course_categories} cc
+                          JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
+                          JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
+                       ) cat ON (ctx.path LIKE $parentcat)
+             LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
+                 WHERE e.id IS NULL) ci ON (c.id = ci.id)";
 
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach($rs as $course) {
index 20b7972..5235c63 100644 (file)
@@ -632,12 +632,14 @@ class course_enrolment_users_table extends course_enrolment_table {
 
         $instances  = $this->manager->get_enrolment_instances();
         $plugins    = $this->manager->get_enrolment_plugins();
+        $manuals    = array();
         // print enrol link or selection
         $links = array();
         foreach($instances as $instance) {
             $plugin = $plugins[$instance->enrol];
             if ($link = $plugin->get_manual_enrol_link($instance)) {
                 $links[$instance->id] = $link;
+                $manuals[$instance->id] = $instance;
             }
         }
         if (!empty($links)) {
@@ -671,6 +673,7 @@ class course_enrolment_users_table extends course_enrolment_table {
             $startdateoptions[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
 
             if ($count == 1) {
+                $instance = reset($manuals);
                 $page->requires->strings_for_js(array(
                     'ajaxoneuserfound',
                     'ajaxxusersfound',
@@ -697,7 +700,7 @@ class course_enrolment_users_table extends course_enrolment_table {
                     'ajaxurl'=>'/enrol/ajax.php',
                     'url'=>$url->out(false),
                     'optionsStartDate'=>$startdateoptions,
-                    'defaultRole'=>get_config('enrol_manual', 'roleid'));
+                    'defaultRole'=>$instance->roleid);
                 $page->requires->yui_module($modules, $function, array($arguments));
             }
             return $control;
index 6a9ec85..fb3801c 100644 (file)
@@ -46,96 +46,101 @@ class filter_mediaplugin extends moodle_text_filter {
             // non string data can not be filtered anyway
             return $text;
         }
+        if (stripos($text, '</a>') === false) {
+            // performance shortcut - all regexes bellow end with the </a> tag,
+            // if not present nothing can match
+            return $text;
+        }
         $newtext = $text; // fullclone is slow and not needed here
 
         if (!empty($CFG->filter_mediaplugin_enable_mp3)) {
-            $search =   '/<a[^>]*?href="([^<]+\.mp3)"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mp3)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_mp3_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_ogg)) {
-            $search =   '/<a[^>]*?href="([^<]+\.ogg)"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.ogg)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_ogg_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_ogv)) {
-            $search =   '/<a[^>]*?href="([^<]+\.ogv)"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.ogv)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_ogv_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_swf)) {
-            $search = '/<a[^>]*?href="([^<]+\.swf)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.swf)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_swf_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_flv)) {
-            $search = '/<a[^>]*?href="([^<]+\.flv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.flv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_flv_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_mov)) {
-            $search = '/<a[^>]*?href="([^<]+\.mov)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mov)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
 
-            $search = '/<a[^>]*?href="([^<]+\.mp4)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mp4)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
 
-            $search = '/<a[^>]*?href="([^<]+\.m4v)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.m4v)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
 
-            $search = '/<a[^>]*?href="([^<]+\.m4a)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.m4a)(\?d=([\d]{1,4}%?)x([\d]{1,4}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_wmv)) {
-            $search = '/<a[^>]*?href="([^<]+\.wmv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.wmv)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_mpg)) {
-            $search = '/<a[^>]*?href="([^<]+\.mpe?g)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.mpe?g)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_qt_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_avi)) {
-            $search = '/<a[^>]*?href="([^<]+\.avi)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.avi)(\?d=([\d]{1,3}%?)x([\d]{1,3}%?))?"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_wmp_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_ram)) {
-            $search = '/<a[^>]*?href="([^<]+\.ram)"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.ram)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_rpm)) {
-            $search = '/<a[^>]*?href="([^<]+\.rpm)"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.rpm)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_rm)) {
-            $search = '/<a[^>]*?href="([^<]+\.rm)"[^>]*>.*?<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.rm)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_real_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_youtube)) {
-            //see MDL-23903 for description of recent changes to this regex
-            //$search = '/<a.*?href="([^<]*)youtube.com\/watch\?v=([^"]*)"[^>]*>(.*?)<\/a>/is';
-            $search = '/<a[^>]*href="([^<]*?)youtube.com\/watch\?v=([^"]*)"[^>]*>(.*?)<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="(([^"]+youtube\.com)\/watch\?v=([A-Za-z0-9\-_]+))[^>]*>(.*?)<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
 
-            $search = '/<a[^>]*href="([^<]*)youtube.com\/v\/([^"]*)"[^>]*>(.*?)<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="(([^"]+youtube\.com)\/v\/([A-Za-z0-9\-_]*))[^>]+>(.*?)<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_callback', $newtext);
 
-            $search = '/<a(\s+[^>]+?)?\s+href="((([^"]+)youtube\.com)\/view_play_list\?p=([^"]*))"[^>]*>(.*?)<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="((([^"]+)youtube\.com)\/view_play_list\?p=([A-Za-z0-9\-_]+))[^>]*>(.*?)<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_youtube_playlist_callback', $newtext);
         }
 
         if (!empty($CFG->filter_mediaplugin_enable_img)) {
-            $search = '/<a[^>]*?href="([^<]+\.jpg)"[^>]*>(.*?)<\/a>/is';
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.jpg)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
-            $search = '/<a[^>]*?href="([^<]+\.png)"[^>]*>(.*?)<\/a>/is';
+
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.png)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
-            $search = '/<a[^>]*?href="([^<]+\.gif)"[^>]*>(.*?)<\/a>/is';
+
+            $search = '/<a(\s+[^>]+?)?\s+href="([^"]+\.gif)"[^>]*>.*?<\/a>/is';
             $newtext = preg_replace_callback($search, 'filter_mediaplugin_img_callback', $newtext);
         }
 
@@ -166,7 +171,7 @@ function filter_mediaplugin_mp3_callback($link) {
     $count++;
     $id = 'filter_mp3_'.time().$count; //we need something unique because it might be stored in text cache
 
-    $url = addslashes_js($link[1]);
+    $url = addslashes_js($link[2]);
 
     $playerpath = $CFG->wwwroot.'/filter/mediaplugin/mp3player.swf';
     $audioplayerpath = $CFG->wwwroot .'/filter/mediaplugin/flowplayer.audio.swf';
@@ -215,7 +220,7 @@ function filter_mediaplugin_ogg_callback($link) {
     $count++;
     $id = 'filter_ogg_'.time().$count; //we need something unique because it might be stored in text cache
 
-    $url = addslashes_js($link[1]);
+    $url = $link[2];
     $printlink = html_writer::link($url, get_string('oggaudio', 'filter_mediaplugin'));
     $unsupportedplugins = get_string('unsupportedplugins', 'filter_mediaplugin', $printlink);
     $output = <<<OET
@@ -234,7 +239,7 @@ function filter_mediaplugin_ogv_callback($link) {
     $count++;
     $id = 'filter_ogv_'.time().$count; //we need something unique because it might be stored in text cache
 
-    $url = addslashes_js($link[1]);
+    $url = $link[2];
     $printlink = html_writer::link($url, get_string('ogvvideo', 'filter_mediaplugin'));
     $unsupportedplugins = get_string('unsupportedplugins', 'filter_mediaplugin', $printlink);
     $output = <<<OET
@@ -252,9 +257,9 @@ function filter_mediaplugin_swf_callback($link) {
     $count++;
     $id = 'filter_swf_'.time().$count; //we need something unique because it might be stored in text cache
 
-    $width  = empty($link[3]) ? '400' : $link[3];
-    $height = empty($link[4]) ? '300' : $link[4];
-    $url = addslashes_js($link[1]);
+    $width  = empty($link[4]) ? '400' : $link[4];
+    $height = empty($link[5]) ? '300' : $link[5];
+    $url = $link[2];
 
     $args = Array();
     $args['movie'] = $url;
@@ -279,16 +284,17 @@ function filter_mediaplugin_flv_callback($link) {
     $count++;
     $id = 'filter_flv_'.time().$count; //we need something unique because it might be stored in text cache
 
-    $width  = empty($link[3]) ? '480' : $link[3];
-    $height = empty($link[4]) ? '360' : $link[4];
-    $url = addslashes_js($link[1]);
+    // note: in 1.9.x this used to be 480x360
+    $width  = empty($link[4]) ? '800' : $link[4];
+    $height = empty($link[5]) ? '600' : $link[5];
+    $url = addslashes_js($link[2]);
 
     $playerpath = $CFG->wwwroot.'/filter/mediaplugin/flvplayer.swf';
 
     $output = <<<EOT
     <span class="mediaplugin mediaplugin_flv" id="$id"></span>
     <noscript><div>
-    <object width="800" height="600" id="undefined" name="undefined" data="$playerpath" type="application/x-shockwave-flash">
+    <object width="$width" height="$height" id="undefined" name="undefined" data="$playerpath" type="application/x-shockwave-flash">
     <param name="movie" value="$playerpath" />
     <param name="allowfullscreen" value="true" />
     <param name="allowscriptaccess" value="always" />
@@ -305,7 +311,8 @@ EOT;
 }
 
 function filter_mediaplugin_real_callback($link, $autostart=false) {
-    $url = addslashes_js($link[1]);
+    $url = $link[2];
+    $jsurl = addslashes_js($link[2]);
     $mimetype = mimeinfo('type', $url);
     $autostart = $autostart ? 'true' : 'false';
 
@@ -315,19 +322,19 @@ function filter_mediaplugin_real_callback($link, $autostart=false) {
 <script type="text/javascript">
 //<![CDATA[
 document.write(\'<object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="240" height="180">\\
-  <param name="src" value="'.$url.'" />\\
+  <param name="src" value="'.$jsurl.'" />\\
   <param name="autostart" value="'.$autostart.'" />\\
   <param name="controls" value="imagewindow" />\\
   <param name="console" value="video" />\\
   <param name="loop" value="true" />\\
-  <embed src="'.$url.'" width="240" height="180" loop="true" type="'.$mimetype.'" controls="imagewindow" console="video" autostart="'.$autostart.'" />\\
+  <embed src="'.$jsurl.'" width="240" height="180" loop="true" type="'.$mimetype.'" controls="imagewindow" console="video" autostart="'.$autostart.'" />\\
   </object><br />\\
   <object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" width="240" height="30">\\
-  <param name="src" value="'.$url.'" />\\
+  <param name="src" value="'.$jsurl.'" />\\
   <param name="autostart" value="'.$autostart.'" />\\
   <param name="controls" value="ControlPanel" />\\
   <param name="console" value="video" />\\
-  <embed src="'.$url.'" width="240" height="30" controls="ControlPanel" type="'.$mimetype.'" console="video" autostart="'.$autostart.'" />\\
+  <embed src="'.$jsurl.'" width="240" height="30" controls="ControlPanel" type="'.$mimetype.'" console="video" autostart="'.$autostart.'" />\\
   </object>\');
 //]]>
 </script></span>';
@@ -337,15 +344,15 @@ document.write(\'<object classid="clsid:CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA" wi
  * Change links to Youtube into embedded Youtube videos
  */
 function filter_mediaplugin_youtube_callback($link, $autostart=false) {
+    $site = s($link[3]);
+    $param = $link[4]; // video id
+    $info = s(strip_tags($link[5]));
 
-    $site = addslashes_js($link[1]);
-    $url = addslashes_js($link[2]);
-    $info = addslashes_js(strip_tags($link[3]));//strip out html tags as they won't work in the title attribute
 
     return '<object title="'.$info.'"
                     class="mediaplugin mediaplugin_youtube" type="application/x-shockwave-flash"
-                    data="'.$site.'youtube.com/v/'.$url.'&amp;fs=1&amp;rel=0" width="425" height="344">'.
-           '<param name="movie" value="'.$site.'youtube.com/v/'.$url.'&amp;fs=1&amp;rel=0" />'.
+                    data="'.$site.'/v/'.$param.'&amp;fs=1&amp;rel=0" width="400" height="320">'.
+           '<param name="movie" value="'.$site.'/v/'.$param.'&amp;fs=1&amp;rel=0" />'.
            '<param name="FlashVars" value="playerMode=embedded" />'.
            '<param name="wmode" value="transparent" />'.
            '<param name="allowFullScreen" value="true" />'.
@@ -358,8 +365,8 @@ function filter_mediaplugin_youtube_callback($link, $autostart=false) {
 function filter_mediaplugin_youtube_playlist_callback($link, $autostart=false) {
 
     $site = s($link[4]);
-    $param = s($link[5]);
-    $info = s($link[6]);
+    $param = $link[5]; // playlist id
+    $info = s(strip_tags($link[6]));
 
     return '<object title="'.$info.'"
                     class="mediaplugin mediaplugin_youtube" type="application/x-shockwave-flash"
@@ -375,9 +382,8 @@ function filter_mediaplugin_youtube_playlist_callback($link, $autostart=false) {
  * Change links to images into embedded images
  */
 function filter_mediaplugin_img_callback($link, $autostart=false) {
-    $url = addslashes_js($link[1]);
-    $info = addslashes_js($link[2]);
-
+    $url = $link[2];
+    $info = s(strip_tags($link[2]));
     return '<img class="mediaplugin mediaplugin_img" alt="" title="'.$info.'" src="'.$url.'" />';
 }
 
@@ -385,13 +391,13 @@ function filter_mediaplugin_img_callback($link, $autostart=false) {
  * Embed video using window media player if available
  */
 function filter_mediaplugin_wmp_callback($link, $autostart=false) {
-    $url = $link[1];
-    if (empty($link[3]) or empty($link[4])) {
+    $url = $link[2];
+    if (empty($link[4]) or empty($link[5])) {
         $mpsize = '';
         $size = 'width="300" height="260"';
         $autosize = 'true';
     } else {
-        $size = 'width="'.$link[3].'" height="'.$link[4].'"';
+        $size = 'width="'.$link[4].'" height="'.$link[5].'"';
         $mpsize = $size;
         $autosize = 'false';
     }
@@ -428,11 +434,11 @@ function filter_mediaplugin_wmp_callback($link, $autostart=false) {
 }
 
 function filter_mediaplugin_qt_callback($link, $autostart=false) {
-    $url = $link[1];
-    if (empty($link[3]) or empty($link[4])) {
+    $url = $link[2];
+    if (empty($link[4]) or empty($link[5])) {
         $size = 'width="440" height="315"';
     } else {
-        $size = 'width="'.$link[3].'" height="'.$link[4].'"';
+        $size = 'width="'.$link[4].'" height="'.$link[5].'"';
     }
     $mimetype = mimeinfo('type', $url);
     $autostart = $autostart ? 'true' : 'false';
diff --git a/filter/mediaplugin/simpletest/testfiltermediaplugin.php b/filter/mediaplugin/simpletest/testfiltermediaplugin.php
new file mode 100644 (file)
index 0000000..362206b
--- /dev/null
@@ -0,0 +1,124 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Unit test for the filter_mediaplugin
+ *
+ * @package    filter
+ * @subpackage Mediaplugin
+ * @copyright  2011 Rossiani Wijaya <rwijaya@moodle.com>
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+require_once($CFG->dirroot . '/filter/mediaplugin/filter.php'); // Include the code to test
+
+/**
+ * Test cases for filter_mediaplugin class
+ */
+class filter_mediaplugin_test extends UnitTestCase {
+
+    function test_filter_mediaplugin_link() {
+        global $CFG;
+
+        // we need to enable the plugins somehow
+        $oldcfg = clone($CFG); // very, very ugly hack
+        $CFG->filter_mediaplugin_enable_mp3     = 1;
+        $CFG->filter_mediaplugin_enable_ogg     = 1;
+        $CFG->filter_mediaplugin_enable_ogv     = 1;
+        $CFG->filter_mediaplugin_enable_swf     = 1;
+        $CFG->filter_mediaplugin_enable_flv     = 1;
+        $CFG->filter_mediaplugin_enable_mov     = 1;
+        $CFG->filter_mediaplugin_enable_wmv     = 1;
+        $CFG->filter_mediaplugin_enable_mpg     = 1;
+        $CFG->filter_mediaplugin_enable_avi     = 1;
+        $CFG->filter_mediaplugin_enable_ram     = 1;
+        $CFG->filter_mediaplugin_enable_rpm     = 1;
+        $CFG->filter_mediaplugin_enable_rm      = 1;
+        $CFG->filter_mediaplugin_enable_youtube = 1;
+        $CFG->filter_mediaplugin_enable_img     = 1;
+
+
+        $filterplugin = new filter_mediaplugin(null, array());
+
+        $validtexts = array (
+                        '<a href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+                        '<a href="http://moodle.org/testfile/test.ogg">test ogg</a>',
+                        '<a id="movie player" class="center" href="http://moodle.org/testfile/test.mpg">test mpg</a>',
+                        '<a href="http://moodle.org/testfile/test.ram">test</a>',
+                        '<a href="http://www.youtube.com/watch?v=JghQgA2HMX8" class="href=css">test file</a>',
+                        '<a class="youtube" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
+                        '<a class="_blanktarget" href="http://moodle.org/testfile/test.flv?d=100x100">test flv</a>',
+                        '<a class="hrefcss" href="http://www.youtube.com/watch?v=JghQgA2HMX8">test file</a>',
+                        '<a href="http://www.moodle.org/logo.jpg"><img src="http://moodle.org/test.jpg" /></a>',
+                        '<a href="http://www.moodle.org/logo.rpm">rpm file</a>',
+                        '<a  class="content"     href="http://moodle.org/testfile/test.avi">test mp3</a>',
+                        '<a     id="audio"      href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+                        '<a  href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+                        '<a     href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+                        '<a     href="http://www.youtube.com/watch?v=JghQgA2HMX8?d=200x200">youtube\'s</a>',
+                        '<a
+                            href="http://moodle.org/testfile/test.mp3">
+                            test mp3</a>',
+                        '<a                         class="content"
+
+
+                            href="http://moodle.org/testfile/test.avi">test mp3
+                                    </a>',
+                        '<a             href="http://www.youtube.com/watch?v=JghQgA2HMX8?d=200x200"     >youtube\'s</a>'
+                    );
+
+        //test for valid link
+        foreach ($validtexts as $text) {
+            $msg = "Testing text: ". $text;
+            $filter = $filterplugin->filter($text);
+            $this->assertNotEqual($text, $filter, $msg);
+        }
+
+        $invalidtexts = array(
+                            '<a class="_blanktarget">href="http://moodle.org/testfile/test.mp3"</a>',
+                            '<a>test test</a>',
+                            '<a >test test</a>',
+                            '<a     >test test</a>',
+                            '<a >test test</a>',
+                            '<ahref="http://moodle.org/testfile/test.mp3">sample</a>',
+                            '<a href="" test></a>',
+                            '<a class="_blanktarget" href="http://moodle.org/testfile/test.flv?d=100x">test flv</a>',
+                            '<a href="http://www.moodle.com/path/to?#param=29">test</a>',
+                            '<a href="http://moodle.org/testfile/test.mp3">test mp3',
+                            '<a href="http://moodle.org/testfile/test.mp3"test</a>',
+                            '<a href="http://moodle.org/testfile/">test</a>',
+                            '<a href="http://www.moodle.org/path/to/test.flv#param2=1?d=100x100">test</a>',
+                            '<href="http://moodle.org/testfile/test.avi">test</a>',
+                            '<a ahref="http://moodle.org/testfile/test.mp3">test mp3</a>',
+                            '<abbr href="http://moodle.org/testfile/test.mp3">test mp3</abbr>',
+                            '<ahref="http://moodle.org/testfile/test.mp3">test mp3</a>',
+                            '<a class="content"href="http://moodle.org/testfile/test.mp3">test mp3</a>',
+                            '<aclass="content" href="http://moodle.org/testfile/test.mp3">test mp3</a>'
+                        );
+
+        //test for invalid link
+        foreach ($invalidtexts as $text) {
+            $msg = "Testing text: ". $text;
+            $filter = $filterplugin->filter($text);
+            $this->assertEqual($text, $filter, $msg);
+        }
+
+        $CFG = $oldcfg; // very, very ugly hack
+    }
+}
index d4f2699..4a4896a 100644 (file)
@@ -415,7 +415,7 @@ if ($config->stage == INSTALL_DATABASE) {
 
     $disabled = empty($distro->dbhost) ? '' : 'disabled="disabled';
     echo '<div class="formrow"><label for="id_dbhost" class="formlabel">'.$strdbhost.'</label>';
-    echo '<input id="id_dbhost" name="dbhost" '.$disabled.' type="text" value="'.s($config->dbhost).'" size="30" class="forminput" />';
+    echo '<input id="id_dbhost" name="dbhost" '.$disabled.' type="text" value="'.s($config->dbhost).'" size="50" class="forminput" />';
     echo '</div>';
 
     echo '<div class="formrow"><label for="id_dbname" class="formlabel">'.$strdbname.'</label>';
@@ -552,15 +552,15 @@ if ($config->stage == INSTALL_PATHS) {
 
     echo '<div class="userinput">';
     echo '<div class="formrow"><label for="id_wwwroot" class="formlabel">'.$paths['wwwroot'].'</label>';
-    echo '<input id="id_wwwroot" name="wwwroot" type="text" value="'.s($CFG->wwwroot).'" disabled="disabled" size="45" class="forminput" />';
+    echo '<input id="id_wwwroot" name="wwwroot" type="text" value="'.s($CFG->wwwroot).'" disabled="disabled" size="70" class="forminput" />';
     echo '</div>';
 
     echo '<div class="formrow"><label for="id_dirroot" class="formlabel">'.$paths['dirroot'].'</label>';
-    echo '<input id="id_dirroot" name="dirroot" type="text" value="'.s($CFG->dirroot).'" disabled="disabled" size="45"class="forminput" />';
+    echo '<input id="id_dirroot" name="dirroot" type="text" value="'.s($CFG->dirroot).'" disabled="disabled" size="70"class="forminput" />';
     echo '</div>';
 
     echo '<div class="formrow"><label for="id_dataroot" class="formlabel">'.$paths['dataroot'].'</label>';
-    echo '<input id="id_dataroot" name="dataroot" type="text" value="'.s($config->dataroot).'" size="45" class="forminput" />';
+    echo '<input id="id_dataroot" name="dataroot" type="text" value="'.s($config->dataroot).'" size="70" class="forminput" />';
     if ($hint_dataroot !== '') {
         echo '<div class="hint">'.$hint_dataroot.'</div>';
     }
index beedc30..f06fce6 100644 (file)
@@ -62,8 +62,8 @@ $string['pathsroparentdataroot'] = 'Das Verzeichnis ({$a->parent}) ist schreibge
 $string['pathssubadmindir'] = 'Einige Webserver benutzen /admin als speziellen Link, um auf Einstellungsseiten oder Ähnliches zu verweisen. Unglücklicherweise kollidiert dies mit dem standardmäßigen Verzeichnis für die Moodle-Administration. Sie können dieses Problem beheben, indem Sie das Verzeichnis admin in Ihrer Moodle-Installation umbenennen und den neuen Namen hier eingeben (z.B. <em>moodleadmin</em>). Mit dieser Änderung werden alle Admin-Links korrigiert.';
 $string['pathssubdataroot'] = 'Sie benötigen einen Platz, wo Moodle hochgeladene Dateien abspeichern kann. Dieses Verzeichnis muss Lese- und Schreibrechte für das Nutzerkonto besitzen, mit dem Ihr Webservers läuft (üblicherweise \'nobody\', \'apache\' oder \'www\'). Außerdem sollte das Verzeichnis nicht direkt aus dem Internet erreichbar sein. Das Intallationsskript wird versuchen, ein solches Verzeichnis zu erstellen, falls es nicht existiert.</p>';
 $string['pathssubdirroot'] = 'Vollständiger Pfad der Moodle-Installation';
-$string['pathssubwwwroot'] = 'Vollständige Webadresse für den Zugriff auf Moodle. Es ist nicht möglich, auf Moodle über unterschiedliche Adressen zuzugreifen. Sollten Sie für Ihre Website mehrere öffentliche Adressen verwenden, so müssen Sie eine Adresse auswählen und für die übrigen Adressen dauerhafte Weiterleitungen einrichten. 
-<p>Falls Ihre Website gleichzeitig im Intranet und im Internet erreichbar ist, so tragen Sie hier die öffentliche Adresse ein. Konfigurieren Sie den DNS-Eintrag des Servers so, dass für alle Intranet-Nutzer ebenfalls die öffentliche Adresse erreichbar ist.
+$string['pathssubwwwroot'] = 'Vollständige Webadresse für den Zugriff auf Moodle. Es ist nicht möglich, über unterschiedliche Adressen auf Moodle zuzugreifen. Sollte Ihre Website mehrere öffentliche Adressen verwenden, so müssen Sie eine Adresse festlegen und für die übrigen Adressen dauerhafte Weiterleitungen dorthin einrichten. 
+<p>Falls Ihre Website gleichzeitig im Intranet und im Internet erreichbar ist, so tragen Sie die öffentliche Adresse ein. Konfigurieren Sie den DNS so, dass Moodle auch aus dem Intranet über die öffentliche Adresse erreichbar ist.
 <p>Führen Sie Ihre Moodle-Installation unbedingt mit der richtigen Adresse durch, weil es andernfalls zu Problemen kommen könnte.';
 $string['pathsunsecuredataroot'] = 'Der Speicherort des Verzeichnisses \'dataroot\' ist unsicher';
 $string['pathswrongadmindir'] = 'Das Admin-Verzeichnis existiert nicht';
index 87532ab..2fb9431 100644 (file)
@@ -41,6 +41,8 @@ $string['dataroot'] = 'Diretório de Dados';
 $string['dbprefix'] = 'Prefixo das tabelas';
 $string['dirroot'] = 'Diretório Moodle';
 $string['environmenthead'] = 'Verificando o ambiente ...';
+$string['environmentsub2'] = 'Cada release do Moodle requer uma versão mínima do PHP e diversas extensões do PHP.
+A verificação completa do ambiente é feita antes de cada instalação e atualização.';
 $string['errorsinenvironment'] = 'Verificação do Ambiente falhou!';
 $string['installation'] = 'Instalação';
 $string['langdownloaderror'] = 'Infelizmente o idioma "{$a}" não foi instalado. A instalação vai continuar em Inglês.';
index ce096ce..02fde93 100644 (file)
@@ -27,11 +27,11 @@ $string['accessdenied'] = 'Access denied';
 $string['accounts'] = 'Accounts';
 $string['additionalhtml'] = 'Additional HTML';
 $string['additionalhtml_heading'] = 'Additional HTML to be added to every page.';
-$string['additionalhtml_desc'] = 'These settings allow you to specify HTML that you want added to every page. You can set HTML that will be added within the HEAD tag for the page, immediatly after the BODY tag has been opened, or immediatly before the body tag is closed.<br />Doing this allows you add custom headers or footers on every page, or add support for services like Google Analytics very easily and independent of your chosen theme.';
+$string['additionalhtml_desc'] = 'These settings allow you to specify HTML that you want added to every page. You can set HTML that will be added within the HEAD tag for the page, immediately after the BODY tag has been opened, or immediately before the body tag is closed.<br />Doing this allows you add custom headers or footers on every page, or add support for services like Google Analytics very easily and independent of your chosen theme.';
 $string['additionalhtmlhead'] = 'Within HEAD';
 $string['additionalhtmlhead_desc'] = 'Content here will be added to the bottom of the HEAD tag for every page.';
 $string['additionalhtmltopofbody'] = 'When BODY is opened';
-$string['additionalhtmltopofbody_desc'] = 'Content here will be added in to every page immediatly after the opening body tag.';
+$string['additionalhtmltopofbody_desc'] = 'Content here will be added in to every page immediately after the opening body tag.';
 $string['additionalhtmlfooter'] = 'Before BODY is closed';
 $string['additionalhtmlfooter_desc'] = 'Content here will be added in to every page right before the body tag is closed.';
 $string['adminseesall'] = 'Admins see all';
@@ -429,6 +429,8 @@ $string['debugsmtp'] = 'Debug email sending';
 $string['debugstringids'] = 'Show origin of languages strings';
 $string['debugvalidators'] = 'Show validator links';
 $string['defaultallowedmodules'] = 'Default allowed modules';
+$string['defaultcity'] = 'Default city';
+$string['defaultcity_help'] = 'A city entered here will be the default city when creating new user accounts.';
 $string['defaulthomepage'] = 'Default home page for users';
 $string['defaultrequestcategory'] = 'Default category for course requests';
 $string['defaultsettinginfo'] = 'Default: {$a}';
index 016a815..42abfad 100644 (file)
@@ -142,6 +142,7 @@ $string['participantnumberaverage'] = 'Average number of participants ({$a})';
 $string['postaladdress'] = 'Postal address';
 $string['postaladdress_help'] = 'Postal address of this site, or of the entity represented by this site.';
 $string['postsnumber'] = 'Number of posts ({$a})';
+$string['previousregistrationdeleted'] = 'The previous registration has been deleted from {$a}. You can restart the registration process. Thank you.';
 $string['prioritise'] = 'Prioritise';
 $string['privacy'] = 'Privacy';
 $string['privacy_help'] = 'The hub may want to display a list of registered sites. If it does then you can choose whether or not you want to appear on that list.';
@@ -174,7 +175,9 @@ $string['registrationconfirmedon'] = 'You are now registered on the hub {$a}. Yo
 $string['registrationupdated'] = 'Registration has been updated.';
 $string['registrationupdatedfailed'] = 'Registration update failed.';
 $string['removefromhub'] = 'Remove from hub';
+$string['renewregistration'] = 'Renew registration';
 $string['resourcesnumber'] = 'Number of resources ({$a})';
+$string['restartregistration'] = 'Restart registration';
 $string['roleassignmentsnumber'] = 'Number of role assignments ({$a})';
 $string['screenshots'] = 'Screenshots';
 $string['screenshots_help'] = 'Any screenshots of the course will be displayed in search results.';
@@ -206,7 +209,7 @@ $string['sitedesc_help'] = 'This description of your site may be shown in the si
 $string['sitegeolocation'] = 'Geolocation';
 $string['sitegeolocation_help'] = 'In future we may provide location-based searching in the hubs. If you want to specify the location for your site use a latitude/longitude value here (eg: -31.947884,115.871285).  One way to find this is to use Google Maps.';
 $string['siteemail'] = 'Email address';
-$string['siteemail_help'] = 'You need to provide an email address so the admin can contact you if necessary.  This will not be used for any other purpose.';
+$string['siteemail_help'] = 'You need to provide an email address so the hub administrator can contact you if necessary.  This will not be used for any other purpose. It is recommended to enter a email address related to a position (example: sitemanager@example.com) and not directly to a person.';
 $string['sitelang'] = 'Language';
 $string['sitelang_help'] = 'Your site language will be displayed on the site listing.';
 $string['sitename'] = 'Name';
index e36c126..f59fe2e 100755 (executable)
@@ -2352,6 +2352,10 @@ function get_context_info_array($contextid) {
  * @return int|bool related course id or false
  */
 function get_courseid_from_context($context) {
+    if (empty($context->contextlevel)) {
+        debugging('Invalid context object specified in get_courseid_from_context() call');
+        return false;
+    }
     if ($context->contextlevel == CONTEXT_COURSE) {
         return $context->instanceid;
     }
@@ -2370,7 +2374,7 @@ function get_courseid_from_context($context) {
     if ($context->contextlevel == CONTEXT_BLOCK) {
         $parentcontexts = get_parent_contexts($context, false);
         $parent = reset($parentcontexts);
-        return get_courseid_from_context($parent);
+        return get_courseid_from_context(get_context_instance_by_id($parent));
     }
 
     return false;
index f6edb2c..8e38044 100644 (file)
@@ -739,7 +739,7 @@ interface parentable_part_of_admin_tree extends part_of_admin_tree {
  */
 class admin_category implements parentable_part_of_admin_tree {
 
-/** @var mixed An array of part_of_admin_tree objects that are this object's children */
+    /** @var mixed An array of part_of_admin_tree objects that are this object's children */
     public $children;
     /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
     public $name;
@@ -752,6 +752,9 @@ class admin_category implements parentable_part_of_admin_tree {
     /** @var mixed Either a string or an array or strings */
     public $visiblepath;
 
+    /** @var array fast lookup category cache, all categories of one tree point to one cache */
+    protected $category_cache;
+
     /**
      * Constructor for an empty admin category
      *
@@ -775,6 +778,11 @@ class admin_category implements parentable_part_of_admin_tree {
      *                  defaults to false
      */
     public function locate($name, $findpath=false) {
+        if (is_array($this->category_cache) and !isset($this->category_cache[$this->name])) {
+            // somebody much have purged the cache
+            $this->category_cache[$this->name] = $this;
+        }
+
         if ($this->name == $name) {
             if ($findpath) {
                 $this->visiblepath[] = $this->visiblename;
@@ -783,6 +791,11 @@ class admin_category implements parentable_part_of_admin_tree {
             return $this;
         }
 
+        // quick category lookup
+        if (!$findpath and is_array($this->category_cache) and isset($this->category_cache[$name])) {
+            return $this->category_cache[$name];
+        }
+
         $return = NULL;
         foreach($this->children as $childid=>$unused) {
             if ($return = $this->children[$childid]->locate($name, $findpath)) {
@@ -831,11 +844,16 @@ class admin_category implements parentable_part_of_admin_tree {
 
         foreach($this->children as $precedence => $child) {
             if ($child->name == $name) {
-            // found it!
+                // clear cache and delete self
+                if (is_array($this->category_cache)) {
+                    while($this->category_cache) {
+                        // delete the cache, but keep the original array address
+                        array_pop($this->category_cache);
+                    }
+                }
                 unset($this->children[$precedence]);
                 return true;
-            }
-            if ($this->children[$precedence]->prune($name)) {
+            } else if ($this->children[$precedence]->prune($name)) {
                 return true;
             }
         }
@@ -862,6 +880,25 @@ class admin_category implements parentable_part_of_admin_tree {
                 return false;
             }
             $parent->children[] = $something;
+            if (is_array($this->category_cache) and ($something instanceof admin_category)) {
+                if (isset($this->category_cache[$something->name])) {
+                    debugging('Duplicate admin catefory name: '.$something->name);
+                } else {
+                    $this->category_cache[$something->name] = $something;
+                    $something->category_cache =& $this->category_cache;
+                    foreach ($something->children as $child) {
+                        // just in case somebody already added subcategories
+                        if ($child instanceof admin_category) {
+                            if (isset($this->category_cache[$child->name])) {
+                                debugging('Duplicate admin catefory name: '.$child->name);
+                            } else {
+                                $this->category_cache[$child->name] = $child;
+                                $child->category_cache =& $this->category_cache;
+                            }
+                        }
+                    }
+                }
+            }
             return true;
 
         } else {
@@ -938,6 +975,8 @@ class admin_root extends admin_category {
         $this->fulltree = $fulltree;
         $this->loaded   = false;
 
+        $this->category_cache = array();
+
         // load custom defaults if found
         $this->custom_defaults = null;
         $defaultsfile = "$CFG->dirroot/local/defaults.php";
@@ -959,6 +998,11 @@ class admin_root extends admin_category {
         $this->children = array();
         $this->fulltree = ($requirefulltree || $this->fulltree);
         $this->loaded   = false;
+        //break circular dependencies - this helps PHP 5.2
+        while($this->category_cache) {
+            array_pop($this->category_cache);
+        }
+        $this->category_cache = array();
     }
 }
 
@@ -5437,7 +5481,7 @@ class admin_page_managefilters extends admin_externalpage {
  * This function must be called on each admin page before other code.
  *
  * @global moodle_page $PAGE
- * 
+ *
  * @param string $section name of page
  * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
  * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
@@ -5499,7 +5543,7 @@ function admin_externalpage_setup($section, $extrabutton = '', array $extraurlpa
         $PAGE->set_cacheable(false);
         return;
     }
-    
+
     // Locate the current item on the navigation and make it active when found.
     $path = $extpage->path;
     $node = $PAGE->settingsnav;
index e7676bb..27b3c1e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20110206" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20110209" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
     <TABLE NAME="registration_hubs" COMMENT="hub where the site is registered on with their associated token" PREVIOUS="license" NEXT="backup_controllers">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="token"/>
-        <FIELD NAME="token" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="hubname"/>
+        <FIELD NAME="token" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="the token to communicate with the hub by web service" PREVIOUS="id" NEXT="hubname"/>
         <FIELD NAME="hubname" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="token" NEXT="huburl"/>
         <FIELD NAME="huburl" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="hubname" NEXT="confirmed"/>
-        <FIELD NAME="confirmed" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="huburl"/>
+        <FIELD NAME="confirmed" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="huburl" NEXT="secret"/>
+        <FIELD NAME="secret" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="the unique site identifier for this hub" PREVIOUS="confirmed"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index 0e1aad1..4fe7651 100644 (file)
@@ -4091,10 +4091,12 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
     }
 
     if ($oldversion < 2010061900.10) {
-        // migrate existing setup of meta courses
+        // migrate existing setup of meta courses, ignore records referencing invalid courses
         $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, customint1)
-                SELECT 'meta', 0, parent_course, 5, child_course
-                  FROM {course_meta}";
+                SELECT 'meta', 0, cm.parent_course, 5, cm.child_course
+                  FROM {course_meta} cm
+                  JOIN {course} p ON p.id = cm.parent_course
+                  JOIN {course} c ON c.id = cm.child_course";
         $DB->execute($sql);
 
         upgrade_main_savepoint(true, 2010061900.10);
@@ -6021,6 +6023,26 @@ WHERE gradeitemid IS NOT NULL AND grademax IS NOT NULL");
         upgrade_main_savepoint(true, 2011020200.01);
     }
 
+    if ($oldversion < 2011020900.07) {
+        $DB->delete_records('course_display', array('display' => 0));
+        upgrade_main_savepoint(true, 2011020900.07);
+    }
+
+    if ($oldversion < 2011020900.08) {
+         // Define field secret to be added to registration_hubs
+        $table = new xmldb_table('registration_hubs');
+        $field = new xmldb_field('secret', XMLDB_TYPE_CHAR, '255', null, null, null,
+                $CFG->siteidentifier, 'confirmed');
+
+        // Conditionally launch add field secret
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint(true, 2011020900.08);
+    }
+
 
     return true;
 }
index c9064ad..b3c1ceb 100644 (file)
@@ -56,17 +56,23 @@ class mysqli_native_moodle_database extends moodle_database {
             throw new dml_exception('dbdriverproblem', $driverstatus);
         }
 
-        if (empty($this->dboptions['dbport'])) {
+        if (!empty($dboptions['dbsocket'])
+                and (strpos($dboptions['dbsocket'], '/') !== false or strpos($dboptions['dbsocket'], '\\') !== false)) {
+            $dbsocket = $dboptions['dbsocket'];
+        } else {
+            $dbsocket = ini_get('mysqli.default_socket');
+        }
+        if (empty($dboptions['dbport'])) {
             $dbport = (int)ini_get('mysqli.default_port');
         } else {
-            $dbport = (int)$this->dboptions['dbport'];
+            $dbport = (int)$dboptions['dbport'];
         }
         // verify ini.get does not return nonsense
         if (empty($dbport)) {
             $dbport = 3306;
         }
         ob_start();
-        $conn = new mysqli($dbhost, $dbuser, $dbpass, '', $dbport); /// Connect without db
+        $conn = new mysqli($dbhost, $dbuser, $dbpass, '', $dbport, $dbsocket); /// Connect without db
         $dberr = ob_get_contents();
         ob_end_clean();
         $errorno = @$conn->connect_errno;
index d932e87..ac6e42a 100644 (file)
@@ -771,7 +771,7 @@ class sqlsrv_native_moodle_database extends moodle_database {
      * @return string sql
      */
     private function limit_to_top_n($sql, $offset, $limit) {
-        // If there is no limit we can return immediatly
+        // If there is no limit we can return immediately
         if ($limit < 1 && $offset < 1) {
             return $sql;
         }
index adaaedc..6ef1fa3 100644 (file)
@@ -719,18 +719,26 @@ function file_save_draft_area_files($draftitemid, $contextid, $component, $filea
         return null;
     }
 
-    // relink embedded files if text submitted - no absolute links allowed in database!
-    if ($CFG->slasharguments) {
-        $draftbase = "$CFG->wwwroot/draftfile.php/$usercontext->id/user/draft/$draftitemid/";
-    } else {
-        $draftbase = "$CFG->wwwroot/draftfile.php?file=/$usercontext->id/user/draft/$draftitemid/";
+    $wwwroot = $CFG->wwwroot;
+    if ($forcehttps) {
+        $wwwroot = str_replace('http://', 'https://', $wwwroot);
     }
 
-    if ($forcehttps) {
-        $draftbase = str_replace('http://', 'https://', $draftbase);
+    // relink embedded files if text submitted - no absolute links allowed in database!
+    $text = str_ireplace("$wwwroot/draftfile.php/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
+
+    if (strpos($text, 'draftfile.php?file=') !== false) {
+        $matches = array();
+        preg_match_all("!$wwwroot/draftfile.php\?file=%2F{$usercontext->id}%2Fuser%2Fdraft%2F{$draftitemid}%2F[^'\",&<>|`\s:\\\\]+!iu", $text, $matches);
+        if ($matches) {
+            foreach ($matches[0] as $match) {
+                $replace = str_ireplace('%2F', '/', $match);
+                $text = str_replace($match, $replace, $text);
+            }
+        }
+        $text = str_ireplace("$wwwroot/draftfile.php?file=/$usercontext->id/user/draft/$draftitemid/", '@@PLUGINFILE@@/', $text);
     }
 
-    $text = str_ireplace($draftbase, '@@PLUGINFILE@@/', $text);
 
     return $text;
 }
@@ -1704,7 +1712,7 @@ function send_file($path, $filename, $lifetime = 'default' , $filter=0, $pathiss
                     $ranges = false;
                 }
                 if ($ranges) {
-                    $handle = fopen($filename, 'rb');
+                    $handle = fopen($path, 'rb');
                     byteserving_send_file($handle, $mimetype, $ranges, $filesize);
                 }
             }
index 184ef84..904cd45 100644 (file)
@@ -1061,17 +1061,6 @@ function close_window(e) {
     window.close();
 }
 
-/**
- * Close the current browser window, forcing the window/tab that opened this
- * popup to reload itself. */
-function close_window_reloading_opener() {
-    if (window.opener) {
-        window.opener.location.reload(1);
-        close_window({});
-        // Intentionally, only try to close the window if there is some evidence we are in a popup.
-    }
-}
-
 /**
  * Used in a couple of modules to hide navigation areas when using AJAX
  */
index 91ab14d..2749c18 100644 (file)
@@ -45,17 +45,17 @@ defined('MOODLE_INTERNAL') || die();
  *  contexturlname - the display text for contexturl
  *
  * @param object $eventdata information about the message (component, userfrom, userto, ...)
- * @return boolean success
+ * @return int|false the ID of the new message or false if there was a problem with a processor
  */
 function message_send($eventdata) {
     global $CFG, $DB;
 
+    //new message ID to return
+    $messageid = false;
+
     //TODO: we need to solve problems with database transactions here somehow, for now we just prevent transactions - sorry
     $DB->transactions_forbidden();
 
-    //flag we'll return indicating that all processors ran successfully
-    $success = true;
-
     if (is_int($eventdata->userto)) {
         mtrace('message_send() userto is a user ID when it should be a user object');
         $eventdata->userto = $DB->get_record('user', array('id' => $eventdata->useridto));
@@ -130,10 +130,10 @@ function message_send($eventdata) {
     if ($processor=='none' && $savemessage->notification) {
         //if they have deselected all processors and its a notification mark it read. The user doesnt want to be bothered
         $savemessage->timeread = time();
-        $DB->insert_record('message_read', $savemessage);
+        $messageid = $DB->insert_record('message_read', $savemessage);
     } else {                        // Process the message
         // Store unread message just in case we can not send it
-        $savemessage->id = $DB->insert_record('message', $savemessage);
+        $messageid = $savemessage->id = $DB->insert_record('message', $savemessage);
         $eventdata->savedmessageid = $savemessage->id;
 
         // Try to deliver the message to each processor
@@ -151,12 +151,12 @@ function message_send($eventdata) {
 
                         if (!$pclass->send_message($eventdata)) {
                             debugging('Error calling message processor '.$procname);
-                            $success = false;
+                            $messageid = false;
                         }
                     }
                 } else {
                     debugging('Error finding message processor '.$procname);
-                    $success = false;
+                    $messageid = false;
                 }
             }
             
@@ -165,16 +165,16 @@ function message_send($eventdata) {
             //unread. To prevent this mark the message read if messaging is disabled
             if (empty($CFG->messaging)) {
                 require_once($CFG->dirroot.'/message/lib.php');
-                message_mark_message_read($savemessage, time());
+                $messageid = message_mark_message_read($savemessage, time());
             } else if ( $DB->count_records('message_working', array('unreadmessageid' => $savemessage->id)) == 0){
                 //if there is no more processors that want to process this we can move message to message_read
                 require_once($CFG->dirroot.'/message/lib.php');
-                message_mark_message_read($savemessage, time(), true);
+                $messageid = message_mark_message_read($savemessage, time(), true);
             }
         }
     }
 
-    return $success;
+    return $messageid;
 }
 
 
diff --git a/lib/minify/MOODLE_README.txt b/lib/minify/MOODLE_README.txt
deleted file mode 100644 (file)
index e69de29..0000000
index 5db2e12..4ea33d1 100644 (file)
@@ -4,12 +4,13 @@
  * @package Minify
  */
 
+defined('MOODLE_INTERNAL') || die();
 
 /**
- * In 'debug' mode, Minify can combine files with no minification and 
- * add comments to indicate line #s of the original files. 
- * 
- * To allow debugging, set this option to true and add "&debug=1" to 
+ * In 'debug' mode, Minify can combine files with no minification and
+ * add comments to indicate line #s of the original files.
+ *
+ * To allow debugging, set this option to true and add "&debug=1" to
  * a URI. E.g. /min/?f=script1.js,script2.js&debug=1
  */
 $min_allowDebugFlag = ($CFG->debug);
@@ -20,7 +21,7 @@ $min_allowDebugFlag = ($CFG->debug);
  * Set to false for no error logging (Minify may be slightly faster).
  * @link http://www.firephp.org/
  *
- * If you want to use a custom error logger, set this to your logger 
+ * If you want to use a custom error logger, set this to your logger
  * instance. Your object should have a method log(string $message).
  *
  * @todo cache system does not have error logging yet.
@@ -29,10 +30,10 @@ $min_errorLogger = false;
 
 
 /**
- * Allow use of the Minify URI Builder app. If you no longer need 
+ * Allow use of the Minify URI Builder app. If you no longer need
  * this, set to false.
  **/
-$min_enableBuilder = true;
+$min_enableBuilder = false;
 
 
 /**
@@ -45,11 +46,11 @@ $min_cachePath = $CFG->dataroot.'/temp';
 /**
  * Leave an empty string to use PHP's $_SERVER['DOCUMENT_ROOT'].
  *
- * On some servers, this value may be misconfigured or missing. If so, set this 
+ * On some servers, this value may be misconfigured or missing. If so, set this
  * to your full document root path with no trailing slash.
  * E.g. '/home/accountname/public_html' or 'c:\\xampp\\htdocs'
  *
- * If /min/ is directly inside your document root, just uncomment the 
+ * If /min/ is directly inside your document root, just uncomment the
  * second line. The third line might work on some Apache servers.
  */
 $min_documentRoot = $CFG->dirroot.'/lib/minify';
@@ -58,7 +59,7 @@ $min_documentRoot = $CFG->dirroot.'/lib/minify';
 
 
 /**
- * Cache file locking. Set to false if filesystem is NFS. On at least one 
+ * Cache file locking. Set to false if filesystem is NFS. On at least one
  * NFS system flock-ing attempts stalled PHP for 30 seconds!
  */
 $min_cacheFileLocking = true;
@@ -67,9 +68,9 @@ $min_cacheFileLocking = true;
 /**
  * Combining multiple CSS files can place @import declarations after rules, which
  * is invalid. Minify will attempt to detect when this happens and place a
- * warning comment at the top of the CSS output. To resolve this you can either 
- * move the @imports within your CSS files, or enable this option, which will 
- * move all @imports to the top of the output. Note that moving @imports could 
+ * warning comment at the top of the CSS output. To resolve this you can either
+ * move the @imports within your CSS files, or enable this option, which will
+ * move all @imports to the top of the output. Note that moving @imports could
  * affect CSS values (which is why this option is disabled by default).
  */
 $min_serveOptions['bubbleCssImports'] = false;
@@ -92,8 +93,8 @@ $min_serveOptions['maxAge'] = 1800;
  * particular directories below DOCUMENT_ROOT, set this here.
  * You will still need to include the directory in the
  * f or b GET parameters.
- * 
- * // = shortcut for DOCUMENT_ROOT 
+ *
+ * // = shortcut for DOCUMENT_ROOT
  */
 //$min_serveOptions['minApp']['allowDirs'] = array('//js', '//css');
 
@@ -113,8 +114,8 @@ $min_serveOptions['minApp']['maxFiles'] = 10;
  * If you minify CSS files stored in symlink-ed directories, the URI rewriting
  * algorithm can fail. To prevent this, provide an array of link paths to
  * target paths, where the link paths are within the document root.
- * 
- * Because paths need to be normalized for this to work, use "//" to substitute 
+ *
+ * Because paths need to be normalized for this to work, use "//" to substitute
  * the doc root in the link paths (the array keys). E.g.:
  * <code>
  * array('//symlink' => '/real/target/path') // unix
@@ -126,17 +127,17 @@ $min_symlinks = array();
 
 /**
  * If you upload files from Windows to a non-Windows server, Windows may report
- * incorrect mtimes for the files. This may cause Minify to keep serving stale 
+ * incorrect mtimes for the files. This may cause Minify to keep serving stale
  * cache files when source file changes are made too frequently (e.g. more than
  * once an hour).
- * 
- * Immediately after modifying and uploading a file, use the touch command to 
+ *
+ * Immediately after modifying and uploading a file, use the touch command to
  * update the mtime on the server. If the mtime jumps ahead by a number of hours,
- * set this variable to that number. If the mtime moves back, this should not be 
+ * set this variable to that number. If the mtime moves back, this should not be
  * needed.
  *
- * In the Windows SFTP client WinSCP, there's an option that may fix this 
- * issue without changing the variable below. Under login > environment, 
+ * In the Windows SFTP client WinSCP, there's an option that may fix this
+ * issue without changing the variable below. Under login > environment,
  * select the option "Adjust remote timestamp with DST".
  * @link http://winscp.net/eng/docs/ui_login_environment#daylight_saving_time
  */
@@ -144,7 +145,7 @@ $min_uploaderHoursBehind = 0;
 
 
 /**
- * Path to Minify's lib folder. If you happen to move it, change 
+ * Path to Minify's lib folder. If you happen to move it, change
  * this accordingly.
  */
 $min_libPath = $CFG->libdir . '/minify/lib';
index c59449a..c9542bd 100644 (file)
@@ -3784,12 +3784,6 @@ function get_complete_user_data($field, $value, $mnethostid = null) {
 
 /// Get various settings and preferences
 
-    if ($displays = $DB->get_records('course_display', array('userid'=>$user->id))) {
-        foreach ($displays as $display) {
-            $user->display[$display->course] = $display->display;
-        }
-    }
-
     // preload preference cache
     check_user_preferences_loaded($user);
 
@@ -3808,18 +3802,23 @@ function get_complete_user_data($field, $value, $mnethostid = null) {
 
     // this is a special hack to speedup calendar display
     $user->groupmember = array();
-    if ($groups = $DB->get_records_sql($sql, array($user->id))) {
-        foreach ($groups as $group) {
-            if (!array_key_exists($group->courseid, $user->groupmember)) {
-                $user->groupmember[$group->courseid] = array();
+    if (!isguestuser($user)) {
+        if ($groups = $DB->get_records_sql($sql, array($user->id))) {
+            foreach ($groups as $group) {
+                if (!array_key_exists($group->courseid, $user->groupmember)) {
+                    $user->groupmember[$group->courseid] = array();
+                }
+                $user->groupmember[$group->courseid][$group->id] = $group->id;
             }
-            $user->groupmember[$group->courseid][$group->id] = $group->id;
         }
     }
 
 /// Add the custom profile fields to the user record
-    require_once($CFG->dirroot.'/user/profile/lib.php');
-    profile_load_custom_fields($user);
+    $user->profile = array();
+    if (!isguestuser($user)) {
+        require_once($CFG->dirroot.'/user/profile/lib.php');
+        profile_load_custom_fields($user);
+    }
 
 /// Rewrite some variables if necessary
     if (!empty($user->description)) {
index 1fbd58a..5376bed 100644 (file)
@@ -1023,7 +1023,7 @@ class global_navigation extends navigation_node {
                 $this->add_course_essentials($coursenode, $course);
                 // Load the course sections into the page
                 $sections = $this->load_course_sections($course, $coursenode);
-                if ($course->id !== SITEID) {
+                if ($course->id != SITEID) {
                     // Find the section for the $CM associated with the page and collect
                     // its section number.
                     if (isset($cm->sectionnum)) {
@@ -1105,7 +1105,7 @@ class global_navigation extends navigation_node {
         }
         // Load each extending user into the navigation.
         foreach ($this->extendforuser as $user) {
-            if ($user->id !== $USER->id) {
+            if ($user->id != $USER->id) {
                 $this->load_for_user($user);
             }
         }
@@ -1384,11 +1384,7 @@ class global_navigation extends navigation_node {
 
         $viewhiddensections = has_capability('moodle/course:viewhiddensections', $this->page->context);
 
-        if (isloggedin() && !isguestuser()) {
-            $activesection = $DB->get_field("course_display", "display", array("userid"=>$USER->id, "course"=>$course->id));
-        } else {
-            $activesection = null;
-        }
+        $activesection = course_get_display($course->id);
 
         $namingfunction = 'callback_'.$courseformat.'_get_section_name';
         $namingfunctionexists = (function_exists($namingfunction));
@@ -1418,7 +1414,7 @@ class global_navigation extends navigation_node {
                 $sectionnode = $coursenode->add($sectionname, $url, navigation_node::TYPE_SECTION, null, $section->id);
                 $sectionnode->nodetype = navigation_node::NODETYPE_BRANCH;
                 $sectionnode->hidden = (!$section->visible);
-                if ($this->page->context->contextlevel != CONTEXT_MODULE && ($sectionnode->isactive || ($activesection != null && $section->section == $activesection))) {
+                if ($this->page->context->contextlevel != CONTEXT_MODULE && ($sectionnode->isactive || ($activesection && $section->section == $activesection))) {
                     $sectionnode->force_open();
                     $this->load_section_activities($sectionnode, $section->section, $modinfo);
                 }
@@ -1517,7 +1513,7 @@ class global_navigation extends navigation_node {
      */
     protected function load_activity($cm, stdClass $course, navigation_node $activity) {
         global $CFG, $DB;
-        
+
         // make sure we have a $cm from get_fast_modinfo as this contains activity access details
         if (!($cm instanceof cm_info)) {
             $modinfo = get_fast_modinfo($course);
@@ -1569,7 +1565,7 @@ class global_navigation extends navigation_node {
         // Get the course set against the page, by default this will be the site
         $course = $this->page->course;
         $baseargs = array('id'=>$user->id);
-        if ($course->id !== SITEID && (!$iscurrentuser || $forceforcontext)) {
+        if ($course->id != SITEID && (!$iscurrentuser || $forceforcontext)) {
             if (array_key_exists($course->id, $this->mycourses)) {
                 $coursenode = $this->mycourses[$course->id]->coursenode;
             } else {
@@ -1857,9 +1853,15 @@ class global_navigation extends navigation_node {
      */
     public function add_course(stdClass $course, $forcegeneric = false) {
         global $CFG;
-        $canviewhidden = has_capability('moodle/course:viewhiddencourses', $this->page->context);
-        if ($course->id !== SITEID && !$canviewhidden && !$course->visible) {
-            return false;
+
+        if ($course->id != SITEID) {
+            if (!$course->visible) {
+                if (is_role_switched($course->id)) {
+                    // user has to be able to access course in order to switch, let's skip the visibility test here
+                } else if (!has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
+                    return false;
+                }
+            }
         }
 
         $issite = ($course->id == SITEID);
@@ -2984,7 +2986,7 @@ class settings_navigation extends navigation_node {
             $availableroles = get_switchable_roles($coursecontext);
             if (is_array($availableroles)) {
                 foreach ($availableroles as $key=>$role) {
-                    if ($assumedrole===(int)$key) {
+                    if ($assumedrole == (int)$key) {
                         continue;
                     }
                     $roles[$key] = $role;
index bbcad53..3231669 100644 (file)
@@ -2409,12 +2409,12 @@ EOD;
             return '';
         }
         if ($item->action instanceof action_link) {
-            //TODO: to be replaced with something else
             $link = $item->action;
             if ($item->hidden) {
                 $link->add_class('dimmed');
             }
-            $content = $this->output->render($link);
+            $link->text = $content.$link->text; // add help icon
+            $content = $this->render($link);
         } else if ($item->action instanceof moodle_url) {
             $attributes = array();
             if ($title !== '') {
index 7be9328..a9d5127 100644 (file)
@@ -37,7 +37,7 @@ defined('MOODLE_INTERNAL') || die();
  *
  * Typical usage would be
  * <pre>
- *     $PAGE->requires->init_js_call('M.mod_forum.init_view');
+ *     $PAGE->requires->js_init_call('M.mod_forum.init_view');
  * </pre>
  *
  * It also supports obsoleted coding style withouth YUI3 modules.
index f655068..f528082 100644 (file)
@@ -216,19 +216,8 @@ abstract class session_stub implements moodle_session {
         $user = null;
 
         if (!empty($CFG->opentogoogle) and !NO_MOODLE_COOKIES) {
-            if (!empty($_SERVER['HTTP_USER_AGENT'])) {
-                // allow web spiders in as guest users
-                if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false ) {
-                    $user = guest_user();
-                } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false ) { // Google
-                    $user = guest_user();
-                } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') !== false ) {  // Yahoo
-                    $user = guest_user();
-                } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) {  // Zoomspider
-                    $user = guest_user();
-                } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSNBOT') !== false ) {  // MSN Search
-                    $user = guest_user();
-                }
+            if (is_web_crawler()) {
+                $user = guest_user();
             }
             if (!empty($CFG->guestloginbutton) and !$user and !empty($_SERVER['HTTP_REFERER'])) {
                 // automaticaly log in users coming from search engine results
index 46028a7..d86f1b8 100644 (file)
@@ -1123,6 +1123,38 @@ function init_eaccelerator() {
     return false;
 }
 
+/**
+ * Checks if current user is a web crawler.
+ *
+ * This list can not be made complete, this is not a security
+ * restriction, we make the list only to help these sites
+ * especially when automatic guest login is disabled.
+ *
+ * If admin needs security they should enable forcelogin
+ * and disable guest access!!
+ *
+ * @return bool
+ */
+function is_web_crawler() {
+    if (!empty($_SERVER['HTTP_USER_AGENT'])) {
+        if (strpos($_SERVER['HTTP_USER_AGENT'], 'Googlebot') !== false ) {
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'google.com') !== false ) { // Google
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yahoo! Slurp') !== false ) {  // Yahoo
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], '[ZSEBOT]') !== false ) {  // Zoomspider
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSNBOT') !== false ) {  // MSN Search
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'Yandex') !== false ) {
+            return true;
+        } else if (strpos($_SERVER['HTTP_USER_AGENT'], 'AltaVista') !== false ) {
+            return true;
+        }
+    }
+    return false;
+}
 
 /**
  * This class solves the problem of how to initialise $OUTPUT.
index 69b13a4..85ef747 100644 (file)
@@ -823,13 +823,12 @@ function close_window($delay = 0, $reloadopener = false) {
     }
 
     if ($reloadopener) {
-        $function = 'close_window_reloading_opener';
-    } else {
-        $function = 'close_window';
+        // Trigger the reload immediately, even if the reload is after a delay.
+        $PAGE->requires->js_function_call('window.opener.location.reload', array(true));
     }
-    echo '<p class="centerpara">' . get_string('windowclosing') . '</p>';
+    $OUTPUT->notification(get_string('windowclosing'), 'notifysuccess');
 
-    $PAGE->requires->js_function_call($function, null, false, $delay);
+    $PAGE->requires->js_function_call('close_window', array(new stdClass()), false, $delay);
 
     echo $OUTPUT->footer();
     exit;
@@ -1967,7 +1966,7 @@ function get_separator() {
  * @return string|void If $return is false, returns nothing, otherwise returns a string of HTML.
  */
 function print_collapsible_region($contents, $classes, $id, $caption, $userpref = '', $default = false, $return = false) {
-    $output  = print_collapsible_region_start($classes, $id, $caption, $userpref, true, true);
+    $output  = print_collapsible_region_start($classes, $id, $caption, $userpref, $default, true);
     $output .= $contents;
     $output .= print_collapsible_region_end(true);
 
@@ -1983,21 +1982,20 @@ function print_collapsible_region($contents, $classes, $id, $caption, $userpref
  * be clicked to expand or collapse the region. If JavaScript is off, then the region
  * will always be expanded.
  *
- * @global object
  * @param string $classes class names added to the div that is output.
  * @param string $id id added to the div that is output. Must not be blank.
  * @param string $caption text displayed at the top. Clicking on this will cause the region to expand or contract.
- * @param boolean $userpref the name of the user preference that stores the user's preferred default state.
+ * @param string $userpref the name of the user preference that stores the user's preferred default state.
  *      (May be blank if you do not wish the state to be persisted.
  * @param boolean $default Initial collapsed state to use if the user_preference it not set.
  * @param boolean $return if true, return the HTML as a string, rather than printing it.
  * @return string|void if $return is false, returns nothing, otherwise returns a string of HTML.
  */
-function print_collapsible_region_start($classes, $id, $caption, $userpref = false, $default = false, $return = false) {
+function print_collapsible_region_start($classes, $id, $caption, $userpref = '', $default = false, $return = false) {
     global $CFG, $PAGE, $OUTPUT;
 
     // Work out the initial state.
-    if (is_string($userpref)) {
+    if (!empty($userpref) and is_string($userpref)) {
         user_preference_allow_ajax_update($userpref, PARAM_BOOL);
         $collapsed = get_user_preferences($userpref, $default);
     } else {
index 53892cb..b0791ed 100644 (file)
@@ -182,7 +182,7 @@ if ($frm and isset($frm->username)) {                             // Login WITH
             $urltogo = $CFG->wwwroot.'/user/edit.php';
             // We don't delete $SESSION->wantsurl yet, so we get there later
 
-        } else if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0)) {
+        } else if (isset($SESSION->wantsurl) and (strpos($SESSION->wantsurl, $CFG->wwwroot) === 0 or strpos($SESSION->wantsurl, str_replace('http://', 'https://', $CFG->wwwroot)) === 0)) {
             $urltogo = $SESSION->wantsurl;    /// Because it's an address in this site
             unset($SESSION->wantsurl);
 
index 373fdd7..ed1e73c 100644 (file)
@@ -79,6 +79,9 @@ class login_signup_form extends moodleform {
         $mform->addElement('text', 'city', get_string('city'), 'maxlength="120" size="20"');
         $mform->setType('city', PARAM_TEXT);
         $mform->addRule('city', get_string('missingcity'), 'required', null, 'server');
+        if (!empty($CFG->defaultcity)) {
+            $mform->setDefault('city', $CFG->defaultcity);
+        }
 
         $country = get_string_manager()->get_list_of_countries();
         $default_country[''] = get_string('selectacountry');
index 7addba5..8792c1c 100644 (file)
@@ -41,8 +41,15 @@ $usergroup = optional_param('usergroup', VIEW_UNREAD_MESSAGES, PARAM_ALPHANUMEXT
 $history   = optional_param('history', MESSAGE_HISTORY_SHORT, PARAM_INT);
 $search    = optional_param('search', '', PARAM_CLEAN); //TODO: use PARAM_RAW, but make sure we use s() and p() properly
 
-$user1id   = optional_param('user', $USER->id, PARAM_INT);
-$user2id   = optional_param('id', 0, PARAM_INT);
+//the same param as 1.9 and the param we have been logging. Use this parameter.
+$user1id   = optional_param(MESSAGE_USER1_PARAM, $USER->id, PARAM_INT);
+//2.0 shipped using this param. Retaining it only for compatibility. It should be removed.
+$user1id   = optional_param('user', $user1id, PARAM_INT);
+
+//the same param as 1.9 and the param we have been logging. Use this parameter.
+$user2id   = optional_param(MESSAGE_USER2_PARAM, 0, PARAM_INT);
+//2.0 shipped using this param. Retaining it only for compatibility. It should be removed.
+$user2id   = optional_param('id', $user2id, PARAM_INT);
 
 $addcontact     = optional_param('addcontact',     0, PARAM_INT); // adding a contact
 $removecontact  = optional_param('removecontact',  0, PARAM_INT); // removing a contact
@@ -158,6 +165,9 @@ if ($currentuser && !empty($user2) && has_capability('moodle/site:sendmessage',
 
             $messageid = message_post_message($user1, $user2, $data->message, FORMAT_MOODLE, 'direct');
             if (!empty($messageid)) {
+                //including the id of the user sending the message in the logged URL so the URL works for admins
+                //note message ID may be misleading as the message may potentially get a different ID when moved from message to message_read
+                add_to_log(SITEID, 'message', 'write', 'index.php?user='.$user1->id.'&id='.$user2->id.'&history=1#m'.$messageid, $user1->id);
                 redirect($CFG->wwwroot . '/message/index.php?usergroup='.$usergroup.'&id='.$user2->id);
             }
         }
index bbb4a93..589b3ef 100644 (file)
@@ -56,6 +56,9 @@ define('VIEW_BLOCKED','blockedusers');
 define('VIEW_COURSE','course_');
 define('VIEW_SEARCH','search');
 
+define('MESSAGE_USER1_PARAM','user1');
+define('MESSAGE_USER2_PARAM','user2');
+
 define('SHOW_ACTION_LINKS_IN_CONTACT_LIST', true);
 
 define('MESSAGE_SEARCH_MAX_RESULTS', 200);
@@ -1547,6 +1550,7 @@ function message_format_message(&$message, &$user, $format='', $keywords='', $cl
 /**
  * Inserts a message into the database, but also forwards it
  * via other means if appropriate.
+ * @return int|false the ID of the new message or false
  */
 function message_post_message($userfrom, $userto, $message, $format, $messagetype) {
     global $SITE, $CFG, $USER;
@@ -1764,7 +1768,7 @@ function message_mark_messages_read($touserid, $fromuserid){
 * @param message an object with an object property ie $message->id which is an id in the message table
 * @param int $timeread the timestamp for when the message should be marked read. Usually time().
 * @param bool $messageworkingempty Is the message_working table already confirmed empty for this message?
-* @return void
+* @return int the ID of the message in the message_read table
 */
 function message_mark_message_read($message, $timeread, $messageworkingempty=false) {
     global $DB;
@@ -1778,6 +1782,7 @@ function message_mark_message_read($message, $timeread, $messageworkingempty=fal
     if (!$messageworkingempty) {
         $DB->delete_records('message_working', array('unreadmessageid' => $messageid));
     }
-    $DB->insert_record('message_read', $message);
+    $messagereadid = $DB->insert_record('message_read', $message);
     $DB->delete_records('message', array('id' => $messageid));
+    return $messagereadid;
 }
index 61bf349..3bd234e 100644 (file)
@@ -55,7 +55,7 @@ class mod_data_export_form extends moodleform {
             }
         }
         $this->add_checkbox_controller(1, null, null, 1);
-        $this->add_action_buttons(true, get_string('exportdatabaserecords', 'data'));
+        $this->add_action_buttons(true, get_string('exportentries', 'data'));
     }
 
 }
index 346c54f..3110490 100755 (executable)
@@ -48,28 +48,16 @@ if ($fieldenclosure !== '') {
 if ($id) {
     $url->param('id', $id);
     $PAGE->set_url($url);
-    if (! $cm = get_coursemodule_from_id('data', $id)) {
-        print_error('invalidcoursemodule');
-    }
-    if (! $course = $DB->get_record('course', array('id'=>$cm->course))) {
-        print_error('coursemisconf');
-    }
-    if (! $data = $DB->get_record('data', array('id'=>$cm->instance))) {
-        print_error('invalidcoursemodule');
-    }
+    $cm     = get_coursemodule_from_id('data', $id, 0, false, MUST_EXIST);
+    $course = $DB->get_record('course', array('id'=>$cm->course), '*', MUST_EXIST);
+    $data   = $DB->get_record('data', array('id'=>$cm->instance), '*', MUST_EXIST);
 
 } else {
     $url->param('d', $d);
     $PAGE->set_url($url);
-    if (! $data = $DB->get_record('data', array('id'=>$d))) {
-        print_error('invalidid', 'data');
-    }
-    if (! $course = $DB->get_record('course', array('id'=>$data->course))) {
-        print_error('coursemisconf');
-    }
-    if (! $cm = get_coursemodule_from_instance('data', $data->id, $course->id)) {
-        print_error('coursemisconf');
-    }
+    $data   = $DB->get_record('data', array('id'=>$d), '*', MUST_EXIST);
+    $course = $DB->get_record('course', array('id'=>$data->course), '*', MUST_EXIST);
+    $cm     = get_coursemodule_from_instance('data', $data->id, $course->id, false, MUST_EXIST);
 }
 
 require_login($course, false, $cm);
@@ -82,8 +70,9 @@ $form = new mod_data_import_form(new moodle_url('/mod/data/import.php'));
 $PAGE->navbar->add(get_string('add', 'data'));
 $PAGE->set_title($data->name);
 $PAGE->set_heading($course->fullname);
+navigation_node::override_active_url(new moodle_url('/mod/data/import.php', array('d' => $data->id)));
 echo $OUTPUT->header();
-echo $OUTPUT->heading(format_string($data->name));
+echo $OUTPUT->heading_with_help(get_string('uploadrecords', 'mod_data'), 'uploadrecords', 'mod_data');
 
 /// Groups needed for Add entry tab
 $currentgroup = groups_get_activity_group($cm);
@@ -93,7 +82,6 @@ if (!$formdata = $form->get_data()) {
     /// Upload records section. Only for teachers and the admin.
     echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthwide');
     require_once('import_form.php');
-    echo $OUTPUT->heading(get_string('uploadrecords', 'data'), 3);
     $form = new mod_data_import_form(new moodle_url('/mod/data/import.php'));
     $formdata = new stdClass();
     $formdata->d = $data->id;
index 351b143..b8d029f 100755 (executable)
@@ -122,8 +122,8 @@ $string['excel'] = 'Excel';
 $string['export'] = 'Export';
 $string['exportaszip'] = 'Export as zip';
 $string['exportaszip_help'] = 'The export as zip feature allows you to save the templates and fields as a preset zip for download. The zip may then be imported to another course.';
-$string['exportdatabaserecords'] = 'Export database records';
 $string['exportedtozip'] = 'Exported to temporary zip...';
+$string['exportentries'] = 'Export entries';
 $string['exportownentries'] = 'Export your own entries only? ({$a->mine}/{$a->all})';
 $string['failedpresetdelete'] = 'Error deleting a preset!';
 $string['fieldadded'] = 'Field added';
@@ -170,6 +170,7 @@ $string['chooseexportfields'] = 'Choose the fields you wish to export:';
 $string['chooseexportformat'] = 'Choose the format you wish to export to:';
 $string['chooseorupload'] = 'Choose file';
 $string['expired'] = 'Sorry, this activity closed on {$a} and is no longer available';
+$string['importentries'] = 'Import entries';
 $string['importsuccess'] = 'The preset has been successfully applied.';
 $string['insufficiententries'] = 'more entries needed to view this database';
 $string['intro'] = 'Introduction';
@@ -307,6 +308,14 @@ $string['unsupportedexport'] = '({$a->fieldtype}) cannot be exported.';
 $string['updatefield'] = 'Update an existing field';
 $string['uploadfile'] = 'Upload file';
 $string['uploadrecords'] = 'Upload entries from a file';
+$string['uploadrecords_help'] = 'Entries may be uploaded via text file. The format of the file should be as follows:
+
+* Each line of the file contains one record
+* Each record is a series of data separated by commas (or other delimiters)
+* The first record contains a list of fieldnames defining the format of the rest of the file
+
+The field enclosure is a character that surrounds each field in each record. It can normally be left unset.';
+$string['uploadrecords_link'] = 'mod/data/import';
 $string['url'] = 'Url';
 $string['usestandard'] = 'Use a preset';
 $string['usestandard_help'] = 'To use a preset available to the whole site, select it from the list. (If you have added a preset to the list using the save as preset feature then you have the option of deleting it.)';
index 534e424..6c02c04 100755 (executable)
@@ -2848,10 +2848,10 @@ function data_extend_settings_navigation(settings_navigation $settings, navigati
     if (has_capability(DATA_CAP_EXPORT, $PAGE->cm->context)) {
         // The capability required to Export database records is centrally defined in 'lib.php'
         // and should be weaker than those required to edit Templates, Fields and Presets.
-        $datanode->add(get_string('export', 'data'), new moodle_url('/mod/data/export.php', array('d'=>$data->id)));
+        $datanode->add(get_string('exportentries', 'data'), new moodle_url('/mod/data/export.php', array('d'=>$data->id)));
     }
     if (has_capability('mod/data:manageentries', $PAGE->cm->context)) {
-        $datanode->add(get_string('import'), new moodle_url('/mod/data/import.php', array('d'=>$data->id)));
+        $datanode->add(get_string('importentries', 'data'), new moodle_url('/mod/data/import.php', array('d'=>$data->id)));
     }
 
     if (has_capability('mod/data:managetemplates', $PAGE->cm->context)) {
index cf69cb7..a32d74b 100644 (file)
@@ -1812,7 +1812,7 @@ function forum_get_readable_forums($userid, $courseid=0) {
     } else {
         // If no course is specified, then the user can see SITE + his courses.
         $courses1 = $DB->get_records('course', array('id' => SITEID));
-        $courses2 = enrol_get_users_courses($userid, true);
+        $courses2 = enrol_get_users_courses($userid, true, array('modinfo'));
         $courses = array_merge($courses1, $courses2);
     }
     if (!$courses) {
@@ -1929,8 +1929,8 @@ function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=5
         $select = array();
 
         if (!$forum->viewhiddentimedposts) {
-            $select[] = "(d.userid = :userid OR (d.timestart < : AND (d.timeend = 0 OR d.timeend > :timeend)))";
-            $params = array('userid'=>$USER->id, 'timestart'=>$now, 'timeend'=>$now);
+            $select[] = "(d.userid = :userid{$forumid} OR (d.timestart < :timestart{$forumid} AND (d.timeend = 0 OR d.timeend > :timeend{$forumid})))";
+            $params = array_merge($params, array('userid'.$forumid=>$USER->id, 'timestart'.$forumid=>$now, 'timeend'.$forumid=>$now));
         }
 
         $cm = $forum->cm;
@@ -1939,7 +1939,7 @@ function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=5
         if ($forum->type == 'qanda'
             && !has_capability('mod/forum:viewqandawithoutposting', $context)) {
             if (!empty($forum->onlydiscussions)) {
-                list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda0');
+                list($discussionid_sql, $discussionid_params) = $DB->get_in_or_equal($forum->onlydiscussions, SQL_PARAMS_NAMED, 'qanda'.$forumid.'_0000');
                 $params = array_merge($params, $discussionid_params);
                 $select[] = "(d.id $discussionid_sql OR p.parent = 0)";
             } else {
@@ -1948,15 +1948,15 @@ function forum_search_posts($searchterms, $courseid=0, $limitfrom=0, $limitnum=5
         }
 
         if (!empty($forum->onlygroups)) {
-            list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps0');
+            list($groupid_sql, $groupid_params) = $DB->get_in_or_equal($forum->onlygroups, SQL_PARAMS_NAMED, 'grps'.$forumid.'_0000');
             $params = array_merge($params, $groupid_params);
             $select[] = "d.groupid $groupid_sql";
         }
 
         if ($select) {
             $selects = implode(" AND ", $select);
-            $where[] = "(d.forum = :forum AND $selects)";
-            $params['forum'] = $forumid;
+            $where[] = "(d.forum = :forum{$forumid} AND $selects)";
+            $params['forum'.$forumid] = $forumid;
         } else {
             $fullaccess[] = $forumid;
         }
index 6a03912..6576056 100644 (file)
@@ -106,11 +106,10 @@ switch ($mode) {
 echo '<div class="user-content">';
 
 if ($course->id == SITEID) {
-    if (empty($CFG->forceloginforprofiles) || isloggedin()) {
+    $searchcourse = SITEID;
+    if (empty($CFG->forceloginforprofiles) or (isloggedin() and !isguestuser() and !is_web_crawler())) {
         // Search throughout the whole site.
         $searchcourse = 0;
-    } else {
-        $searchcourse = SITEID;
     }
 } else {
     // Search only for posts the user made in this course.
index 278ecc1..e81f196 100644 (file)
@@ -39,6 +39,7 @@ $string['allowduplicatedentries'] = 'Duplicate entries allowed';
 $string['allowduplicatedentries_help'] = 'If enabled, multiple entries can have the same concept name.';
 $string['allowprintview'] = 'Allow print view';
 $string['allowprintview_help'] = 'If enabled, students are provided with a link to a printer-friendly version of the glossary. The link is always available to teachers.';
+$string['andmorenewentries'] = 'and {$a} more new entries.';
 $string['answer'] = 'Answer';
 $string['approve'] = 'Approve';
 $string['areyousuredelete'] = 'Are you sure you want to delete this entry?';
index 498036b..cb96fac 100644 (file)
@@ -318,9 +318,13 @@ function glossary_print_recent_activity($course, $viewfullnames, $timestart) {
     global $CFG, $USER, $DB, $OUTPUT;
 
     //TODO: use timestamp in approved field instead of changing timemodified when approving in 2.0
+    if (!defined('GLOSSARY_RECENT_ACTIVITY_LIMIT')) {
+        define('GLOSSARY_RECENT_ACTIVITY_LIMIT', 50);
+    }
 
     $modinfo = get_fast_modinfo($course);
     $ids = array();
+
     foreach ($modinfo->cms as $cm) {
         if ($cm->modname != 'glossary') {
             continue;
@@ -328,58 +332,71 @@ function glossary_print_recent_activity($course, $viewfullnames, $timestart) {
         if (!$cm->uservisible) {
             continue;
         }
-        $ids[$cm->instance] = $cm->instance;
+        $ids[$cm->instance] = $cm->id;
     }
 
     if (!$ids) {
         return false;
     }
 
-    $glist = implode(',', $ids); // there should not be hundreds of glossaries in one course, right?
-
-    if (!$entries = $DB->get_records_sql("SELECT ge.id, ge.concept, ge.approved, ge.timemodified, ge.glossaryid,
-                                                 ge.userid, u.firstname, u.lastname, u.email, u.picture
-                                            FROM {glossary_entries} ge
-                                            JOIN {user} u ON u.id = ge.userid
-                                           WHERE ge.glossaryid IN ($glist) AND ge.timemodified > ?
-                                        ORDER BY ge.timemodified ASC", array($timestart))) {
-        return false;
+    // generate list of approval capabilities for all glossaries in the course.
+    $approvals = array();
+    foreach ($ids as $glinstanceid => $glcmid) {
+        $context = get_context_instance(CONTEXT_MODULE, $glcmid);
+        // get records glossary entries that are approved if user has no capability to approve entries.
+        if (has_capability('mod/glossary:approve', $context)) {
+            $approvals[] = ' ge.glossaryid = :glsid'.$glinstanceid.' ';
+        } else {
+            $approvals[] = ' (ge.approved = 1 AND ge.glossaryid = :glsid'.$glinstanceid.') ';
+        }
+        $params['glsid'.$glinstanceid] = $glinstanceid;
     }
 
-    $editor  = array();
+    $selectsql = 'SELECT ge.id, ge.concept, ge.approved, ge.timemodified, ge.glossaryid,
+                                        ge.userid, '.user_picture::fields('u',null,'uid');
+    $countsql = 'SELECT COUNT(*)';
 
-    foreach ($entries as $entryid=>$entry) {
-        if ($entry->approved) {
-            continue;
-        }
+    $joins = array(' FROM {glossary_entries} ge ');
+    $joins[] = 'JOIN {user} u ON u.id = ge.userid ';
+    $fromsql = implode($joins, "\n");
 
-        if (!isset($editor[$entry->glossaryid])) {
-            $editor[$entry->glossaryid] = has_capability('mod/glossary:approve', get_context_instance(CONTEXT_MODULE, $modinfo->instances['glossary'][$entry->glossaryid]->id));
-        }
+    $params['timestart'] = $timestart;
+    $clausesql = ' WHERE ge.timemodified > :timestart AND (';
+    $approvalsql = implode($approvals, ' OR ');
 
-        if (!$editor[$entry->glossaryid]) {
-            unset($entries[$entryid]);
-        }
-    }
+    $ordersql = ') ORDER BY ge.timemodified ASC';
+
+    $entries = $DB->get_records_sql($selectsql.$fromsql.$clausesql.$approvalsql.$ordersql, $params, 0, (GLOSSARY_RECENT_ACTIVITY_LIMIT+1));
 
-    if (!$entries) {
+    if (empty($entries)) {
         return false;
     }
+
     echo $OUTPUT->heading(get_string('newentries', 'glossary').':');
 
     $strftimerecent = get_string('strftimerecent');
+    $entrycount = 0;
     foreach ($entries as $entry) {
-        $link = $CFG->wwwroot.'/mod/glossary/view.php?g='.$entry->glossaryid.'&amp;mode=entry&amp;hook='.$entry->id;
-        if ($entry->approved) {
-            $dimmed = '';
+        if ($entrycount < GLOSSARY_RECENT_ACTIVITY_LIMIT) {
+            if ($entry->approved) {
+                $dimmed = '';
+                $urlparams = array('g' => $entry->glossaryid, 'mode' => 'entry', 'hook' => $entry->id);
+            } else {
+                $dimmed = ' dimmed_text';
+                $urlparams = array('id' => $ids[$entry->glossaryid], 'mode' => 'approval', 'hook' => format_text($entry->concept, true));
+            }
+            $link = new moodle_url($CFG->wwwroot.'/mod/glossary/view.php' , $urlparams);
+            echo '<div class="head'.$dimmed.'">';
+            echo '<div class="date">'.userdate($entry->timemodified, $strftimerecent).'</div>';
+            echo '<div class="name">'.fullname($entry, $viewfullnames).'</div>';
+            echo '</div>';
+            echo '<div class="info"><a href="'.$link.'">'.format_text($entry->concept, true).'</a></div>';
+            $entrycount += 1;
         } else {
-            $dimmed = ' dimmed_text';
+            $numnewentries = $DB->count_records_sql($countsql.$joins[0].$clausesql.$approvalsql.')', $params);
+            echo '<div class="head"><div class="activityhead">'.get_string('andmorenewentries', 'glossary', $numnewentries - GLOSSARY_RECENT_ACTIVITY_LIMIT).'</div></div>';
+            break;
         }
-        echo '<div class="head'.$dimmed.'">';
-        echo '<div class="date">'.userdate($entry->timemodified, $strftimerecent).'</div>';
-        echo '<div class="name">'.fullname($entry, $viewfullnames).'</div>';
-        echo '</div>';
-        echo '<div class="info"><a href="'.$link.'">'.format_text($entry->concept, true).'</a></div>';
     }
 
     return true;
index 912473b..6298f80 100644 (file)
     $sqlsortkey = NULL;
     $textlib = textlib_get_instance();
 
+    // For cases needing inner view
+    $sqlwrapheader = '';
+    $sqlwrapfooter = '';
+
 /// Calculate the SQL sortkey to be used by the SQL statements later
     switch ( $sortkey ) {
         case "CREATION":
             } else {
                 $searchcond = implode(" AND ", $searchcond);
 
-                $sqlselect  = "SELECT DISTINCT ge.*, ge.concept AS glossarypivot";
+                // Need one inner view here to avoid distinct + text
+                $sqlwrapheader = 'SELECT ge.*, ge.concept AS glossarypivot
+                                    FROM {glossary_entries} ge
+                                    JOIN ( ';
+                $sqlwrapfooter = ' ) gei ON (ge.id = gei.id)';
+
+                $sqlselect  = "SELECT DISTINCT ge.id";
                 $sqlfrom    = "FROM {glossary_entries} ge
                                LEFT JOIN {glossary_alias} al ON al.entryid = ge.id";
                 $where      = "AND ($searchcond)";
         $limitnum = $entriesbypage;
     }
 
-    $allentries = $DB->get_records_sql("$sqlselect $sqlfrom $sqlwhere $sqlorderby", $params, $limitfrom, $limitnum);
+    $query = "$sqlwrapheader $sqlselect $sqlfrom $sqlwhere $sqlwrapfooter $sqlorderby";
+    $allentries = $DB->get_records_sql($query, $params, $limitfrom, $limitnum);
 
index 0b90f42..c55bffe 100644 (file)
@@ -76,8 +76,11 @@ class backup_lesson_activity_structure_step extends backup_activity_structure_st
             'minquestions','maxpages','timed','maxtime','retake','activitylink',
             'mediafile','mediaheight','mediawidth','mediaclose','slideshow',
             'width','height','bgcolor','displayleft','displayleftif','progressbar',
-            'highscores','maxhighscores','available','deadline','timemodified'
+            'showhighscores','maxhighscores','available','deadline','timemodified'
         ));
+        // Tell the lesson element about the showhighscores elements mapping to the highscores
+        // database field.
+        $lesson->set_source_alias('highscores', 'showhighscores');
 
         // The lesson_pages table
         // Grouped within a `pages` element, important to note that page is relational
index 90d0cb6..00dc383 100644 (file)
@@ -62,6 +62,13 @@ class restore_lesson_activity_structure_step extends restore_activity_structure_
         $data->deadline = $this->apply_date_offset($data->deadline);
         $data->timemodified = $this->apply_date_offset($data->timemodified);
 
+        // lesson->highscores can come both in data->highscores and
+        // data->showhighscores, handle both. MDL-26229
+        if (isset($data->showhighscores)) {
+            $data->highscores = $data->showhighscores;
+            unset($data->showhighscores);
+        }
+
         // insert the lesson record
         $newitemid = $DB->insert_record('lesson', $data);
         // immediately after inserting "activity" record, call this
@@ -181,6 +188,17 @@ class restore_lesson_activity_structure_step extends restore_activity_structure_
         }
         $rs->close();
 
+        // Remap all the restored 'jumpto' fields now that we have all the pages and their mappings
+        $rs = $DB->get_recordset('lesson_answers', array('lessonid' => $this->task->get_activityid()),
+                                 '', 'id, jumpto');
+        foreach ($rs as $answer) {
+            if ($answer->jumpto > 0) {
+                $answer->jumpto = $this->get_mappingid('lesson_page', $answer->jumpto);
+                $DB->update_record('lesson_answers', $answer);
+            }
+        }
+        $rs->close();
+
         // TODO: somewhere at the end of the restore... when all the activities have been restored
         // TODO: we need to decode the lesson->activitylink that points to another activity in the course
         // TODO: great functionality that breaks self-contained principles, grrr
index 6015d55..04d3fad 100644 (file)
@@ -135,12 +135,17 @@ switch ($mode) {
         } else {
             $queryadd = '';
             $params = array ("lessonid" => $lesson->id);
-            if (!$users = $DB->get_records_sql("SELECT DISTINCT u.id, u.*
-                                     FROM {user} u,
-                                          {lesson_attempts} a
-                                     WHERE a.lessonid = :lessonid and
-                                           u.id = a.userid
-                                     ORDER BY u.lastname", $params)) {
+            // Need to use inner view to avoid distinct + text
+            if (!$users = $DB->get_records_sql("
+                SELECT u.*
+                  FROM {user} u
+                  JOIN (
+                    SELECT DISTINCT u.id
+                      FROM {user} u,
+                           {lesson_attempts} a
+                     WHERE a.lessonid = :lessonid and
+                           u.id = a.userid) ui ON (u.id = ui.id)
+                  ORDER BY u.lastname", $params)) {
                 print_error('cannotfinduser', 'lesson');
             }
         }
index 8fdcb1f..1d670c9 100644 (file)
@@ -41,10 +41,12 @@ require_login($course, false, $cm);
 $context = get_context_instance(CONTEXT_MODULE, $cm->id);
 require_capability('mod/lesson:manage', $context);
 
+$ufields = user_picture::fields('u'); // These fields are enough
 $params = array("lessonid" => $lesson->id);
+// TODO: Improve this. Fetching all students always is crazy!
 if (!empty($cm->groupingid)) {
     $params["groupid"] = $cm->groupingid;
-    $sql = "SELECT DISTINCT u.id, u.*
+    $sql = "SELECT DISTINCT $ufields
                 FROM {lesson_attempts} a
                     INNER JOIN {user} u ON u.id = a.userid
                     INNER JOIN {groups_members} gm ON gm.userid = u.id
@@ -52,7 +54,7 @@ if (!empty($cm->groupingid)) {
                 WHERE a.lessonid = :lessonid
                 ORDER BY u.lastname";
 } else {
-    $sql = "SELECT DISTINCT u.id, u.*
+    $sql = "SELECT DISTINCT $ufields
             FROM {user} u,
                  {lesson_attempts} a
             WHERE a.lessonid = :lessonid and
index 7a05c5a..655cb5f 100644 (file)
@@ -39,7 +39,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
         // Define each element separated
         $quiz = new backup_nested_element('quiz', array('id'), array(
             'name', 'intro', 'introformat', 'timeopen',
-            'timeclose', 'optionflags', 'penaltyscheme', 'attempts',
+            'timeclose', 'optionflags', 'penaltyscheme', 'attempts_number',
             'attemptonlast', 'grademethod', 'decimalpoints', 'questiondecimalpoints',
             'review', 'questionsperpage', 'shufflequestions', 'shuffleanswers',
             'questions', 'sumgrades', 'grade', 'timecreated',
@@ -119,6 +119,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
         }
 
         // Define source alias
+        $quiz->set_source_alias('attempts', 'attempts_number');
         $grade->set_source_alias('grade', 'gradeval');
         $attempt->set_source_alias('attempt', 'attemptnum');
 
index 30f8baa..7222241 100644 (file)
@@ -67,6 +67,13 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st
 
         $data->questions = $this->questions_recode_layout($data->questions);
 
+        // quiz->attempts can come both in data->attempts and
+        // data->attempts_number, handle both. MDL-26229
+        if (isset($data->attempts_number)) {
+            $data->attempts = $data->attempts_number;
+            unset($data->attempts_number);
+        }
+
         // insert the quiz record
         $newitemid = $DB->insert_record('quiz', $data);
         // immediately after inserting "activity" record, call this
index e085c18..a27bc32 100644 (file)
@@ -916,7 +916,8 @@ function quiz_get_reviewoptions($quiz, $attempt, $context) {
     }
 
     // Show a link to the comment box only for closed attempts
-    if ($attempt->timefinish && has_capability('mod/quiz:grade', $context)) {
+    if (!empty($attempt->id) && $attempt->timefinish &&
+            has_capability('mod/quiz:grade', $context)) {
         $options->questioncommentlink = new moodle_url('/mod/quiz/comment.php', array('attempt' => $attempt->id));
     }
 
@@ -1153,7 +1154,7 @@ function quiz_send_notification_emails($course, $quiz, $attempt, $context, $cm)
     // send confirmation if required
     if ($sendconfirm) {
         // send the email and update stats
-        switch (quiz_send_confirmation($a)) {
+        switch (!empty(quiz_send_confirmation($a))) {
             case true:
                 $emailresult['good']++;
                 break;
index 0ef2a32..7a9b5e1 100644 (file)
@@ -202,7 +202,7 @@ class quiz_overview_report extends quiz_default_report {
         if (!$nostudents || ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)) {
 
             // Construct the SQL
-            $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')').' AS uniqueid, ';
+            $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, 0)').' AS uniqueid, ';
             if ($qmsubselect) {
                 $fields .=
                     "(CASE " .
index b73ccfd..01662ff 100644 (file)
@@ -185,7 +185,7 @@ class quiz_responses_report extends quiz_default_report {
             $hasfeedback = quiz_has_feedback($quiz);
 
             // Construct the SQL
-            $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')').' AS concattedid, ';
+            $fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, 0)').' AS concattedid, ';
             if ($qmsubselect) {
                 $fields .=
                     "(CASE " .
index a18e905..f1db6ec 100644 (file)
@@ -178,12 +178,6 @@ M.mod_scorm.init = function(Y, hide_nav, hide_toc, toc_title, window_name, launc
             if (alsowidth) {
                 scorm_layout_widget.setStyle('width', '');
                 var newwidth = scorm_get_htmlelement_size('content', 'width');
-                if (newwidth > 600) {
-                    scorm_layout_widget.setStyle('width', newwidth+'px');
-                }
-                else {
-                    scorm_layout_widget.setStyle('width', '600px');
-                }
             }
             // make sure that the max width of the TOC doesn't go to far
 
index 388c201..3a92877 100644 (file)
@@ -6,7 +6,7 @@
 /////////////////////////////////////////////////////////////////////////////////
 
 
-$module->version  = 2011013100;   // The (date) version of this module
+$module->version  = 2011021400;   // The (date) version of this module
 $module->requires = 2010080300;   // The version of Moodle that is required
 $module->cron     = 300;            // How often should cron check this module (seconds)?
 
index 13f02d8..df614c9 100644 (file)
@@ -264,5 +264,49 @@ function xmldb_workshop_upgrade($oldversion) {
         upgrade_mod_savepoint(true, 2010111200, 'workshop');
     }
 
+    /**
+     * Check the course_module integrity - see MDL-26312 for details
+     *
+     * Because of a bug in Workshop upgrade code, multiple workshop course_modules can
+     * potentially point to a single workshop instance. The chance is pretty low as in most cases,
+     * the upgrade failed. But under certain circumstances, workshop could be upgraded with
+     * this data integrity issue. We want to detect it now and let the admin know.
+     */
+    if ($oldversion < 2011021100) {
+        $sql = "SELECT cm.id, cm.course, cm.instance
+                  FROM {course_modules} cm
+                 WHERE cm.module IN (SELECT id
+                                       FROM {modules}
+                                      WHERE name = ?)";
+        $rs = $DB->get_recordset_sql($sql, array('workshop'));
+        $map = array(); // returned stdClasses by instance id
+        foreach ($rs as $cm) {
+            $map[$cm->instance][$cm->id] = $cm;
+        }
+        $rs->close();
+
+        $problems = array();
+        foreach ($map as $instanceid => $cms) {
+            if (count($cms) > 1) {
+                $problems[] = 'workshop instance ' . $instanceid . ' referenced by course_modules ' . implode(', ', array_keys($cms));
+            }
+        }
+        if ($problems) {
+            echo $OUTPUT->notification('¡Ay, caramba! Data integrity corruption has been detected in your workshop ' . PHP_EOL .
+                'module database tables. This might be caused by a bug in workshop upgrade code. ' . PHP_EOL .
+                'Please report this issue immediately in workshop module support forum at ' . PHP_EOL .
+                'http://moodle.org so that we can help to fix this problem. Please copy and keep ' . PHP_EOL .
+                'following information for future reference:');
+            foreach ($problems as $problem) {
+                echo $OUTPUT->notification($problem);
+                upgrade_log(UPGRADE_LOG_NOTICE, 'mod_workshop', 'course_modules integrity problem', $problem);
+            }
+        }
+
+        unset($problems);
+        unset($map);
+        upgrade_mod_savepoint(true, 2011021100, 'workshop');
+    }
+
     return true;
 }
index 8bc77f4..4b79846 100644 (file)
@@ -154,13 +154,13 @@ function workshop_upgrade_module_instances() {
 
     upgrade_set_timeout();
     $moduleid = $DB->get_field('modules', 'id', array('name' => 'workshop'), MUST_EXIST);
-    $rs = $DB->get_recordset_select('workshop_old', 'newid IS NULL');
+    $rs = $DB->get_recordset_select('workshop_old', 'newid IS NULL', null, 'id');
     foreach ($rs as $old) {
         $new = workshop_upgrade_transform_instance($old);
-        $newid = $DB->insert_record('workshop', $new, true, true);
-        $DB->set_field('course_modules', 'instance', $newid, array('module' => $moduleid, 'instance' => $old->id));
+        $new->id = $old->id;
+        $DB->import_record('workshop', $new);
         $DB->set_field('workshop_old', 'newplugin', 'workshop', array('id' => $old->id));
-        $DB->set_field('workshop_old', 'newid', $newid, array('id' => $old->id));
+        $DB->set_field('workshop_old', 'newid', $new->id, array('id' => $old->id));
     }
     $rs->close();
 }
@@ -214,24 +214,6 @@ function workshop_upgrade_transform_instance(stdClass $old) {
     return $new;
 }
 
-/**
- * Returns the list of new workshop instances ids
- *
- * @return array (int)oldid => (int)newid
- */
-function workshop_upgrade_workshop_id_mappings() {
-    global $DB;
-
-    $oldrecords = $DB->get_records('workshop_old', null, 'id', 'id,newid');
-    $newids = array();
-    foreach ($oldrecords as $oldid => $oldrecord) {
-        if ($oldrecord->id and $oldrecord->newid) {
-            $newids[$oldid] = $oldrecord->newid;
-        }
-    }
-    return $newids;
-}
-
 /**
  * Copies records from workshop_submissions_old into workshop_submissions. Can be called after all workshop module instances
  * were correctly migrated and new ids are filled in workshop_old
@@ -242,19 +224,18 @@ function workshop_upgrade_submissions() {
     global $CFG, $DB;
 
     upgrade_set_timeout();
-    $newworkshopids = workshop_upgrade_workshop_id_mappings();
 
-    // list of teachers in every workshop: array of (int)oldworkshopid => array of (int)userid => notused
+    // list of teachers in every workshop: array of (int)workshopid => array of (int)userid => notused
     $workshopteachers = array();
 
     $rs = $DB->get_recordset_select('workshop_submissions_old', 'newid IS NULL');
     foreach ($rs as $old) {
         if (!isset($workshopteachers[$old->workshopid])) {
-            $cm = get_coursemodule_from_instance('workshop', $newworkshopids[$old->workshopid], 0, false, MUST_EXIST);
+            $cm = get_coursemodule_from_instance('workshop', $old->workshopid, 0, false, MUST_EXIST);
             $context = get_context_instance(CONTEXT_MODULE, $cm->id);
             $workshopteachers[$old->workshopid] = get_users_by_capability($context, 'mod/workshop:manage', 'u.id');
         }
-        $new = workshop_upgrade_transform_submission($old, $newworkshopids[$old->workshopid], $workshopteachers[$old->workshopid]);
+        $new = workshop_upgrade_transform_submission($old, $old->workshopid, $workshopteachers[$old->workshopid]);
         $newid = $DB->insert_record('workshop_submissions', $new, true, true);
         $DB->set_field('workshop_submissions_old', 'newplugin', 'submissions', array('id' => $old->id));
         $DB->set_field('workshop_submissions_old', 'newid', $newid, array('id' => $old->id));
@@ -343,17 +324,17 @@ function workshop_upgrade_assessments() {
     global $CFG, $DB, $OUTPUT;
 
     upgrade_set_timeout();
-    $newworkshopids     = workshop_upgrade_workshop_id_mappings();
+
     $newsubmissionids   = workshop_upgrade_submission_id_mappings();
     $teacherweights     = workshop_upgrade_legacy_teacher_weights();
 
-    // list of teachers in every workshop: array of (int)oldworkshopid => array of (int)userid => notused
+    // list of teachers in every workshop: array of (int)workshopid => array of (int)userid => notused
     $workshopteachers   = array();
 
     $rs = $DB->get_recordset_select('workshop_assessments_old', 'newid IS NULL');
     foreach ($rs as $old) {
         if (!isset($workshopteachers[$old->workshopid])) {
-            $cm = get_coursemodule_from_instance('workshop', $newworkshopids[$old->workshopid], 0, false, MUST_EXIST);
+            $cm = get_coursemodule_from_instance('workshop', $old->workshopid, 0, false, MUST_EXIST);
             $context = get_context_instance(CONTEXT_MODULE, $cm->id);
             $workshopteachers[$old->workshopid] = get_users_by_capability($context, 'mod/workshop:manage', 'u.id');
         }
index 3376c86..1e7237e 100644 (file)
@@ -49,11 +49,10 @@ function workshopform_accumulative_upgrade_legacy() {
                  WHERE workshopid $workshopids
                        AND newid IS NULL";
         $rs = $DB->get_recordset_sql($sql, $params);
-        $newworkshopids = workshop_upgrade_workshop_id_mappings();
         // prepare system (global) scales to replace the legacy in-built ones
         $newscaleids = workshopform_accumulative_upgrade_scales();
         foreach ($rs as $old) {
-            $new = workshopform_accumulative_upgrade_element($old, $newscaleids, $newworkshopids[$old->workshopid]);
+            $new = workshopform_accumulative_upgrade_element($old, $newscaleids, $old->workshopid);
             $newid = $DB->insert_record('workshopform_accumulative', $new);
             $DB->set_field('workshop_elements_old', 'newplugin', 'accumulative', array('id' => $old->id));
             $DB->set_field('workshop_elements_old', 'newid', $newid, array('id' => $old->id));
index f1e537b..ee45f2a 100644 (file)
@@ -49,9 +49,8 @@ function workshopform_comments_upgrade_legacy() {
                  WHERE workshopid $workshopids
                        AND newid IS NULL";
         $rs = $DB->get_recordset_sql($sql, $params);
-        $newworkshopids = workshop_upgrade_workshop_id_mappings();
         foreach ($rs as $old) {
-            $new = workshopform_comments_upgrade_element($old, $newworkshopids[$old->workshopid]);
+            $new = workshopform_comments_upgrade_element($old, $old->workshopid);
             $newid = $DB->insert_record('workshopform_comments', $new);
             $DB->set_field('workshop_elements_old', 'newplugin', 'comments', array('id' => $old->id));
             $DB->set_field('workshop_elements_old', 'newid', $newid, array('id' => $old->id));
index f7b07f8..e27d61e 100644 (file)
@@ -53,11 +53,10 @@ function workshopform_numerrors_upgrade_legacy() {
                  WHERE workshopid $workshopids
                        AND newid IS NULL";
         $rs = $DB->get_recordset_sql($sql, $params);
-        $newworkshopids = workshop_upgrade_workshop_id_mappings();
         foreach ($rs as $old) {
             // process the information about mapping
             $newmapping = new stdclass();
-            $newmapping->workshopid = $newworkshopids[$old->workshopid];
+            $newmapping->workshopid = $old->workshopid;
             $newmapping->nonegative = $old->elementno;
             $newmapping->grade = $old->maxscore;
             if ($old->maxscore > 0) {
@@ -70,7 +69,7 @@ function workshopform_numerrors_upgrade_legacy() {
             $DB->insert_record('workshopform_numerrors_map', $newmapping);
             // process the information about the element itself
             if (trim($old->description) and $old->description <> '@@ GRADE_MAPPING_ELEMENT @@') {
-                $new = workshopform_numerrors_upgrade_element($old, $newworkshopids[$old->workshopid]);
+                $new = workshopform_numerrors_upgrade_element($old, $old->workshopid);
                 $newid = $DB->insert_record('workshopform_numerrors', $new);
             } else {
                 $newid = 0;
index 40df767..50c4fbd 100644 (file)
@@ -57,25 +57,24 @@ function workshopform_rubric_upgrade_legacy_criterion() {
                  WHERE workshopid $workshopids
                        AND newid IS NULL";
         $rs = $DB->get_recordset_sql($sql, $params);
-        $newworkshopids = workshop_upgrade_workshop_id_mappings();
-        $newdimensionids = array(); // (int)oldworkshopid => (int)dimensionid
+        $newdimensionids = array(); // (int)workshopid => (int)dimensionid
         foreach ($rs as $old) {
             // create rubric criterion and the configuration if necessary
             if (!isset($newdimensionids[$old->workshopid])) {
-                if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => 1))) {
+                if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $old->workshopid, 'sort' => 1))) {
                     $newdimension = new stdclass();
-                    $newdimension->workshopid = $newworkshopids[$old->workshopid];
+                    $newdimension->workshopid = $old->workshopid;
                     $newdimension->sort = 1;
                     $newdimension->description = trim(get_string('dimensionnumber', 'workshopform_rubric', ''));
                     $newdimension->descriptionformat = FORMAT_HTML;
                     $newdimensionids[$old->workshopid] = $DB->insert_record('workshopform_rubric', $newdimension);
                 } else {
                     $newdimensionids[$old->workshopid] = $DB->get_field('workshopform_rubric', 'id',
-                                                                array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => 1));
+                                                                array('workshopid' => $old->workshopid, 'sort' => 1));
                 }
-                if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $newworkshopids[$old->workshopid]))) {
+                if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $old->workshopid))) {
                     $newconfig = new stdclass();
-                    $newconfig->workshopid = $newworkshopids[$old->workshopid];
+                    $newconfig->workshopid = $old->workshopid;
                     $newconfig->layout = 'list';
                     $DB->insert_record('workshopform_rubric_config', $newconfig);
                 }
@@ -148,27 +147,26 @@ function workshopform_rubric_upgrade_legacy_rubric() {
                        AND r.newid IS NULL
               ORDER BY e.workshopid, e.elementno, r.rubricno";
         $rs = $DB->get_recordset_sql($sql, $params);
-        $newworkshopids     = workshop_upgrade_workshop_id_mappings();  // array of (int)oldworkshopid => (int)newworkshopid
-        $newdimensionids    = array();  // (int)oldworkshopid => (int)elementno => (int)dimensionid
+        $newdimensionids    = array();  // (int)workshopid => (int)elementno => (int)dimensionid
         $newlevelids        = array();  // (int)oldrubricid => (int)newlevelid
         $prevelement        = null;
         foreach ($rs as $old) {
             // create rubric criterion and the configuration if necessary
             if (!isset($newdimensionids[$old->workshopid]) or !isset($newdimensionids[$old->workshopid][$old->esort])) {
-                if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => $old->esort))) {
+                if (!$DB->record_exists('workshopform_rubric', array('workshopid' => $old->workshopid, 'sort' => $old->esort))) {
                     $newdimension = new stdclass();
-                    $newdimension->workshopid = $newworkshopids[$old->workshopid];
+                    $newdimension->workshopid = $old->workshopid;
                     $newdimension->sort = $old->esort;
                     $newdimension->description = $old->edesc;
                     $newdimension->descriptionformat = FORMAT_HTML;
                     $newdimensionids[$old->workshopid][$old->esort] = $DB->insert_record('workshopform_rubric', $newdimension);
                 } else {
                     $newdimensionids[$old->workshopid][$old->esort] = $DB->get_field('workshopform_rubric', 'id',
-                                                                    array('workshopid' => $newworkshopids[$old->workshopid], 'sort' => $old->esort));
+                                                                    array('workshopid' => $old->workshopid, 'sort' => $old->esort));
                 }
-                if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $newworkshopids[$old->workshopid]))) {
+                if (!$DB->record_exists('workshopform_rubric_config', array('workshopid' => $old->workshopid))) {
                     $newconfig = new stdclass();
-                    $newconfig->workshopid = $newworkshopids[$old->workshopid];
+                    $newconfig->workshopid = $old->workshopid;
                     $newconfig->layout = 'grid';
                     $DB->insert_record('workshopform_rubric_config', $newconfig);
                 }
index 9cc8d88..11348c1 100644 (file)
@@ -29,6 +29,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$module->version  = 2010111200;
-$module->requires = 2010111002;  // Requires this Moodle version
+$module->version  = 2011021100;
+$module->requires = 2011020900;  // Requires this Moodle version
 //$module->cron     = 60;
index 113329a..06cce6c 100644 (file)
@@ -337,6 +337,102 @@ class qformat_gift_test extends UnitTestCase {
         $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
     }
 
+    public function test_import_multichoice_multi() {
+        $gift = "
+// multiple choice, multiple response with specified feedback for right and wrong answers
+::colours:: What's between orange and green in the spectrum?
+{
+    ~%50%yellow # right; good!
+    ~%-100%red # [html]wrong
+    ~%50%off-beige # right; good!
+    ~%-100%[plain]blue # wrong
+}";
+        $lines = preg_split('/[\\n\\r]/', str_replace("\r\n", "\n", $gift));
+
+        $importer = new qformat_gift();
+        $q = $importer->readquestion($lines);
+
+        $expectedq = (object) array(
+            'name' => 'colours',
+            'questiontext' => "What's between orange and green in the spectrum?",
+            'questiontextformat' => FORMAT_MOODLE,
+            'generalfeedback' => '',
+            'generalfeedbackformat' => FORMAT_MOODLE,
+            'qtype' => 'multichoice',
+            'defaultgrade' => 1,
+            'penalty' => 0.1,
+            'length' => 1,
+            'single' => 0,
+            'shuffleanswers' => '1',
+            'answernumbering' => 'abc',
+            'correctfeedback' => array(
+                'text' => '',
+                'format' => FORMAT_MOODLE,
+                'files' => array(),
+            ),
+            'partiallycorrectfeedback' => array(
+                'text' => '',
+                'format' => FORMAT_MOODLE,
+                'files' => array(),
+            ),
+            'incorrectfeedback' => array(
+                'text' => '',
+                'format' => FORMAT_MOODLE,
+                'files' => array(),
+            ),
+            'answer' => array(
+                0 => array(
+                    'text' => 'yellow',
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+                1 => array(
+                    'text' => 'red',
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+                2 => array(
+                    'text' => 'off-beige',
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+                3 => array(
+                    'text' => 'blue',
+                    'format' => FORMAT_PLAIN,
+                    'files' => array(),
+                ),
+            ),
+            'fraction' => array(0.5, -1, 0.5, -1),
+            'feedback' => array(
+                0 => array(
+                    'text' => 'right; good!',
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+                1 => array(
+                    'text' => "wrong",
+                    'format' => FORMAT_HTML,
+                    'files' => array(),
+                ),
+                2 => array(
+                    'text' => "right; good!",
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+                3 => array(
+                    'text' => "wrong",
+                    'format' => FORMAT_MOODLE,
+                    'files' => array(),
+                ),
+            ),
+        );
+
+        // Repeated test for better failure messages.
+        $this->assertEqual($expectedq->answer, $q->answer);
+        $this->assertEqual($expectedq->feedback, $q->feedback);
+        $this->assert(new CheckSpecifiedFieldsExpectation($expectedq), $q);
+    }
+
     public function test_export_multichoice() {
         $qdata = (object) array(
             'id' => 666 ,
index 7c5b745..36bf82c 100644 (file)
@@ -15,7 +15,7 @@ M.core_question_engine.init_form = function(Y, form) {
     Y.one(form).setAttribute('autocomplete', 'off');
     Y.on('key', function (e) {
         if (!e.target.test('a') && !e.target.test('input[type=submit]') &&
-                !e.target.test('input[type=img]')) {
+                !e.target.test('input[type=img]') && !e.target.test('textarea')) {
             e.preventDefault();
         }
     }, form, 'press:13');
index 5d83737..ebc37b8 100644 (file)
@@ -1044,7 +1044,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
         $penaltygrp = array();
         $penaltygrp[] =& $mform->createElement('text', 'unitpenalty', get_string('unitpenalty', 'qtype_numerical') ,
                 array('size' => 6));
-        $unitgradingtypes = array('1' => get_string('decfractionofquestiongrade', 'qtype_numerical'), '2' => get_string('decfractionofresponsegrade', 'qtype_numerical'));
+        $unitgradingtypes = array('1' => get_string('decfractionofresponsegrade', 'qtype_numerical'), '2' => get_string('decfractionofquestiongrade', 'qtype_numerical'));
         $penaltygrp[] =& $mform->createElement('select', 'unitgradingtypes', '' , $unitgradingtypes );
         $mform->addGroup($penaltygrp, 'penaltygrp', get_string('unitpenalty', 'qtype_numerical'),' ' , false);
         $multichoicedisplaygrp = array();
@@ -1151,6 +1151,7 @@ class question_numerical_qtype extends question_shortanswer_qtype {
                         break;
                     case 2 : // NUMERICALQUESTIONUNITTEXTDISPLAY
                         $default_values['unitrole'] = 1 ;
+                        break;
                     case 3 : // NUMERICALQUESTIONUNITNODISPLAY
                         $default_values['unitrole'] = 0 ;
                         //  $default_values['showunits1'] = $question->options->showunits ;
index aaaec82..1cda88b 100644 (file)
@@ -47,9 +47,9 @@ class restore_qtype_random_plugin extends restore_qtype_plugin {
         $answer = $state->answer;
         $result = '';
         // randomxx-yy answer format
-        if (preg_match('~^random([0-9]+)-(.*)$~', $answer, $matches)) {
+        if (preg_match('~^random([0-9]+)-~', $answer, $matches)) {
             $questionid = $matches[1];
-            $subanswer  = $matches[2];
+            $subanswer = substr($answer, strlen('random' . $questionid . '-'));
             $newquestionid = $this->get_mappingid('question', $questionid);
             $questionqtype = $DB->get_field('question', 'qtype', array('id' => $newquestionid));
             // Delegate subanswer recode to proper qtype, faking one question_states record
index 6d99083..72c5aab 100644 (file)
@@ -232,7 +232,7 @@ class random_qtype extends default_questiontype {
         /// the other question types in that it now only needs one response
         /// record per question.
         global $QTYPES, $DB, $OUTPUT;
-        if (!preg_match('~^random([0-9]+)-(.*)$~', $state->responses[''], $answerregs)) {
+        if (!preg_match('~^random([0-9]+)-~', $state->responses[''], $matches)) {
             if (empty($state->responses[''])) {
                 // This is the case if there weren't enough questions available in the category.
                 $question->questiontext = '<span class="notifyproblem">'.
@@ -250,15 +250,16 @@ class random_qtype extends default_questiontype {
                 echo $OUTPUT->notification("Wrapped state missing");
             }
         } else {
-            if (!$wrappedquestion = $DB->get_record('question', array('id' => $answerregs[1]))) {
+            $questionid = $matches[1];
+            if (!$wrappedquestion = $DB->get_record('question', array('id' => $questionid))) {
                 // The teacher must have deleted this question by mistake
                 // Convert it into a description type question with an explanation to the student
                 $wrappedquestion = clone($question);
-                $wrappedquestion->id = $answerregs[1];
+                $wrappedquestion->id = $questionid;
                 $wrappedquestion->questiontext = get_string('questiondeleted', 'quiz');
                 $wrappedquestion->qtype = 'missingtype';
             }
-            $state->responses[''] = (false === $answerregs[2]) ? '' : $answerregs[2];
+            $state->responses[''] = substr($state->responses[''], strlen('random' . $questionid . '-'));
         }
 
         if (!$QTYPES[$wrappedquestion->qtype]
index 331529d..a1a2cb7 100644 (file)
@@ -141,10 +141,10 @@ $aggregatetoreturn = round($items[0]->rating->aggregate,1);
 // Output a dash if aggregation method == COUNT as the count is output next to the aggregate anyway
 if ($items[0]->rating->settings->aggregationmethod==RATING_AGGREGATE_COUNT or $items[0]->rating->count == 0) {
     $aggregatetoreturn = ' - ';
-} else if($rating->scaleid < 0) { //if its non-numeric scale
+} else if($items[0]->rating->settings->scale->id < 0) { //if its non-numeric scale
     //dont use the scale item if the aggregation method is sum as adding items from a custom scale makes no sense
     if ($items[0]->rating->settings->aggregationmethod!= RATING_AGGREGATE_SUM) {
-        $scalerecord = $DB->get_record('scale', array('id' => -$rating->scaleid));
+        $scalerecord = $DB->get_record('scale', array('id' => -$items[0]->rating->settings->scale->id));
         if ($scalerecord) {
             $scalearray = explode(',', $scalerecord->scale);
             $aggregatetoreturn = $scalearray[$aggregatetoreturn-1];
index 0edc42c..3619d4f 100755 (executable)
@@ -105,7 +105,21 @@ class repository_upload extends repository {
         if (empty($saveas_filename)) {
             $record->filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
         } else {
-            $record->filename = $saveas_filename;
+            $ext = '';
+            $match = array();
+            $filename = clean_param($_FILES[$elname]['name'], PARAM_FILE);
+            if (preg_match('/\.([a-z0-9]+)$/i', $filename, $match)) {
+                if (isset($match[1])) {
+                    $ext = $match[1];
+                }
+            }
+            $ext = !empty($ext) ? $ext : '';
+            if (preg_match('#\.(' . $ext . ')$#i', $saveas_filename)) {
+                // saveas filename contains file extension already
+                $record->filename = $saveas_filename;
+            } else {
+                $record->filename = $saveas_filename . '.' . $ext;
+            }
         }
 
         if ($this->mimetypes != '*') {
index 2228465..464638a 100644 (file)
@@ -93,18 +93,18 @@ foreach ($parts as $part) {
 
     if ($mimetype === 'text/css') {
         if ($version == 'moodle') {
-            $filecontent = preg_replace('/([a-z_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$frankenstyle.'/'.array_shift($bits).'/$1.$2', $filecontent);
+            $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$frankenstyle.'/'.array_shift($bits).'/$1.$2', $filecontent);
         } else if ($version == 'gallery') {
             // search for all images in gallery module CSS and serve them through the yui_image.php script
-            $filecontent = preg_replace('/([a-z_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$bits[0].'/'.$bits[1].'/$1.$2', $filecontent);
+            $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/'.$bits[0].'/'.$bits[1].'/$1.$2', $filecontent);
         } else {
             // First we need to remove relative paths to images. These are used by YUI modules to make use of global assets.
             // I've added this as a separate regex so it can be easily removed once
             // YUI standardise there CSS methods
-            $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z_-]+)\.(png|gif)#', '$2.$3', $filecontent);
+            $filecontent = preg_replace('#(\.\./\.\./\.\./\.\./assets/skins/sam/)?([a-z0-9_-]+)\.(png|gif)#', '$2.$3', $filecontent);
 
             // search for all images in yui2 CSS and serve them through the yui_image.php script
-            $filecontent = preg_replace('/([a-z_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/$1.$2', $filecontent);
+            $filecontent = preg_replace('/([a-z0-9_-]+)\.(png|gif)/', 'yui_image.php?file='.$version.'/$1.$2', $filecontent);
         }
     }
 
index 2151512..6114924 100644 (file)
@@ -194,7 +194,9 @@ function useredit_shared_definition(&$mform, $editoroptions = null) {
     $mform->addElement('text', 'city', get_string('city'), 'maxlength="120" size="21"');
     $mform->setType('city', PARAM_MULTILANG);
     $mform->addRule('city', $strrequired, 'required', null, 'client');
-
+    if (!empty($CFG->defaultcity)) {
+        $mform->setDefault('city', $CFG->defaultcity);
+    }
 
     $choices = get_string_manager()->get_list_of_countries();
     $choices= array(''=>get_string('selectacountry').'...') + $choices;
index da41cb3..a373051 100644 (file)
@@ -42,9 +42,12 @@ require_once($CFG->libdir.'/filelib.php');
 $userid = optional_param('id', 0, PARAM_INT);
 $edit   = optional_param('edit', null, PARAM_BOOL);    // Turn editing on and off
 
+$PAGE->set_url('/user/profile.php', array('id'=>$userid));
+
 if (!empty($CFG->forceloginforprofiles)) {
     require_login();
     if (isguestuser()) {
+        $SESSION->wantsurl = $PAGE->url->out(false);
         redirect(get_login_url());
     }
 } else if (!empty($CFG->forcelogin)) {
@@ -106,8 +109,6 @@ if (has_capability('moodle/user:viewhiddendetails', $context)) {
 // Start setting up the page
 $strpublicprofile = get_string('publicprofile');
 
-$params = array('id'=>$userid);
-$PAGE->set_url('/user/profile.php', $params);
 $PAGE->blocks->add_region('content');
 $PAGE->set_subpage($currentpage->id);
 $PAGE->set_title(fullname($user).": $strpublicprofile");
index e70df1c..1cd701b 100644 (file)
@@ -40,8 +40,7 @@ if ($courseid == SITEID) {   // Since Moodle 2.0 all site-level profiles are sho
     redirect($CFG->wwwroot.'/user/profile.php?id='.$id);  // Immediate redirect
 }
 
-$url = new moodle_url('/user/view.php', array('id'=>$id,'course'=>$courseid));
-$PAGE->set_url($url);
+$PAGE->set_url('/user/view.php', array('id'=>$id,'course'=>$courseid));
 
 $user = $DB->get_record('user', array('id'=>$id), '*', MUST_EXIST);
 $course = $DB->get_record('course', array('id'=>$courseid), '*', MUST_EXIST);
@@ -57,6 +56,14 @@ if (isguestuser($user)) {
     print_error('invaliduserid');
 }
 
+if (!empty($CFG->forceloginforprofiles)) {
+    require_login(); // we can not log in to course due to the parent hack bellow
+    if (isguestuser()) {
+        $SESSION->wantsurl = $PAGE->url->out(false);
+        redirect(get_login_url());
+    }
+}
+
 $PAGE->set_context($coursecontext);
 $PAGE->set_course($course);
 $PAGE->set_pagetype('course-view-' . $course->format);  // To get the blocks exactly like the course
@@ -76,7 +83,7 @@ if (!$currentuser
 } else {
     // normal course
     require_login($course);
-    // what to do with users temporary accessing this course? shoudl they see the details?
+    // what to do with users temporary accessing this course? should they see the details?
 }
 
 $strpersonalprofile = get_string('personalprofile');
index bb4daa9..508da1b 100644 (file)
@@ -29,7 +29,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$version = 2011020900;  // YYYYMMDD   = date of the last version bump
+$version = 2011020900.08;  // YYYYMMDD   = date of the last version bump
                         //         XX = daily increments
 
 $release = '2.0.1+ (Build: 20110209)';  // Human-friendly version name