Merge branch 'w51_MDL-37282_m25_tinymce358' of git://github.com/skodak/moodle
authorDan Poltawski <dan@moodle.com>
Tue, 8 Jan 2013 06:26:52 +0000 (14:26 +0800)
committerDan Poltawski <dan@moodle.com>
Tue, 8 Jan 2013 06:26:52 +0000 (14:26 +0800)
180 files changed:
admin/filters.php
admin/settings/security.php
admin/tool/uploaduser/index.php
admin/user.php
auth/db/auth.php
auth/db/cli/sync_users.php
auth/db/db/install.php
auth/db/lang/en/auth_db.php
auth/db/tests/db_test.php [new file with mode: 0644]
auth/db/version.php
auth/shibboleth/index_form.html
auth/shibboleth/lang/en/auth_shibboleth.php
backup/backupfilesedit.php
backup/converter/moodle1/lib.php
backup/converter/moodle1/tests/lib_test.php
backup/moodle2/restore_stepslib.php
blocks/completionstatus/block_completionstatus.php
blocks/completionstatus/db/access.php
blocks/completionstatus/db/upgrade.php [new file with mode: 0644]
blocks/completionstatus/lang/en/block_completionstatus.php
blocks/completionstatus/version.php
blocks/course_summary/block_course_summary.php
blocks/course_summary/db/access.php
blocks/course_summary/db/upgrade.php [new file with mode: 0644]
blocks/course_summary/lang/en/block_course_summary.php
blocks/course_summary/version.php
blocks/dock.js
blocks/glossary_random/block_glossary_random.php
blocks/glossary_random/db/access.php
blocks/glossary_random/db/upgrade.php [new file with mode: 0644]
blocks/glossary_random/lang/en/block_glossary_random.php
blocks/glossary_random/version.php
blocks/mentees/block_mentees.php
blocks/mentees/db/access.php
blocks/mentees/db/upgrade.php [new file with mode: 0644]
blocks/mentees/lang/en/block_mentees.php
blocks/mentees/version.php
blocks/news_items/block_news_items.php
blocks/news_items/db/access.php
blocks/news_items/db/upgrade.php [new file with mode: 0644]
blocks/news_items/lang/en/block_news_items.php
blocks/news_items/version.php
blocks/online_users/block_online_users.php
blocks/online_users/db/access.php
blocks/online_users/db/upgrade.php [new file with mode: 0644]
blocks/online_users/lang/en/block_online_users.php
blocks/online_users/version.php
blocks/rss_client/backup/moodle2/restore_rss_client_stepslib.php
blocks/selfcompletion/block_selfcompletion.php
blocks/selfcompletion/db/access.php
blocks/selfcompletion/db/upgrade.php [new file with mode: 0644]
blocks/selfcompletion/lang/en/block_selfcompletion.php
blocks/selfcompletion/version.php
blocks/tags/edit_form.php
blocks/tags/lang/en/block_tags.php
blog/rsslib.php
cache/classes/loaders.php
cache/stores/static/lib.php
calendar/lib.php
calendar/managesubscriptions.php
calendar/renderer.php
comment/comment_post.php
course/category.php
course/dnduploadlib.php
course/lib.php
course/switchrole.php
enrol/category/cli/sync.php
enrol/category/lib.php
enrol/category/locallib.php
enrol/category/tests/sync_test.php
enrol/cohort/ajax.php
enrol/cohort/cli/sync.php
enrol/cohort/edit.php
enrol/cohort/lib.php
enrol/cohort/locallib.php
enrol/cohort/tests/sync_test.php
enrol/database/cli/sync.php
enrol/database/lib.php
enrol/database/tests/sync_test.php
enrol/ldap/lib.php
enrol/locallib.php
enrol/otherusers.php
enrol/renderer.php
files/renderer.php
filter/activitynames/db/install.php
filter/algebra/algebradebug.php
filter/algebra/pix.php
filter/manage.php
filter/mediaplugin/db/install.php
filter/tex/displaytex.php
filter/tex/pix.php
filter/tex/texdebug.php
filter/upgrade.txt
grade/edit/tree/lib.php
group/externallib.php
lang/en/admin.php
lang/en/block.php
lang/en/error.php
lang/en/moodle.php
lang/en/question.php
lang/en/repository.php
lib/adminlib.php
lib/authlib.php
lib/db/install.xml
lib/db/upgrade.php
lib/ddl/database_manager.php
lib/deprecatedlib.php
lib/editor/tinymce/plugins/dragmath/lib.php
lib/editor/tinymce/plugins/moodleemoticon/lib.php
lib/editor/tinymce/plugins/spellchecker/changelog.txt
lib/editor/tinymce/plugins/spellchecker/classes/GoogleSpell.php
lib/editor/tinymce/plugins/spellchecker/readme_moodle.txt
lib/editor/tinymce/upgrade.txt
lib/filebrowser/file_info_context_course.php
lib/filebrowser/file_info_context_coursecat.php
lib/filelib.php
lib/filterlib.php
lib/moodlelib.php
lib/navigationlib.php
lib/outputlib.php
lib/outputrenderers.php
lib/outputrequirementslib.php
lib/pluginlib.php
lib/rsslib.php
lib/tests/authlib_test.php [new file with mode: 0644]
lib/tests/filter_test.php
lib/tests/textlib_test.php
lib/textlib.class.php
lib/weblib.php
login/change_password.php
login/change_password_form.php
login/forgot_password.php
login/index.php
login/unlock_account.php [new file with mode: 0644]
mnet/lib.php
mod/assign/feedback/file/importziplib.php
mod/assign/feedback/file/locallib.php
mod/assign/feedback/offline/locallib.php
mod/assign/locallib.php
mod/assign/submission/comments/lib.php
mod/data/data.js
mod/data/edit.php
mod/data/field/file/field.class.php
mod/data/field/picture/field.class.php
mod/forum/db/services.php [new file with mode: 0644]
mod/forum/externallib.php [new file with mode: 0644]
mod/forum/lib.php
mod/forum/rsslib.php
mod/forum/subscribe.php
mod/forum/tests/externallib_test.php [new file with mode: 0644]
mod/forum/version.php
mod/quiz/lang/en/quiz.php
mod/quiz/mod_form.php
mod/quiz/settings.php
mod/scorm/report/reportlib.php
mod/upgrade.txt
mod/wiki/create.php
mod/wiki/filesedit.php
mod/wiki/pagelib.php
question/format/learnwise/format.php
question/format/learnwise/lang/en/qformat_learnwise.php
question/format/xml/format.php
question/preview.php
question/previewlib.php
question/type/multianswer/edit_multianswer_form.php
question/type/multianswer/question.php
question/type/multianswer/questiontype.php
question/type/multianswer/tests/helper.php
question/type/multianswer/tests/question_test.php
question/type/multianswer/tests/walkthrough_test.php
question/type/shortanswer/question.php
question/type/shortanswer/tests/question_test.php
report/outline/index.php
report/security/locallib.php
repository/s3/lib.php
tag/coursetags_add.php
theme/base/style/filemanager.css
user/files.php
user/profile.php
version.php

index c102666..64589cf 100644 (file)
@@ -35,7 +35,7 @@
     require_once($CFG->libdir . '/adminlib.php');
 
     $action = optional_param('action', '', PARAM_ALPHANUMEXT);
-    $filterpath = optional_param('filterpath', '', PARAM_PATH);
+    $filterpath = optional_param('filterpath', '', PARAM_SAFEDIR);
 
     require_login();
     $systemcontext = context_system::instance();
 
     case 'down':
         if (isset($filters[$filterpath])) {
-            $oldpos = $filters[$filterpath]->sortorder;
-            if ($oldpos <= count($filters)) {
-                filter_set_global_state($filterpath, $filters[$filterpath]->active, $oldpos + 1);
-            }
+            filter_set_global_state($filterpath, $filters[$filterpath]->active, 1);
         }
         break;
 
     case 'up':
         if (isset($filters[$filterpath])) {
             $oldpos = $filters[$filterpath]->sortorder;
-            if ($oldpos >= 1) {
-                filter_set_global_state($filterpath, $filters[$filterpath]->active, $oldpos - 1);
-            }
+            filter_set_global_state($filterpath, $filters[$filterpath]->active, -1);
         }
         break;
 
     case 'delete':
-        if (!empty($filternames[$filterpath])) {
-            $filtername = $filternames[$filterpath];
-        } else {
-            $filtername = $filterpath;
-        }
-
-        if (substr($filterpath, 0, 4) == 'mod/') {
-            $mod = basename($filterpath);
-            $a = new stdClass;
-            $a->filter = $filtername;
-            $a->module = get_string('modulename', $mod);
-            print_error('cannotdeletemodfilter', 'admin', $returnurl, $a);
-        }
-
         // If not yet confirmed, display a confirmation message.
         if (!optional_param('confirm', '', PARAM_BOOL)) {
+            $filtername = filter_get_name($filterpath);
+
             $title = get_string('deletefilterareyousure', 'admin', $filtername);
             echo $OUTPUT->header();
             echo $OUTPUT->heading($title);
         }
 
         // Do the deletion.
-        $title = get_string('deletingfilter', 'admin', $filtername);
+        $title = get_string('deletingfilter', 'admin', $filterpath);
         echo $OUTPUT->header();
         echo $OUTPUT->heading($title);
 
         filter_delete_all_for_filter($filterpath);
 
         $a = new stdClass;
-        $a->filter = $filtername;
-        $a->directory = $filterpath;
+        $a->filter = $filterpath;
+        $a->directory = "$CFG->dirroot/filter/$filterpath";
         echo $OUTPUT->box(get_string('deletefilterfiles', 'admin', $a), 'generalbox', 'notice');
         echo $OUTPUT->continue_button($returnurl);
         echo $OUTPUT->footer();
@@ -241,7 +224,7 @@ function get_table_row($filterinfo, $isfirstrow, $islastactive, $applytostrings)
     }
 
     // Disable/off/on
-    $select = new single_select(filters_action_url($filter, 'setstate'), 'newstate', $activechoices, $filterinfo->active, null, 'active' . basename($filter));
+    $select = new single_select(filters_action_url($filter, 'setstate'), 'newstate', $activechoices, $filterinfo->active, null, 'active' . $filter);
     $select->set_label(get_string('isactive', 'filters'), array('class' => 'accesshide'));
     $row[] = $OUTPUT->render($select);
 
@@ -263,25 +246,20 @@ function get_table_row($filterinfo, $isfirstrow, $islastactive, $applytostrings)
     $row[] = $updown;
 
     // Apply to strings.
-    $select = new single_select(filters_action_url($filter, 'setapplyto'), 'stringstoo', $applytochoices, $applytostrings, null, 'applyto' . basename($filter));
+    $select = new single_select(filters_action_url($filter, 'setapplyto'), 'stringstoo', $applytochoices, $applytostrings, null, 'applyto' . $filter);
     $select->set_label(get_string('applyto', 'filters'), array('class' => 'accesshide'));
     $select->disabled = $filterinfo->active == TEXTFILTER_DISABLED;
     $row[] = $OUTPUT->render($select);
 
     // Settings link, if required
     if (filter_has_global_settings($filter)) {
-        $row[] = '<a href="' . $CFG->wwwroot . '/' . $CFG->admin . '/settings.php?section=filtersetting' .
-                str_replace('/', '',$filter) . '">' . get_string('settings') . '</a>';
+        $row[] = '<a href="' . $CFG->wwwroot . '/' . $CFG->admin . '/settings.php?section=filtersetting' . $filter . '">' . get_string('settings') . '</a>';
     } else {
         $row[] = '';
     }
 
     // Delete
-    if (substr($filter, 0, 4) != 'mod/') {
-        $row[] = '<a href="' . filters_action_url($filter, 'delete') . '">' . get_string('delete') . '</a>';
-    } else {
-        $row[] = '';
-    }
+    $row[] = '<a href="' . filters_action_url($filter, 'delete') . '">' . get_string('delete') . '</a>';
 
     return $row;
 }
index 189bf87..94b379d 100644 (file)
@@ -59,6 +59,11 @@ if ($hassiteconfig) { // speedup for non-admins, add all caps used on this page
     $temp->add(new admin_setting_configcheckbox('cronclionly', new lang_string('cronclionly', 'admin'), new lang_string('configcronclionly', 'admin'), 0));
     $temp->add(new admin_setting_configpasswordunmask('cronremotepassword', new lang_string('cronremotepassword', 'admin'), new lang_string('configcronremotepassword', 'admin'), ''));
 
+    $options = array(0=>get_string('no'), 3=>3, 5=>5, 7=>7, 10=>10, 20=>20, 30=>30, 50=>50, 100=>100);
+    $temp->add(new admin_setting_configselect('lockoutthreshold', new lang_string('lockoutthreshold', 'admin'), new lang_string('lockoutthreshold_desc', 'admin'), 0, $options));
+    $temp->add(new admin_setting_configduration('lockoutwindow', new lang_string('lockoutwindow', 'admin'), new lang_string('lockoutwindow_desc', 'admin'), 60*30));
+    $temp->add(new admin_setting_configduration('lockoutduration', new lang_string('lockoutduration', 'admin'), new lang_string('lockoutduration_desc', 'admin'), 60*30));
+
     $temp->add(new admin_setting_configcheckbox('passwordpolicy', new lang_string('passwordpolicy', 'admin'), new lang_string('configpasswordpolicy', 'admin'), 1));
     $temp->add(new admin_setting_configtext('minpasswordlength', new lang_string('minpasswordlength', 'admin'), new lang_string('configminpasswordlength', 'admin'), 8, PARAM_INT));
     $temp->add(new admin_setting_configtext('minpassworddigits', new lang_string('minpassworddigits', 'admin'), new lang_string('configminpassworddigits', 'admin'), 1, PARAM_INT));
index 632cfe1..9a6a104 100644 (file)
@@ -285,7 +285,11 @@ if ($formdata = $mform2->is_cancelled()) {
             $userserrors++;
             continue;
         }
-
+        if ($user->username !== clean_param($user->username, PARAM_USERNAME)) {
+            $upt->track('status', get_string('invalidusername', 'error', 'username'), 'error');
+            $upt->track('username', $errorstr, 'error');
+            $userserrors++;
+        }
         if ($existinguser = $DB->get_record('user', array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id))) {
             $upt->track('id', $existinguser->id, 'normal', false);
         }
index 97f1abb..aac7112 100644 (file)
@@ -2,6 +2,7 @@
 
     require_once('../config.php');
     require_once($CFG->libdir.'/adminlib.php');
+    require_once($CFG->libdir.'/authlib.php');
     require_once($CFG->dirroot.'/user/filters/lib.php');
 
     $delete       = optional_param('delete', 0, PARAM_INT);
@@ -16,6 +17,7 @@
     $acl          = optional_param('acl', '0', PARAM_INT);           // id of user to tweak mnet ACL (requires $access)
     $suspend      = optional_param('suspend', 0, PARAM_INT);
     $unsuspend    = optional_param('unsuspend', 0, PARAM_INT);
+    $unlock       = optional_param('unlock', 0, PARAM_INT);
 
     admin_externalpage_setup('editusers');
 
@@ -32,6 +34,7 @@
     $strshowallusers = get_string('showallusers');
     $strsuspend = get_string('suspenduser', 'admin');
     $strunsuspend = get_string('unsuspenduser', 'admin');
+    $strunlock = get_string('unlockaccount', 'admin');
     $strconfirm = get_string('confirm');
 
     if (empty($CFG->loginhttps)) {
             }
         }
         redirect($returnurl);
+
+    } else if ($unlock and confirm_sesskey()) {
+        require_capability('moodle/user:update', $sitecontext);
+
+        if ($user = $DB->get_record('user', array('id'=>$unlock, 'mnethostid'=>$CFG->mnet_localhost_id, 'deleted'=>0))) {
+            login_unlock_account($user);
+        }
+        redirect($returnurl);
     }
 
     // create the user filter form
                         }
                     }
 
+                    if (login_is_lockedout($user)) {
+                        $buttons[] = html_writer::link(new moodle_url($returnurl, array('unlock'=>$user->id, 'sesskey'=>sesskey())), html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/unlock'), 'alt'=>$strunlock, 'class'=>'iconsmall')), array('title'=>$strunlock));
+                    }
                 }
             }
 
index 4c95249..57c46e4 100644 (file)
@@ -1,11 +1,25 @@
 <?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/>.
+
 /**
  * Authentication Plugin: External Database Authentication
  *
  * Checks against an external database.
  *
- * @package    auth
- * @subpackage db
+ * @package    auth_db
  * @author     Martin Dougiamas
  * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
  */
@@ -13,7 +27,6 @@
 defined('MOODLE_INTERNAL') || die();
 
 require_once($CFG->libdir.'/authlib.php');
-require_once($CFG->libdir.'/adodb/adodb.inc.php');
 
 /**
  * External database authentication plugin.
@@ -23,7 +36,10 @@ class auth_plugin_db extends auth_plugin_base {
     /**
      * Constructor.
      */
-    function auth_plugin_db() {
+    function __construct() {
+        global $CFG;
+        require_once($CFG->libdir.'/adodb/adodb.inc.php');
+
         $this->authtype = 'db';
         $this->config = get_config('auth/db');
         if (empty($this->config->extencoding)) {
@@ -37,7 +53,6 @@ class auth_plugin_db extends auth_plugin_base {
      *
      * @param string $username The username
      * @param string $password The password
-     *
      * @return bool Authentication success or failure.
      */
     function user_login($username, $password) {
@@ -47,9 +62,9 @@ class auth_plugin_db extends auth_plugin_base {
         $extpassword = textlib::convert($password, 'utf-8', $this->config->extencoding);
 
         if ($this->is_internal()) {
-            // lookup username externally, but resolve
+            // Lookup username externally, but resolve
             // password locally -- to support backend that
-            // don't track passwords
+            // don't track passwords.
 
             if (isset($this->config->removeuser) and $this->config->removeuser == AUTH_REMOVEUSER_KEEP) {
                 // No need to connect to external database in this case because users are never removed and we verify password locally.
@@ -62,8 +77,9 @@ class auth_plugin_db extends auth_plugin_base {
 
             $authdb = $this->db_init();
 
-            $rs = $authdb->Execute("SELECT * FROM {$this->config->table}
-                                     WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' ");
+            $rs = $authdb->Execute("SELECT *
+                                      FROM {$this->config->table}
+                                     WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'");
             if (!$rs) {
                 $authdb->Close();
                 debugging(get_string('auth_dbcantconnect','auth_db'));
@@ -73,32 +89,32 @@ class auth_plugin_db extends auth_plugin_base {
             if (!$rs->EOF) {
                 $rs->Close();
                 $authdb->Close();
-                // user exists externally
-                // check username/password internally
+                // User exists externally - check username/password internally.
                 if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
                     return validate_internal_user_password($user, $password);
                 }
             } else {
                 $rs->Close();
                 $authdb->Close();
-                // user does not exist externally
+                // User does not exist externally.
                 return false;
             }
 
         } else {
-            // normal case: use external db for both usernames and passwords
+            // Normal case: use external db for both usernames and passwords.
 
             $authdb = $this->db_init();
 
-            if ($this->config->passtype === 'md5') {   // Re-format password accordingly
+            if ($this->config->passtype === 'md5') {   // Re-format password accordingly.
                 $extpassword = md5($extpassword);
             } else if ($this->config->passtype === 'sha1') {
                 $extpassword = sha1($extpassword);
             }
 
-            $rs = $authdb->Execute("SELECT * FROM {$this->config->table}
-                                WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'
-                                  AND {$this->config->fieldpass} = '".$this->ext_addslashes($extpassword)."' ");
+            $rs = $authdb->Execute("SELECT *
+                                      FROM {$this->config->table}
+                                     WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'
+                                           AND {$this->config->fieldpass} = '".$this->ext_addslashes($extpassword)."'");
             if (!$rs) {
                 $authdb->Close();
                 debugging(get_string('auth_dbcantconnect','auth_db'));
@@ -119,11 +135,11 @@ class auth_plugin_db extends auth_plugin_base {
     }
 
     function db_init() {
-        // Connect to the external database (forcing new connection)
+        // Connect to the external database (forcing new connection).
         $authdb = ADONewConnection($this->config->type);
         if (!empty($this->config->debugauthdb)) {
             $authdb->debug = true;
-            ob_start();//start output buffer to allow later use of the page headers
+            ob_start(); //Start output buffer to allow later use of the page headers.
         }
         $authdb->Connect($this->config->host, $this->config->user, $this->config->pass, $this->config->name, true);
         $authdb->SetFetchMode(ADODB_FETCH_ASSOC);
@@ -135,7 +151,7 @@ class auth_plugin_db extends auth_plugin_base {
     }
 
     /**
-     * Returns user attribute mappings between moodle and ldap
+     * Returns user attribute mappings between moodle and ldap.
      *
      * @return array
      */
@@ -152,11 +168,10 @@ class auth_plugin_db extends auth_plugin_base {
 
     /**
      * Reads any other information for a user from external database,
-     * then returns it in an array
+     * then returns it in an array.
      *
      * @param string $username
-     *
-     * @return array without magic quotes
+     * @return array
      */
     function get_userinfo($username) {
         global $CFG;
@@ -165,22 +180,22 @@ class auth_plugin_db extends auth_plugin_base {
 
         $authdb = $this->db_init();
 
-        //Array to map local fieldnames we want, to external fieldnames
+        // Array to map local fieldnames we want, to external fieldnames.
         $selectfields = $this->db_attributes();
 
         $result = array();
-        //If at least one field is mapped from external db, get that mapped data:
+        // If at least one field is mapped from external db, get that mapped data.
         if ($selectfields) {
-            $select = '';
+            $select = array();
             foreach ($selectfields as $localname=>$externalname) {
-                $select .= ", $externalname AS $localname";
+                $select[] = "$externalname AS $localname";
             }
-            $select = 'SELECT ' . substr($select,1);
-            $sql = $select .
-                " FROM {$this->config->table}" .
-                " WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'";
+            $select = implode(', ', $select);
+            $sql = "SELECT $select
+                      FROM {$this->config->table}
+                     WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'";
             if ($rs = $authdb->Execute($sql)) {
-                if ( !$rs->EOF ) {
+                if (!$rs->EOF) {
                     $fields_obj = $rs->FetchObj();
                     $fields_obj = (object)array_change_key_case((array)$fields_obj , CASE_LOWER);
                     foreach ($selectfields as $localname=>$externalname) {
@@ -195,12 +210,11 @@ class auth_plugin_db extends auth_plugin_base {
     }
 
     /**
-     * Change a user's password
+     * Change a user's password.
      *
-     * @param  object  $user        User table object
+     * @param  stdClass  $user      User table object
      * @param  string  $newpassword Plaintext password
-     *
-     * @return bool                  True on success
+     * @return bool                 True on success
      */
     function user_update_password($user, $newpassword) {
         global $DB;
@@ -214,13 +228,13 @@ class auth_plugin_db extends auth_plugin_base {
                 return false;
             }
         } else {
-            // we should have never been called!
+            // We should have never been called!
             return false;
         }
     }
 
     /**
-     * synchronizes user from external db to moodle user table
+     * Synchronizes user from external db to moodle user table.
      *
      * Sync should be done by using idnumber attribute, not username.
      * You need to pass firstsync parameter to function to fill in
@@ -231,17 +245,17 @@ class auth_plugin_db extends auth_plugin_base {
      *
      * This implementation is simpler but less scalable than the one found in the LDAP module.
      *
+     * @param progress_trace $trace
      * @param bool $do_updates  Optional: set to true to force an update of existing accounts
-     * @param bool $verbose
      * @return int 0 means success, 1 means failure
      */
-    function sync_users($do_updates=false, $verbose=false) {
+    function sync_users(progress_trace $trace, $do_updates=false) {
         global $CFG, $DB;
 
-        // list external users
+        // List external users.
         $userlist = $this->get_userlist();
 
-        // delete obsolete internal users
+        // Delete obsolete internal users.
         if (!empty($this->config->removeuser)) {
 
             $suspendselect = "";
@@ -249,7 +263,7 @@ class auth_plugin_db extends auth_plugin_base {
                 $suspendselect = "AND u.suspended = 0";
             }
 
-            // find obsolete users
+            // Find obsolete users.
             if (count($userlist)) {
                 list($notin_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', false);
                 $params['authtype'] = $this->authtype;
@@ -267,56 +281,46 @@ class auth_plugin_db extends auth_plugin_base {
             $remove_users = $DB->get_records_sql($sql, $params);
 
             if (!empty($remove_users)) {
-                if ($verbose) {
-                    mtrace(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
-                }
+                $trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
 
                 foreach ($remove_users as $user) {
                     if ($this->config->removeuser == AUTH_REMOVEUSER_FULLDELETE) {
                         delete_user($user);
-                        if ($verbose) {
-                            mtrace("\t".get_string('auth_dbdeleteuser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)));
-                        }
+                        $trace->output(get_string('auth_dbdeleteuser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
                     } else if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
                         $updateuser = new stdClass();
                         $updateuser->id   = $user->id;
                         $updateuser->suspended = 1;
                         $updateuser->timemodified = time();
                         $DB->update_record('user', $updateuser);
-                        if ($verbose) {
-                            mtrace("\t".get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)));
-                        }
+                        $trace->output(get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
                     }
                 }
             }
-            unset($remove_users); // free mem!
+            unset($remove_users);
         }
 
         if (!count($userlist)) {
-            // exit right here
-            // nothing else to do
+            // Exit right here, nothing else to do.
+            $trace->finished();
             return 0;
         }
 
-        ///
-        /// update existing accounts
-        ///
+        // Update existing accounts.
         if ($do_updates) {
-            // narrow down what fields we need to update
+            // Narrow down what fields we need to update.
             $all_keys = array_keys(get_object_vars($this->config));
             $updatekeys = array();
             foreach ($all_keys as $key) {
                 if (preg_match('/^field_updatelocal_(.+)$/',$key, $match)) {
                     if ($this->config->{$key} === 'onlogin') {
-                        array_push($updatekeys, $match[1]); // the actual key name
+                        array_push($updatekeys, $match[1]); // The actual key name.
                     }
                 }
             }
-            // print_r($all_keys); print_r($updatekeys);
             unset($all_keys); unset($key);
 
-            // only go ahead if we actually
-            // have fields to update locally
+            // Only go ahead if we actually have fields to update locally.
             if (!empty($updatekeys)) {
                 list($in_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', true);
                 $params['authtype'] = $this->authtype;
@@ -324,32 +328,23 @@ class auth_plugin_db extends auth_plugin_base {
                           FROM {user} u
                          WHERE u.auth=:authtype AND u.deleted=0 AND u.username {$in_sql}";
                 if ($update_users = $DB->get_records_sql($sql, $params)) {
-                    if ($verbose) {
-                        mtrace("User entries to update: ".count($update_users));
-                    }
+                    $trace->output("User entries to update: ".count($update_users));
 
                     foreach ($update_users as $user) {
                         if ($this->update_user_record($user->username, $updatekeys)) {
-                            if ($verbose) {
-                                mtrace("\t".get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)));
-                            }
+                            $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
                         } else {
-                            if ($verbose) {
-                                mtrace("\t".get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id))." - ".get_string('skipped'));
-                            }
+                            $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id))." - ".get_string('skipped'), 1);
                         }
                     }
-                    unset($update_users); // free memory
+                    unset($update_users);
                 }
             }
         }
 
 
-        ///
-        /// create missing accounts
-        ///
-        // NOTE: this is very memory intensive
-        // and generally inefficient
+        // Create missing accounts.
+        // NOTE: this is very memory intensive and generally inefficient.
         $suspendselect = "";
         if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
             $suspendselect = "AND u.suspended = 0";
@@ -360,7 +355,7 @@ class auth_plugin_db extends auth_plugin_base {
 
         $users = $DB->get_records_sql($sql, array('authtype'=>$this->authtype, 'mnethostid'=>$CFG->mnet_localhost_id));
 
-        // simplify down to usernames
+        // Simplify down to usernames.
         $usernames = array();
         if (!empty($users)) {
             foreach ($users as $user) {
@@ -373,25 +368,21 @@ class auth_plugin_db extends auth_plugin_base {
         unset($usernames);
 
         if (!empty($add_users)) {
-            if ($verbose) {
-                mtrace(get_string('auth_dbuserstoadd','auth_db',count($add_users)));
-            }
+            $trace->output(get_string('auth_dbuserstoadd','auth_db',count($add_users)));
             // Do not use transactions around this foreach, we want to skip problematic users, not revert everything.
             foreach($add_users as $user) {
                 $username = $user;
                 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
                     if ($old_user = $DB->get_record('user', array('username'=>$username, 'deleted'=>0, 'suspended'=>1, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
                         $DB->set_field('user', 'suspended', 0, array('id'=>$old_user->id));
-                        if ($verbose) {
-                            mtrace("\t".get_string('auth_dbreviveduser', 'auth_db', array('name'=>$username, 'id'=>$old_user->id)));
-                        }
+                        $trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name'=>$username, 'id'=>$old_user->id)), 1);
                         continue;
                     }
                 }
 
                 // Do not try to undelete users here, instead select suspending if you ever expect users will reappear.
 
-                // prep a few params
+                // Prep a few params.
                 $user = $this->get_userinfo_asobj($user);
                 $user->username   = $username;
                 $user->confirmed  = 1;
@@ -403,23 +394,17 @@ class auth_plugin_db extends auth_plugin_base {
                 $user->timecreated = time();
                 $user->timemodified = $user->timecreated;
                 if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype), 'id,username,auth')) {
-                    if ($verbose) {
-                        mtrace("\t".get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)));
-                    }
+                    $trace->output(get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)), 1);
                     continue;
                 }
                 try {
                     $id = $DB->insert_record ('user', $user); // it is truly a new user
-                    if ($verbose) {
-                        mtrace("\t".get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)));
-                    }
+                    $trace->output(get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)), 1);
                 } catch (moodle_exception $e) {
-                    if ($verbose) {
-                        mtrace("\t".get_string('auth_dbinsertusererror', 'auth_db', $user->username));
-                    }
+                    $trace->output(get_string('auth_dbinsertusererror', 'auth_db', $user->username), 1);
                     continue;
                 }
-                // if relevant, tag for password generation
+                // If relevant, tag for password generation.
                 if ($this->is_internal()) {
                     set_user_preference('auth_forcepasswordchange', 1, $id);
                     set_user_preference('create_password',          1, $id);
@@ -427,27 +412,29 @@ class auth_plugin_db extends auth_plugin_base {
                 // Make sure user context is present.
                 context_user::instance($id);
             }
-            unset($add_users); // free mem
+            unset($add_users);
         }
+        $trace->finished();
         return 0;
     }
 
     function user_exists($username) {
 
-    /// Init result value
+        // Init result value.
         $result = false;
 
         $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding);
 
         $authdb = $this->db_init();
 
-        $rs = $authdb->Execute("SELECT * FROM {$this->config->table}
-                                     WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' ");
+        $rs = $authdb->Execute("SELECT *
+                                  FROM {$this->config->table}
+                                 WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' ");
 
         if (!$rs) {
             print_error('auth_dbcantconnect','auth_db');
         } else if (!$rs->EOF) {
-            // user exists externally
+            // User exists externally.
             $result = true;
         }
 
@@ -458,14 +445,14 @@ class auth_plugin_db extends auth_plugin_base {
 
     function get_userlist() {
 
-    /// Init result value
+        // Init result value.
         $result = array();
 
         $authdb = $this->db_init();
 
-        // fetch userlist
+        // Fetch userlist.
         $rs = $authdb->Execute("SELECT {$this->config->fielduser} AS username
-                                FROM   {$this->config->table} ");
+                                  FROM {$this->config->table} ");
 
         if (!$rs) {
             print_error('auth_dbcantconnect','auth_db');
@@ -481,9 +468,9 @@ class auth_plugin_db extends auth_plugin_base {
     }
 
     /**
-     * reads user information from DB and return it in an object
+     * Reads user information from DB and return it in an object.
      *
-     * @param string $username username (with system magic quotes)
+     * @param string $username username
      * @return array
      */
     function get_userinfo_asobj($username) {
@@ -498,7 +485,7 @@ class auth_plugin_db extends auth_plugin_base {
     /**
      * will update a local user record from an external source.
      * is a lighter version of the one in moodlelib -- won't do
-     * expensive ops such as enrolment
+     * expensive ops such as enrolment.
      *
      * If you don't pass $updatekeys, there is a performance hit and
      * values removed from DB won't be removed from moodle.
@@ -521,14 +508,14 @@ class auth_plugin_db extends auth_plugin_base {
             die;
         }
 
-        // Ensure userid is not overwritten
+        // Ensure userid is not overwritten.
         $userid = $user->id;
         $updated = false;
 
         if ($newinfo = $this->get_userinfo($username)) {
             $newinfo = truncate_userinfo($newinfo);
 
-            if (empty($updatekeys)) { // all keys? this does not support removing values
+            if (empty($updatekeys)) { // All keys? This does not support removing values.
                 $updatekeys = array_keys($newinfo);
             }
 
@@ -540,7 +527,7 @@ class auth_plugin_db extends auth_plugin_base {
                 }
 
                 if (!empty($this->config->{'field_updatelocal_' . $key})) {
-                    if (isset($user->{$key}) and $user->{$key} != $value) { // only update if it's changed
+                    if (isset($user->{$key}) and $user->{$key} != $value) { // Only update if it's changed.
                         $DB->set_field('user', $key, $value, array('id'=>$userid));
                         $updated = true;
                     }
@@ -558,8 +545,8 @@ class auth_plugin_db extends auth_plugin_base {
      * Modifies user in external database. It takes olduser (before changes) and newuser (after changes)
      * compares information saved modified information to external db.
      *
-     * @param mixed $olduser     Userobject before modifications
-     * @param mixed $newuser     Userobject new modified userobject
+     * @param stdClass $olduser     Userobject before modifications
+     * @param stdClass $newuser     Userobject new modified userobject
      * @return boolean result
      *
      */
@@ -570,7 +557,7 @@ class auth_plugin_db extends auth_plugin_base {
         }
 
         if (isset($olduser->auth) and $olduser->auth != $this->authtype) {
-            return true; // just change auth and skip update
+            return true; // Just change auth and skip update.
         }
 
         $curruser = $this->get_userinfo($olduser->username);
@@ -586,10 +573,10 @@ class auth_plugin_db extends auth_plugin_base {
         $update = array();
         foreach($curruser as $key=>$value) {
             if ($key == 'username') {
-                continue; // skip this
+                continue; // Skip this.
             }
             if (empty($this->config->{"field_updateremote_$key"})) {
-                continue; // remote update not requested
+                continue; // Remote update not requested.
             }
             if (!isset($newuser->$key)) {
                 continue;
@@ -612,8 +599,8 @@ class auth_plugin_db extends auth_plugin_base {
      * A chance to validate form data, and last chance to
      * do stuff before it is inserted in config_plugin
      *
-     * @param stfdClass config form
-     * @param array $error errors
+     * @param stfdClass $form
+     * @param array $err errors
      * @return void
      */
      function validate_form($form, &$err) {
@@ -670,10 +657,10 @@ class auth_plugin_db extends auth_plugin_base {
      */
     function change_password_url() {
         if ($this->is_internal()) {
-            // standard form
+            // Standard form.
             return null;
         } else {
-            // use admin defined custom url
+            // Use admin defined custom url.
             return new moodle_url($this->config->changepasswordurl);
         }
     }
@@ -704,6 +691,7 @@ class auth_plugin_db extends auth_plugin_base {
 
     /**
      * Processes and stores configuration data for this authentication plugin.
+     *
      * @param srdClass $config
      * @return bool always true or exception
      */
@@ -755,7 +743,7 @@ class auth_plugin_db extends auth_plugin_base {
             $config->changepasswordurl = '';
         }
 
-        // save settings
+        // Save settings.
         set_config('host',          $config->host,          'auth/db');
         set_config('type',          $config->type,          'auth/db');
         set_config('sybasequoting', $config->sybasequoting, 'auth/db');
@@ -775,8 +763,13 @@ class auth_plugin_db extends auth_plugin_base {
         return true;
     }
 
+    /**
+     * Add slashes, we can not use placeholders or system functions.
+     *
+     * @param string $text
+     * @return string
+     */
     function ext_addslashes($text) {
-        // using custom made function for now
         if (empty($this->config->sybasequoting)) {
             $text = str_replace('\\', '\\\\', $text);
             $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text);
index 20ef137..f84bf2f 100644 (file)
@@ -23,7 +23,7 @@
  *
  * Sample cron entry:
  * # 5 minutes past 4am
- * 5 4 * * * $sudo -u www-data /usr/bin/php /var/www/moodle/auth/db/cli/sync_users.php
+ * 5 4 * * * sudo -u www-data /usr/bin/php /var/www/moodle/auth/db/cli/sync_users.php
  *
  * Notes:
  *   - it is required to use the web server account when executing PHP CLI scripts
  * Performance notes:
  * + The code is simpler, but not as optimized as its LDAP counterpart.
  *
- * @package    auth
- * @subpackage db
+ * @package    auth_db
  * @copyright  2006 Martin Langhoff
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 define('CLI_SCRIPT', true);
 
-require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
-require_once($CFG->dirroot.'/course/lib.php');
-require_once($CFG->libdir.'/clilib.php');
+require(__DIR__.'/../../../config.php');
+require_once("$CFG->libdir/clilib.php");
 
-// now get cli options
+// Now get cli options.
 list($options, $unrecognized) = cli_get_params(array('noupdate'=>false, 'verbose'=>false, 'help'=>false), array('n'=>'noupdate', 'v'=>'verbose', 'h'=>'help'));
 
 if ($unrecognized) {
@@ -64,15 +62,15 @@ The auth_db plugin must be enabled and properly configured.
 
 Options:
 -n, --noupdate        Skip update of existing users
--v, --verbose         Print verbose progess information
+-v, --verbose         Print verbose progress information
 -h, --help            Print out this help
 
 Example:
-\$sudo -u www-data /usr/bin/php auth/db/cli/sync_users.php
+\$ sudo -u www-data /usr/bin/php auth/db/cli/sync_users.php
 
 Sample cron entry:
 # 5 minutes past 4am
-5 4 * * * \$sudo -u www-data /usr/bin/php /var/www/moodle/auth/db/cli/sync_users.php
+5 4 * * * sudo -u www-data /usr/bin/php /var/www/moodle/auth/db/cli/sync_users.php
 ";
 
     echo $help;
@@ -80,13 +78,19 @@ Sample cron entry:
 }
 
 if (!is_enabled_auth('db')) {
-    echo "Plugin not enabled!";
-    exit(1);
+    cli_error('auth_db plugin is disabled, synchronisation stopped', 2);
+}
+
+if (empty($options['verbose'])) {
+    $trace = new null_progress_trace();
+} else {
+    $trace = new text_progress_trace();
 }
 
-$verbose = !empty($options['verbose']);
 $update = empty($options['noupdate']);
 
+/** @var auth_plugin_db $dbauth */
 $dbauth = get_auth_plugin('db');
-return $dbauth->sync_users($update, $verbose);
+$result = $dbauth->sync_users($trace, $update);
 
+exit($result);
index e5fdb34..240dead 100644 (file)
@@ -1,4 +1,26 @@
 <?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/>.
+
+/**
+ * auth_db installer script.
+ *
+ * @package    auth_db
+ * @copyright  2009 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
 
 function xmldb_auth_db_install() {
     global $CFG, $DB;
index 76967ea..e2e295c 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -16,7 +15,7 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Strings for component 'auth_db', language 'en', branch 'MOODLE_20_STABLE'
+ * Strings for component 'auth_db', language 'en'.
  *
  * @package   auth_db
  * @copyright 1999 onwards Martin Dougiamas  {@link http://moodle.com}
diff --git a/auth/db/tests/db_test.php b/auth/db/tests/db_test.php
new file mode 100644 (file)
index 0000000..f91b44a
--- /dev/null
@@ -0,0 +1,361 @@
+<?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/>.
+
+/**
+ * External database auth sync tests, this also tests adodb drivers
+ * that are matching our four supported Moodle database drivers.
+ *
+ * @package    auth_db
+ * @category   phpunit
+ * @copyright  2012 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die();
+
+
+class auth_db_testcase extends advanced_testcase {
+
+    protected function init_auth_database() {
+        global $DB, $CFG;
+        require_once("$CFG->dirroot/auth/db/auth.php");
+
+        $dbman = $DB->get_manager();
+
+        set_config('extencoding', 'utf-8', 'auth/db');
+
+        set_config('host', $CFG->dbhost, 'auth/db');
+        set_config('user', $CFG->dbuser, 'auth/db');
+        set_config('pass', $CFG->dbpass, 'auth/db');
+        set_config('name', $CFG->dbname, 'auth/db');
+
+        if (!empty($CFG->dboptions['dbport'])) {
+            set_config('host', $CFG->dbhost.':'.$CFG->dboptions['dbport'], 'auth/db');
+        }
+
+        switch (get_class($DB)) {
+            case 'mssql_native_moodle_database':
+                set_config('type', 'mssql_n', 'auth/db');
+                set_config('sybasequoting', '1', 'auth/db');
+                break;
+
+            case 'mysqli_native_moodle_database':
+                set_config('type', 'mysqli', 'auth/db');
+                set_config('setupsql', "SET NAMES 'UTF-8'", 'auth/db');
+                set_config('sybasequoting', '0', 'auth/db');
+                if (!empty($CFG->dboptions['dbsocket'])) {
+                    $dbsocket = $CFG->dboptions['dbsocket'];
+                    if ((strpos($dbsocket, '/') === false and strpos($dbsocket, '\\') === false)) {
+                        $dbsocket = ini_get('mysqli.default_socket');
+                    }
+                    set_config('type', 'mysqli://'.rawurlencode($CFG->dbuser).':'.rawurlencode($CFG->dbpass).'@'.rawurlencode($CFG->dbhost).'/'.rawurlencode($CFG->dbname).'?socket='.rawurlencode($dbsocket), 'auth/db');
+                }
+                break;
+
+            case 'oci_native_moodle_database':
+                set_config('type', 'oci8po', 'auth/db');
+                set_config('sybasequoting', '1', 'auth/db');
+                break;
+
+            case 'pgsql_native_moodle_database':
+                set_config('type', 'postgres7', 'auth/db');
+                set_config('setupsql', "SET NAMES 'UTF-8'", 'auth/db');
+                set_config('sybasequoting', '0', 'auth/db');
+                if (!empty($CFG->dboptions['dbsocket']) and ($CFG->dbhost === 'localhost' or $CFG->dbhost === '127.0.0.1')) {
+                    if (strpos($CFG->dboptions['dbsocket'], '/') !== false) {
+                        set_config('host', $CFG->dboptions['dbsocket'], 'auth/db');
+                    } else {
+                        set_config('host', '', 'auth/db');
+                    }
+                }
+                break;
+
+            case 'sqlsrv_native_moodle_database':
+                set_config('type', 'mssqlnative', 'auth/db');
+                set_config('sybasequoting', '1', 'auth/db');
+                break;
+
+            default:
+                throw new exception('Unknown database driver '.get_class($DB));
+        }
+
+        $table = new xmldb_table('auth_db_users');
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null);
+        $table->add_field('pass', XMLDB_TYPE_CHAR, '255', null, null, null);
+        $table->add_field('email', XMLDB_TYPE_CHAR, '255', null, null, null);
+        $table->add_field('firstname', XMLDB_TYPE_CHAR, '255', null, null, null);
+        $table->add_field('lastname', XMLDB_TYPE_CHAR, '255', null, null, null);
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        if ($dbman->table_exists($table)) {
+            $dbman->drop_table($table);
+        }
+        $dbman->create_table($table);
+        set_config('table', $CFG->prefix.'auth_db_users', 'auth/db');
+        set_config('fielduser', 'name', 'auth/db');
+        set_config('fieldpass', 'pass', 'auth/db');
+
+        // Setu up field mappings.
+
+        set_config('field_map_email', 'email', 'auth/db');
+        set_config('field_updatelocal_email', 'oncreate', 'auth/db');
+        set_config('field_updateremote_email', '0', 'auth/db');
+        set_config('field_lock_email', 'unlocked', 'auth/db');
+
+        // Init the rest of settings.
+        set_config('passtype', 'plaintext', 'auth/db');
+        set_config('changepasswordurl', '', 'auth/db');
+        set_config('debugauthdb', 0, 'auth/db');
+        set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth/db');
+    }
+
+    protected function cleanup_auth_database() {
+        global $DB;
+
+        $dbman = $DB->get_manager();
+        $table = new xmldb_table('auth_db_users');
+        $dbman->drop_table($table);
+    }
+
+    public function test_plugin() {
+        global $DB, $CFG;
+
+        $this->resetAfterTest(false);
+
+        // NOTE: It is strongly discouraged to create new tables in advanced_testcase classes,
+        //       but there is no other simple way to test ext database enrol sync, so let's
+        //       disable transactions are try to cleanup after the tests.
+
+        $this->preventResetByRollback();
+
+        $this->init_auth_database();
+
+        /** @var auth_plugin_db $auth */
+        $auth = get_auth_plugin('db');
+
+        $authdb = $auth->db_init();
+
+
+        // Test adodb may access the table.
+
+        $user1 = (object)array('name'=>'u1', 'pass'=>'heslo', 'email'=>'u1@example.com');
+        $user1->id = $DB->insert_record('auth_db_users', $user1);
+
+
+        $sql = "SELECT * FROM {$auth->config->table}";
+        $rs = $authdb->Execute($sql);
+        $this->assertInstanceOf('ADORecordSet', $rs);
+        $this->assertFalse($rs->EOF);
+        $fields = $rs->FetchRow();
+        $this->assertTrue(is_array($fields));
+        $this->assertTrue($rs->EOF);
+        $rs->Close();
+
+        $authdb->Close();
+
+
+        // Test bulk user account creation.
+
+        $user2 = (object)array('name'=>'u2', 'pass'=>'heslo', 'email'=>'u2@example.com');
+        $user2->id = $DB->insert_record('auth_db_users', $user2);
+
+        $user3 = (object)array('name'=>'admin', 'pass'=>'heslo', 'email'=>'admin@example.com'); // Should be skipped.
+        $user3->id = $DB->insert_record('auth_db_users', $user3);
+
+        $this->assertCount(2, $DB->get_records('user'));
+
+        $trace = new null_progress_trace();
+        $auth->sync_users($trace, false);
+
+        $this->assertEquals(4, $DB->count_records('user'));
+        $u1 = $DB->get_record('user', array('username'=>$user1->name, 'auth'=>'db'));
+        $this->assertSame($user1->email, $u1->email);
+        $u2 = $DB->get_record('user', array('username'=>$user2->name, 'auth'=>'db'));
+        $this->assertSame($user2->email, $u2->email);
+        $admin = $DB->get_record('user', array('username'=>'admin', 'auth'=>'manual'));
+        $this->assertNotEmpty($admin);
+
+
+        // Test sync updates.
+
+        $user2b = clone($user2);
+        $user2b->email = 'u2b@example.com';
+        $DB->update_record('auth_db_users', $user2b);
+
+        $auth->sync_users($trace, false);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $u2 = $DB->get_record('user', array('username'=>$user2->name));
+        $this->assertSame($user2->email, $u2->email);
+
+        $auth->sync_users($trace, true);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $u2 = $DB->get_record('user', array('username'=>$user2->name));
+        $this->assertSame($user2->email, $u2->email);
+
+        set_config('field_updatelocal_email', 'onlogin', 'auth/db');
+        $auth->config->field_updatelocal_email = 'onlogin';
+
+        $auth->sync_users($trace, false);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $u2 = $DB->get_record('user', array('username'=>$user2->name));
+        $this->assertSame($user2->email, $u2->email);
+
+        $auth->sync_users($trace, true);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $u2 = $DB->get_record('user', array('username'=>$user2->name));
+        $this->assertSame($user2b->email, $u2->email);
+
+
+        // Test sync deletes and suspends.
+
+        $DB->delete_records('auth_db_users', array('id'=>$user2->id));
+        $this->assertCount(2, $DB->get_records('auth_db_users'));
+        unset($user2);
+        unset($user2b);
+
+        $auth->sync_users($trace, false);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
+        $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+
+        set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth/db');
+        $auth->config->removeuser = AUTH_REMOVEUSER_SUSPEND;
+
+        $auth->sync_users($trace, false);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
+        $this->assertEquals(1, $DB->count_records('user', array('suspended'=>1)));
+
+        $user2 = (object)array('name'=>'u2', 'pass'=>'heslo', 'email'=>'u2@example.com');
+        $user2->id = $DB->insert_record('auth_db_users', $user2);
+
+        $auth->sync_users($trace, false);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $this->assertEquals(0, $DB->count_records('user', array('deleted'=>1)));
+        $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+
+        $DB->delete_records('auth_db_users', array('id'=>$user2->id));
+
+        set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth/db');
+        $auth->config->removeuser = AUTH_REMOVEUSER_FULLDELETE;
+
+        $auth->sync_users($trace, false);
+        $this->assertEquals(4, $DB->count_records('user'));
+        $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
+        $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+
+        $user2 = (object)array('name'=>'u2', 'pass'=>'heslo', 'email'=>'u2@example.com');
+        $user2->id = $DB->insert_record('auth_db_users', $user2);
+
+        $auth->sync_users($trace, false);
+        $this->assertEquals(5, $DB->count_records('user'));
+        $this->assertEquals(1, $DB->count_records('user', array('deleted'=>1)));
+        $this->assertEquals(0, $DB->count_records('user', array('suspended'=>1)));
+
+
+        // Test user_login().
+
+        $user3 = (object)array('name'=>'u3', 'pass'=>'heslo', 'email'=>'u3@example.com');
+        $user3->id = $DB->insert_record('auth_db_users', $user3);
+
+        $this->assertFalse($auth->user_login('u4', 'heslo'));
+        $this->assertTrue($auth->user_login('u1', 'heslo'));
+
+        $this->assertFalse($DB->record_exists('user', array('username'=>'u3', 'auth'=>'db')));
+        $this->assertTrue($auth->user_login('u3', 'heslo'));
+        $this->assertFalse($DB->record_exists('user', array('username'=>'u3', 'auth'=>'db')));
+
+        set_config('passtype', 'md5', 'auth/db');
+        $auth->config->passtype = 'md5';
+        $user3->pass = md5('heslo');
+        $DB->update_record('auth_db_users', $user3);
+        $this->assertTrue($auth->user_login('u3', 'heslo'));
+
+        set_config('passtype', 'sh1', 'auth/db');
+        $auth->config->passtype = 'sha1';
+        $user3->pass = sha1('heslo');
+        $DB->update_record('auth_db_users', $user3);
+        $this->assertTrue($auth->user_login('u3', 'heslo'));
+
+        set_config('passtype', 'internal', 'auth/db');
+        $auth->config->passtype = 'internal';
+        create_user_record('u3', 'heslo', 'db');
+        $this->assertTrue($auth->user_login('u3', 'heslo'));
+
+
+        $DB->delete_records('auth_db_users', array('id'=>$user3->id));
+
+        set_config('removeuser', AUTH_REMOVEUSER_KEEP, 'auth/db');
+        $auth->config->removeuser = AUTH_REMOVEUSER_KEEP;
+        $this->assertTrue($auth->user_login('u3', 'heslo'));
+
+        set_config('removeuser', AUTH_REMOVEUSER_SUSPEND, 'auth/db');
+        $auth->config->removeuser = AUTH_REMOVEUSER_SUSPEND;
+        $this->assertFalse($auth->user_login('u3', 'heslo'));
+
+        set_config('removeuser', AUTH_REMOVEUSER_FULLDELETE, 'auth/db');
+        $auth->config->removeuser = AUTH_REMOVEUSER_FULLDELETE;
+        $this->assertFalse($auth->user_login('u3', 'heslo'));
+
+        set_config('passtype', 'sh1', 'auth/db');
+        $auth->config->passtype = 'sha1';
+        $this->assertFalse($auth->user_login('u3', 'heslo'));
+
+
+        // Test login create and update.
+
+        $user4 = (object)array('name'=>'u4', 'pass'=>'heslo', 'email'=>'u4@example.com');
+        $user4->id = $DB->insert_record('auth_db_users', $user4);
+
+        set_config('passtype', 'plaintext', 'auth/db');
+        $auth->config->passtype = 'plaintext';
+
+        $iuser4 = create_user_record('u4', 'heslo', 'db');
+        $this->assertNotEmpty($iuser4);
+        $this->assertSame($user4->name, $iuser4->username);
+        $this->assertSame($user4->email, $iuser4->email);
+        $this->assertSame('db', $iuser4->auth);
+        $this->assertSame($CFG->mnet_localhost_id, $iuser4->mnethostid);
+
+        $user4b = clone($user4);
+        $user4b->email = 'u4b@example.com';
+        $DB->update_record('auth_db_users', $user4b);
+
+        set_config('field_updatelocal_email', 'oncreate', 'auth/db');
+        $auth->config->field_updatelocal_email = 'oncreate';
+
+        update_user_record('u4');
+        $iuser4 = $DB->get_record('user', array('id'=>$iuser4->id));
+        $this->assertSame($user4->email, $iuser4->email);
+
+        set_config('field_updatelocal_email', 'onlogin', 'auth/db');
+        $auth->config->field_updatelocal_email = 'onlogin';
+
+        update_user_record('u4');
+        $iuser4 = $DB->get_record('user', array('id'=>$iuser4->id));
+        $this->assertSame($user4b->email, $iuser4->email);
+
+
+        // Test user_exists()
+
+        $this->assertTrue($auth->user_exists('u1'));
+        $this->assertTrue($auth->user_exists('admin'));
+        $this->assertFalse($auth->user_exists('u3'));
+        $this->assertTrue($auth->user_exists('u4'));
+
+        $this->cleanup_auth_database();
+    }
+}
index 4cecc7e..914a9fa 100644 (file)
@@ -17,8 +17,7 @@
 /**
  * Version details
  *
- * @package    auth
- * @subpackage db
+ * @package    auth_db
  * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
index 1e93cf2..29943d6 100644 (file)
@@ -40,8 +40,7 @@ if ($show_instructions) {
             </form>
             <p>
             <?php
-                print_string("auth_shibboleth_contact_administrator", "auth_shibboleth");
-                echo '<a href="mailto:'.get_admin()->email.'"> Moodle Administrator</a>.';
+                print_string("auth_shib_contact_administrator", "auth_shibboleth", get_admin()->email);
             ?>
             </p>
           </div>
index aace9a1..ea8980d 100644 (file)
@@ -25,7 +25,7 @@
 
 $string['auth_shib_auth_method'] = 'Authentication method name';
 $string['auth_shib_auth_method_description'] = 'Provide a name for the Shibboleth authentication method that is familiar to your users. This could be the name of your Shibboleth federation, e.g. <tt>SWITCHaai Login</tt> or <tt>InCommon Login</tt> or similar.';
-$string['auth_shibboleth_contact_administrator'] = 'In case you are not associated with the given organizations and you need access to a course on this server, please contact the';
+$string['auth_shib_contact_administrator'] = 'In case you are not associated with the given organizations and you need access to a course on this server, please contact the <a href="mailto:{$a}">Moodle Administrator</a>.';
 $string['auth_shibbolethdescription'] = 'Using this method users are created and authenticated using <a href="http://shibboleth.internet2.edu/">Shibboleth</a>.<br />Be sure to read the <a href="../auth/shibboleth/README.txt">README</a> for Shibboleth on how to set up your Moodle with Shibboleth';
 $string['auth_shibboleth_errormsg'] = 'Please select the organization you are member of!';
 $string['auth_shibboleth_login'] = 'Shibboleth login';
index 6b81b7d..ac5584c 100644 (file)
@@ -33,7 +33,7 @@ $currentcontext = required_param('currentcontext', PARAM_INT);
 // file parameters
 $component  = optional_param('component', null, PARAM_COMPONENT);
 $filearea   = optional_param('filearea', null, PARAM_AREA);
-$returnurl  = optional_param('returnurl', null, PARAM_URL);
+$returnurl  = optional_param('returnurl', null, PARAM_LOCALURL);
 
 list($context, $course, $cm) = get_context_info_array($currentcontext);
 $filecontext = context::instance_by_id($contextid, IGNORE_MISSING);
index 4fd0e1b..4df141f 100644 (file)
@@ -642,7 +642,9 @@ class moodle1_converter extends base_converter {
         }
         foreach ($matches[2] as $match) {
             $file = str_replace(array('$@FILEPHP@$', '$@SLASH@$', '$@FORCEDOWNLOAD@$'), array('', '/', ''), $match);
-            $files[] = rawurldecode($file);
+            if ($file === clean_param($file, PARAM_PATH)) {
+                $files[] = rawurldecode($file);
+            }
         }
 
         return array_unique($files);
@@ -1210,6 +1212,10 @@ class moodle1_file_manager implements loggable {
 
         $sourcefullpath = $this->basepath.'/'.$sourcepath;
 
+        if ($sourcefullpath !== clean_param($sourcefullpath, PARAM_PATH)) {
+            throw new moodle1_convert_exception('file_invalid_path', $sourcefullpath);
+        }
+
         if (!is_readable($sourcefullpath)) {
             throw new moodle1_convert_exception('file_not_readable', $sourcefullpath);
         }
index bf4935c..154b6c8 100644 (file)
@@ -273,6 +273,15 @@ class moodle1_converter_testcase extends advanced_testcase {
         $fileids = $fileman->get_fileids();
         $this->assertEquals(gettype($fileids), 'array');
         $this->assertEquals(0, count($fileids));
+        // try to migrate an invalid file
+        $fileman->itemid = 1;
+        $thrown = false;
+        try {
+            $fileman->migrate_file('/../../../../../../../../../../../../../../etc/passwd');
+        } catch (moodle1_convert_exception $e) {
+            $thrown = true;
+        }
+        $this->assertTrue($thrown);
         // migrate a single file
         $fileman->itemid = 4;
         $fileman->migrate_file('moddata/unittest/4/icon.gif');
@@ -435,6 +444,8 @@ class moodle1_converter_testcase extends advanced_testcase {
 
         $text = 'This is a text containing links to file.php
 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif" /><a href="$@FILEPHP@$$@SLASH@$pics$@SLASH@$news.gif$@FORCEDOWNLOAD@$">download image</a><br />
+    <div><a href=\'$@FILEPHP@$/../../../../../../../../../../../../../../../etc/passwd\'>download passwords</a></div>
+    <div><a href=\'$@FILEPHP@$$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$etc$@SLASH@$shadow\'>download shadows</a></div>
     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />';
 
         $files = moodle1_converter::find_referenced_files($text);
@@ -446,6 +457,8 @@ as it is parsed from the backup file. <br /><br /><img border="0" width="110" vs
         $text = moodle1_converter::rewrite_filephp_usage($text, array('/pics/news.gif', '/another/file/notused.txt'));
         $this->assertEquals($text, 'This is a text containing links to file.php
 as it is parsed from the backup file. <br /><br /><img border="0" width="110" vspace="0" hspace="0" height="92" title="News" alt="News" src="@@PLUGINFILE@@/pics/news.gif" /><a href="@@PLUGINFILE@@/pics/news.gif?forcedownload=1">download image</a><br />
+    <div><a href=\'$@FILEPHP@$/../../../../../../../../../../../../../../../etc/passwd\'>download passwords</a></div>
+    <div><a href=\'$@FILEPHP@$$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$..$@SLASH@$etc$@SLASH@$shadow\'>download shadows</a></div>
     <br /><a href=\'$@FILEPHP@$$@SLASH@$MANUAL.DOC$@FORCEDOWNLOAD@$\'>download manual</a><br />');
     }
 
index babbcd0..b7da2d3 100644 (file)
@@ -297,8 +297,13 @@ class restore_gradebook_structure_step extends restore_structure_step {
 
         $data->courseid = $this->get_courseid();
 
-        $newitemid = $DB->insert_record('grade_settings', $data);
-        //$this->set_mapping('grade_setting', $oldid, $newitemid);
+        if (!$DB->record_exists('grade_settings', array('courseid' => $data->courseid, 'name' => $data->name))) {
+            $newitemid = $DB->insert_record('grade_settings', $data);
+        } else {
+            $newitemid = $data->id;
+        }
+
+        $this->set_mapping('grade_setting', $oldid, $newitemid);
     }
 
     /**
@@ -1817,6 +1822,14 @@ class restore_filters_structure_step extends restore_structure_step {
 
         $data = (object)$data;
 
+        if (strpos($data->filter, 'filter/') === 0) {
+            $data->filter = substr($data->filter, 7);
+
+        } else if (strpos($data->filter, '/') !== false) {
+            // Unsupported old filter.
+            return;
+        }
+
         if (!filter_is_enabled($data->filter)) { // Not installed or not enabled, nothing to do
             return;
         }
@@ -1827,6 +1840,14 @@ class restore_filters_structure_step extends restore_structure_step {
 
         $data = (object)$data;
 
+        if (strpos($data->filter, 'filter/') === 0) {
+            $data->filter = substr($data->filter, 7);
+
+        } else if (strpos($data->filter, '/') !== false) {
+            // Unsupported old filter.
+            return;
+        }
+
         if (!filter_is_enabled($data->filter)) { // Not installed or not enabled, nothing to do
             return;
         }
index d098938..12f0ea1 100644 (file)
@@ -38,6 +38,10 @@ class block_completionstatus extends block_base {
         $this->title = get_string('pluginname', 'block_completionstatus');
     }
 
+    function applicable_formats() {
+        return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
+    }
+
     public function get_content() {
         global $USER;
 
index 8220ecc..8d38ec8 100644 (file)
@@ -26,16 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
-    'block/completionstatus:myaddinstance' => array(
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_SYSTEM,
-        'archetypes' => array(
-            'user' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
     'block/completionstatus:addinstance' => array(
         'riskbitmask' => RISK_SPAM | RISK_XSS,
 
diff --git a/blocks/completionstatus/db/upgrade.php b/blocks/completionstatus/db/upgrade.php
new file mode 100644 (file)
index 0000000..b5bbe8c
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file keeps track of upgrades to the completion status block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installation to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2012 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Handles upgrading instances of this block.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_completionstatus_upgrade($oldversion, $block) {
+    global $DB;
+
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2012112901) {
+        // Get the instances of this block.
+        if ($blocks = $DB->get_records('block_instances', array('blockname' => 'completionstatus', 'pagetypepattern' => 'my-index'))) {
+            // Loop through and remove them from the My Moodle page.
+            foreach ($blocks as $block) {
+                blocks_delete_instance($block);
+            }
+
+        }
+
+        // Savepoint reached.
+        upgrade_block_savepoint(true, 2012112901, 'completionstatus');
+    }
+
+
+    return true;
+}
\ No newline at end of file
index 3c90759..e389743 100644 (file)
@@ -25,7 +25,6 @@
 
 $string['completionprogressdetails'] = 'Completion progress details';
 $string['completionstatus:addinstance'] = 'Add a new course completion status block';
-$string['completionstatus:myaddinstance'] = 'Add a new course completion status block to the My Moodle page';
 $string['criteriagroup'] = 'Criteria group';
 $string['firstofsecond'] = '{$a->first} of {$a->second}';
 $string['pluginname'] = 'Course completion status';
index b97ad8e..218f326 100644 (file)
@@ -26,7 +26,7 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version      = 2012112900; // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version      = 2012112901; // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires     = 2012112900; // Requires this Moodle version
 $plugin->component    = 'block_completionstatus';
-$plugin->dependencies = array('report_completion' => 2012112900);
\ No newline at end of file
+$plugin->dependencies = array('report_completion' => 2012112900);
index cac6591..504c626 100644 (file)
@@ -5,6 +5,10 @@ class block_course_summary extends block_base {
         $this->title = get_string('pluginname', 'block_course_summary');
     }
 
+    function applicable_formats() {
+        return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
+    }
+
     function specialization() {
         if($this->page->pagetype == PAGE_COURSE_VIEW && $this->page->course->id != SITEID) {
             $this->title = get_string('coursesummary', 'block_course_summary');
index 5613ae7..a1e4265 100644 (file)
@@ -26,16 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
-    'block/course_summary:myaddinstance' => array(
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_SYSTEM,
-        'archetypes' => array(
-            'user' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
     'block/course_summary:addinstance' => array(
         'riskbitmask' => RISK_SPAM | RISK_XSS,
 
diff --git a/blocks/course_summary/db/upgrade.php b/blocks/course_summary/db/upgrade.php
new file mode 100644 (file)
index 0000000..7a16a1f
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file keeps track of upgrades to the course summary block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installation to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2012 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Handles upgrading instances of this block.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_course_summary_upgrade($oldversion, $block) {
+    global $DB;
+
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2012112901) {
+        // Get the instances of this block.
+        if ($blocks = $DB->get_records('block_instances', array('blockname' => 'course_summary', 'pagetypepattern' => 'my-index'))) {
+            // Loop through and remove them from the My Moodle page.
+            foreach ($blocks as $block) {
+                blocks_delete_instance($block);
+            }
+
+        }
+
+        // Savepoint reached.
+        upgrade_block_savepoint(true, 2012112901, 'course_summary');
+    }
+
+
+    return true;
+}
\ No newline at end of file
index 836cbfc..92adcf9 100644 (file)
@@ -25,5 +25,4 @@
 
 $string['coursesummary'] = 'Course summary';
 $string['course_summary:addinstance'] = 'Add a new course/site description block';
-$string['course_summary:myaddinstance'] = 'Add a new course/site description block to the My Moodle page';
 $string['pluginname'] = 'Course/Site description';
index 38cb1cf..d036ba1 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2012112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012112900;        // Requires this Moodle version
 $plugin->component = 'block_course_summary'; // Full name of the plugin (used for diagnostics)
index 73249e9..46c0479 100644 (file)
@@ -936,7 +936,7 @@ M.core_dock.genericblock.prototype = {
             }, this);
             // Add a close icon
             // Must set the image src seperatly of we get an error with XML strict headers
-            var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="" /></span>');
+            var closeicon = Y.Node.create('<span class="hidepanelicon" tabindex="0"><img alt="'+M.str.block.hidepanel+'" title="'+M.str.block.hidedockpanel+'" /></span>');
             closeicon.one('img').setAttribute('src', M.util.image_url('t/dockclose', 'moodle'));
             closeicon.on('forceclose|click', this.hide, this);
             closeicon.on('dock:actionkey',this.hide, this, {actions:{enter:true,toggle:true}});
index 1eccd11..af9c550 100644 (file)
@@ -5,10 +5,15 @@ define('BGR_LASTMODIFIED', '1');
 define('BGR_NEXTONE',      '2');
 
 class block_glossary_random extends block_base {
+
     function init() {
         $this->title = get_string('pluginname','block_glossary_random');
     }
 
+    function applicable_formats() {
+        return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
+    }
+
     function specialization() {
         global $CFG, $DB;
 
index 0c1acd6..e7bb687 100644 (file)
@@ -26,16 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
-    'block/glossary_random:myaddinstance' => array(
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_SYSTEM,
-        'archetypes' => array(
-            'user' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
     'block/glossary_random:addinstance' => array(
         'riskbitmask' => RISK_SPAM | RISK_XSS,
 
diff --git a/blocks/glossary_random/db/upgrade.php b/blocks/glossary_random/db/upgrade.php
new file mode 100644 (file)
index 0000000..81aeabe
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file keeps track of upgrades to the glossary random block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installation to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2012 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Handles upgrading instances of this block.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_glossary_random_upgrade($oldversion, $block) {
+    global $DB;
+
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2012112901) {
+        // Get the instances of this block.
+        if ($blocks = $DB->get_records('block_instances', array('blockname' => 'glossary_random', 'pagetypepattern' => 'my-index'))) {
+            // Loop through and remove them from the My Moodle page.
+            foreach ($blocks as $block) {
+                blocks_delete_instance($block);
+            }
+
+        }
+
+        // Savepoint reached.
+        upgrade_block_savepoint(true, 2012112901, 'glossary_random');
+    }
+
+
+    return true;
+}
\ No newline at end of file
index ddb483d..3c51dfa 100644 (file)
@@ -28,7 +28,6 @@ $string['askaddentry'] = 'When users can add entries to the glossary, show a lin
 $string['askinvisible'] = 'When users cannot edit or view the glossary, show this text (without link)';
 $string['askviewglossary'] = 'When users can view the glossary but not add entries, show a link with this text';
 $string['glossary_random:addinstance'] = 'Add a new random glossary entry block';
-$string['glossary_random:myaddinstance'] = 'Add a new random glossary entry block to the My Moodle page';
 $string['intro'] = 'Make sure you have at least one glossary with at least one entry added to this course. Then you can adjust the following settings';
 $string['invisible'] = '(to be continued)';
 $string['lastmodified'] = 'Last modified entry';
index bcca83b..061ec44 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2012112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012112900;        // Requires this Moodle version
 $plugin->component = 'block_glossary_random'; // Full name of the plugin (used for diagnostics)
index aa340af..85f48f3 100644 (file)
@@ -7,7 +7,7 @@ class block_mentees extends block_base {
     }
 
     function applicable_formats() {
-        return array('all' => true, 'tag' => false);
+        return array('all' => true, 'tag' => false, 'my' => false);
     }
 
     function specialization() {
index ba14b07..489ceb3 100644 (file)
@@ -26,16 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
-    'block/mentees:myaddinstance' => array(
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_SYSTEM,
-        'archetypes' => array(
-            'user' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
     'block/mentees:addinstance' => array(
         'riskbitmask' => RISK_SPAM | RISK_XSS,
 
diff --git a/blocks/mentees/db/upgrade.php b/blocks/mentees/db/upgrade.php
new file mode 100644 (file)
index 0000000..e8aba71
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file keeps track of upgrades to the mentees block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installation to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2012 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Handles upgrading instances of this block.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_mentees_upgrade($oldversion, $block) {
+    global $DB;
+
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2012112901) {
+        // Get the instances of this block.
+        if ($blocks = $DB->get_records('block_instances', array('blockname' => 'mentees', 'pagetypepattern' => 'my-index'))) {
+            // Loop through and remove them from the My Moodle page.
+            foreach ($blocks as $block) {
+                blocks_delete_instance($block);
+            }
+
+        }
+
+        // Savepoint reached.
+        upgrade_block_savepoint(true, 2012112901, 'mentees');
+    }
+
+
+    return true;
+}
\ No newline at end of file
index 3a22057..1c0cdab 100644 (file)
@@ -27,6 +27,5 @@ $string['configtitle'] = 'Block title';
 $string['configtitleblankhides'] = 'Block title (no title if blank)';
 $string['leaveblanktohide'] = 'leave blank to hide the title';
 $string['mentees:addinstance'] = 'Add a new mentees block';
-$string['mentees:myaddinstance'] = 'Add a new mentees block to the My Moodle page';
 $string['newmenteesblock'] = '(new Mentees block)';
 $string['pluginname'] = 'Mentees';
index 8ead4fc..c3a8db3 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2012112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012112900;        // Requires this Moodle version
 $plugin->component = 'block_mentees';   // Full name of the plugin (used for diagnostics)
index 5ecbec2..e803c6e 100644 (file)
@@ -5,6 +5,10 @@ class block_news_items extends block_base {
         $this->title = get_string('pluginname', 'block_news_items');
     }
 
+    function applicable_formats() {
+        return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
+    }
+
     function get_content() {
         global $CFG, $USER;
 
index 86bb1b7..452158e 100644 (file)
@@ -26,16 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
-    'block/news_items:myaddinstance' => array(
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_SYSTEM,
-        'archetypes' => array(
-            'user' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
     'block/news_items:addinstance' => array(
         'riskbitmask' => RISK_SPAM | RISK_XSS,
 
diff --git a/blocks/news_items/db/upgrade.php b/blocks/news_items/db/upgrade.php
new file mode 100644 (file)
index 0000000..e634e57
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file keeps track of upgrades to the latest news block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installation to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2012 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Handles upgrading instances of this block.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_news_items_upgrade($oldversion, $block) {
+    global $DB;
+
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2012112901) {
+        // Get the instances of this block.
+        if ($blocks = $DB->get_records('block_instances', array('blockname' => 'news_items', 'pagetypepattern' => 'my-index'))) {
+            // Loop through and remove them from the My Moodle page.
+            foreach ($blocks as $block) {
+                blocks_delete_instance($block);
+            }
+
+        }
+
+        // Savepoint reached.
+        upgrade_block_savepoint(true, 2012112901, 'news_items');
+    }
+
+
+    return true;
+}
\ No newline at end of file
index d1ab3d8..1de6507 100644 (file)
@@ -24,5 +24,4 @@
  */
 
 $string['news_items:addinstance'] = 'Add a new latest news block';
-$string['news_items:myaddinstance'] = 'Add a new navigation block to the My Moodle page';
 $string['pluginname'] = 'Latest news';
index 4cdea31..11d74ca 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2012112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012112900;        // Requires this Moodle version
 $plugin->component = 'block_news_items'; // Full name of the plugin (used for diagnostics)
index 77f8a69..6d14ebb 100644 (file)
@@ -10,7 +10,13 @@ class block_online_users extends block_base {
         $this->title = get_string('pluginname','block_online_users');
     }
 
-    function has_config() {return true;}
+    function applicable_formats() {
+        return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
+    }
+
+    function has_config() {
+        return true;
+    }
 
     function get_content() {
         global $USER, $CFG, $DB, $OUTPUT;
index f238b73..41959c4 100644 (file)
@@ -26,16 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
-    'block/online_users:myaddinstance' => array(
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_SYSTEM,
-        'archetypes' => array(
-            'user' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
     'block/online_users:addinstance' => array(
         'riskbitmask' => RISK_SPAM | RISK_XSS,
 
diff --git a/blocks/online_users/db/upgrade.php b/blocks/online_users/db/upgrade.php
new file mode 100644 (file)
index 0000000..f45a788
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file keeps track of upgrades to the online users block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installation to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2012 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Handles upgrading instances of this block.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_online_users_upgrade($oldversion, $block) {
+    global $DB;
+
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2012112901) {
+        // Get the instances of this block.
+        if ($blocks = $DB->get_records('block_instances', array('blockname' => 'online_users', 'pagetypepattern' => 'my-index'))) {
+            // Loop through and remove them from the My Moodle page.
+            foreach ($blocks as $block) {
+                blocks_delete_instance($block);
+            }
+
+        }
+
+        // Savepoint reached.
+        upgrade_block_savepoint(true, 2012112901, 'online_users');
+    }
+
+
+    return true;
+}
\ No newline at end of file
index 034fce5..b7a008e 100644 (file)
@@ -25,7 +25,6 @@
 
 $string['configtimetosee'] = 'Number of minutes determining the period of inactivity after which a user is no longer considered to be online.';
 $string['online_users:addinstance'] = 'Add a new online users block';
-$string['online_users:myaddinstance'] = 'Add a new online users block to the My Moodle page';
 $string['online_users:viewlist'] = 'View list of online users';
 $string['periodnminutes'] = 'last {$a} minutes';
 $string['pluginname'] = 'Online users';
index 18e1354..4463c45 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2012112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012112900;        // Requires this Moodle version
 $plugin->component = 'block_online_users'; // Full name of the plugin (used for diagnostics)
index 8e28ac2..c23d7b9 100644 (file)
@@ -78,6 +78,9 @@ class restore_rss_client_block_structure_step extends restore_structure_step {
         $configdata = $DB->get_field('block_instances', 'configdata', array('id' => $this->task->get_blockid()));
         // Extract configdata
         $config = unserialize(base64_decode($configdata));
+        if (empty($config)) {
+            $config = new stdClass();
+        }
         // Set array of used rss feeds
         $config->rssid = $feedsarr;
         // Serialize back the configdata
index 7a44dfe..9ea6db4 100644 (file)
@@ -36,7 +36,11 @@ require_once($CFG->libdir.'/completionlib.php');
 class block_selfcompletion extends block_base {
 
     public function init() {
-        $this->title   = get_string('pluginname', 'block_selfcompletion');
+        $this->title = get_string('pluginname', 'block_selfcompletion');
+    }
+
+    function applicable_formats() {
+        return array('all' => true, 'mod' => false, 'tag' => false, 'my' => false);
     }
 
     public function get_content() {
index 489eebd..d91d4d4 100644 (file)
@@ -26,16 +26,6 @@ defined('MOODLE_INTERNAL') || die();
 
 $capabilities = array(
 
-    'block/selfcompletion:myaddinstance' => array(
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_SYSTEM,
-        'archetypes' => array(
-            'user' => CAP_ALLOW
-        ),
-
-        'clonepermissionsfrom' => 'moodle/my:manageblocks'
-    ),
-
     'block/selfcompletion:addinstance' => array(
         'riskbitmask' => RISK_SPAM | RISK_XSS,
 
diff --git a/blocks/selfcompletion/db/upgrade.php b/blocks/selfcompletion/db/upgrade.php
new file mode 100644 (file)
index 0000000..fd03025
--- /dev/null
@@ -0,0 +1,69 @@
+<?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/>.
+
+/**
+ * This file keeps track of upgrades to the self completion block
+ *
+ * Sometimes, changes between versions involve alterations to database structures
+ * and other major things that may break installations.
+ *
+ * The upgrade function in this file will attempt to perform all the necessary
+ * actions to upgrade your older installation to the current version.
+ *
+ * If there's something it cannot do itself, it will tell you what you need to do.
+ *
+ * The commands in here will all be database-neutral, using the methods of
+ * database_manager class
+ *
+ * Please do not forget to use upgrade_set_timeout()
+ * before any action that may take longer time to finish.
+ *
+ * @since 2.0
+ * @package blocks
+ * @copyright 2012 Mark Nelson <markn@moodle.com>
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Handles upgrading instances of this block.
+ *
+ * @param int $oldversion
+ * @param object $block
+ */
+function xmldb_block_selfcompletion_upgrade($oldversion, $block) {
+    global $DB;
+
+    // Moodle v2.4.0 release upgrade line
+    // Put any upgrade step following this.
+
+    if ($oldversion < 2012112901) {
+        // Get the instances of this block.
+        if ($blocks = $DB->get_records('block_instances', array('blockname' => 'selfcompletion', 'pagetypepattern' => 'my-index'))) {
+            // Loop through and remove them from the My Moodle page.
+            foreach ($blocks as $block) {
+                blocks_delete_instance($block);
+            }
+
+        }
+
+        // Savepoint reached.
+        upgrade_block_savepoint(true, 2012112901, 'selfcompletion');
+    }
+
+
+    return true;
+}
\ No newline at end of file
index e7c89d3..457c77c 100644 (file)
@@ -28,4 +28,3 @@ $string['completecourse'] = 'Complete course';
 $string['pluginname'] = 'Self completion';
 $string['selfcompletionnotenabled'] = 'The self completion criteria has not been enabled for this course';
 $string['selfcompletion:addinstance'] = 'Add a new self completion block';
-$string['selfcompletion:myaddinstance'] = 'Add a new self completion block to the My Moodle page';
index 517c84b..2a0d036 100644 (file)
@@ -25,6 +25,6 @@
 
 defined('MOODLE_INTERNAL') || die();
 
-$plugin->version   = 2012112900;        // The current plugin version (Date: YYYYMMDDXX)
+$plugin->version   = 2012112901;        // The current plugin version (Date: YYYYMMDDXX)
 $plugin->requires  = 2012112900;        // Requires this Moodle version
 $plugin->component = 'block_selfcompletion'; // Full name of the plugin (used for diagnostics)
index bc5c265..e6bde82 100644 (file)
@@ -33,7 +33,7 @@ class block_tags_edit_form extends block_edit_form {
         // Fields for editing HTML block title and contents.
         $mform->addElement('header', 'configheader', get_string('blocksettings', 'block'));
 
-        $mform->addElement('text', 'config_title', get_string('pluginname', 'block_tags'));
+        $mform->addElement('text', 'config_title', get_string('configtitle', 'block_tags'));
         $mform->setType('config_title', PARAM_TEXT);
         $mform->setDefault('config_title', get_string('pluginname', 'block_tags'));
 
index 7f2d59f..6dcb0b9 100644 (file)
@@ -25,6 +25,7 @@
 $string['add'] = 'Add';
 $string['alltags'] = 'All tags:';
 $string['arrowtitle'] = 'Click here to enter the suggested text (grey letters).';
+$string['configtitle'] = 'Block title';
 $string['coursetags'] = 'Course tags:';
 $string['disabledtags'] = 'Tags are disabled';
 $string['defaultdisplay'] = 'Tag type to display';
index b6195bd..73cd5ee 100644 (file)
@@ -163,6 +163,13 @@ function blog_rss_get_feed($context, $args) {
         return '';
     }
 
+    if ($CFG->bloglevel == BLOG_SITE_LEVEL) {
+        if (isguestuser()) {
+            debugging(get_string('nopermissiontoshow','error'));
+            return '';
+        }
+    }
+
     $sitecontext = context_system::instance();
     if (!has_capability('moodle/blog:view', $sitecontext)) {
         return null;
index 74014c4..028a31e 100644 (file)
@@ -1093,6 +1093,10 @@ class cache_application extends cache implements cache_loader_with_locking {
             $todelete = array();
             // Iterate the returned data for the events.
             foreach ($events as $event => $keys) {
+                if ($keys === false) {
+                    // There are no keys.
+                    continue;
+                }
                 // Look at each key and check the timestamp.
                 foreach ($keys as $key => $timestamp) {
                     // If the timestamp of the event is more than or equal to the last invalidation (happened between the last
index dcbdbd7..7281fad 100644 (file)
@@ -357,6 +357,7 @@ class cachestore_static extends static_data_store implements cache_is_key_aware
      */
     public function purge() {
         $this->flush_store_by_id($this->storeid);
+        $this->store = &self::register_store_id($this->storeid);
     }
 
     /**
index 86ca22f..dc56f91 100644 (file)
@@ -2977,6 +2977,48 @@ function calendar_update_subscription_events($subscriptionid) {
     return $return;
 }
 
+/**
+ * Checks to see if the user can edit a given subscription feed.
+ *
+ * @param mixed $subscriptionorid Subscription object or id
+ * @return bool true if current user can edit the subscription else false
+ */
+function calendar_can_edit_subscription($subscriptionorid) {
+    global $DB;
+
+    if (is_array($subscriptionorid)) {
+        $subscription = (object)$subscriptionorid;
+    } else if (is_object($subscriptionorid)) {
+        $subscription = $subscriptionorid;
+    } else {
+        $subscription = $DB->get_record('event_subscriptions', array('id' => $subscriptionorid), '*', MUST_EXIST);
+    }
+    $allowed = new stdClass;
+    $courseid = $subscription->courseid;
+    $groupid = $subscription->groupid;
+    calendar_get_allowed_types($allowed, $courseid);
+    switch ($subscription->eventtype) {
+        case 'user':
+            return $allowed->user;
+        case 'course':
+            if (isset($allowed->courses[$courseid])) {
+                return $allowed->courses[$courseid];
+            } else {
+                return false;
+            }
+        case 'site':
+            return $allowed->site;
+        case 'group':
+            if (isset($allowed->groups[$groupid])) {
+                return $allowed->groups[$groupid];
+            } else {
+                return false;
+            }
+        default:
+            return false;
+    }
+}
+
 /**
  * Update calendar subscriptions.
  *
index 95479ef..cc726f1 100644 (file)
@@ -78,11 +78,15 @@ if (!empty($formdata)) {
         $importresults = calendar_update_subscription_events($subscriptionid);
     }
     // Redirect to prevent refresh issues.
-    redirect($PAGE->url);
+    redirect($PAGE->url, $importresults);
 } else if (!empty($subscriptionid)) {
     // The user is wanting to perform an action upon an existing subscription.
     require_sesskey(); // Must have sesskey for all actions.
-    $importresults = calendar_process_subscription_row($subscriptionid, $pollinterval, $action);
+    if (calendar_can_edit_subscription($subscriptionid)) {
+        $importresults = calendar_process_subscription_row($subscriptionid, $pollinterval, $action);
+    } else {
+        print_error('nopermissions', 'error', $PAGE->url, get_string('managesubscriptions', 'calendar'));
+    }
 }
 
 $sql = 'SELECT *
index 5a18073..b7cbd79 100644 (file)
@@ -736,6 +736,7 @@ class core_calendar_renderer extends plugin_renderer_base {
         $table->head  = array(
             get_string('colcalendar', 'calendar'),
             get_string('collastupdated', 'calendar'),
+            get_string('eventkind', 'calendar'),
             get_string('colpoll', 'calendar'),
             get_string('colactions', 'calendar')
         );
@@ -762,10 +763,12 @@ class core_calendar_renderer extends plugin_renderer_base {
 
             $cell = new html_table_cell($this->subscription_action_form($sub, $courseid));
             $cell->colspan = 2;
+            $type = $sub->eventtype . 'events';
 
             $table->data[] = new html_table_row(array(
                 new html_table_cell($label),
                 new html_table_cell($lastupdated),
+                new html_table_cell(get_string($type, 'calendar')),
                 $cell
             ));
         }
index b7a39a8..a960293 100644 (file)
@@ -38,7 +38,7 @@ $action    = optional_param('action',    '',  PARAM_ALPHA);
 $area      = optional_param('area',      '',  PARAM_AREA);
 $content   = optional_param('content',   '',  PARAM_RAW);
 $itemid    = optional_param('itemid',    '',  PARAM_INT);
-$returnurl = optional_param('returnurl', '/', PARAM_URL);
+$returnurl = optional_param('returnurl', '/', PARAM_LOCALURL);
 $component = optional_param('component', '',  PARAM_COMPONENT);
 
 // Currently this script can only add comments
index 33bc709..37138b5 100644 (file)
@@ -191,6 +191,7 @@ if ($editingon && can_edit_in_category()) {
     // Integrate into the admin tree only if the user can edit categories at the top level,
     // otherwise the admin block does not appear to this user, and you get an error.
     require_once($CFG->libdir . '/adminlib.php');
+    navigation_node::override_active_url(new moodle_url('/course/category.php', array('id' => $id)));
     admin_externalpage_setup('coursemgmt', '', $urlparams, $CFG->wwwroot . '/course/category.php');
     $PAGE->set_context($context);   // Ensure that we are actually showing blocks etc for the cat context
 
index 6fb00b0..21dc7a7 100644 (file)
@@ -555,6 +555,15 @@ class dndupload_ajax_processor {
         $this->cm->groupmode = $this->course->groupmode;
         $this->cm->groupingid = $this->course->defaultgroupingid;
 
+        // Set the correct default for completion tracking.
+        $this->cm->completion = COMPLETION_TRACKING_NONE;
+        $completion = new completion_info($this->course);
+        if ($completion->is_enabled()) {
+            if (plugin_supports('mod', $this->cm->modulename, FEATURE_MODEDIT_DEFAULT_COMPLETION, true)) {
+                $this->cm->completion = COMPLETION_TRACKING_MANUAL;
+            }
+        }
+
         if (!$this->cm->id = add_course_module($this->cm)) {
             throw new coding_exception("Unable to create the course module");
         }
index f2d90db..a58b86a 100644 (file)
@@ -1516,8 +1516,9 @@ function print_section($course, $section, $mods, $modnamesused, $absolute=false,
                     $textcss = '';
                 }
 
-                // Get on-click attribute value if specified
-                $onclick = $mod->get_on_click();
+                // Get on-click attribute value if specified and decode the onclick - it
+                // has already been encoded for display (puke).
+                $onclick = htmlspecialchars_decode($mod->get_on_click(), ENT_QUOTES);
 
                 $groupinglabel = '';
                 if (!empty($mod->groupingid) && has_capability('moodle/course:managegroups', context_course::instance($course->id))) {
index 719762e..2b756b6 100644 (file)
@@ -35,7 +35,7 @@ require_once($CFG->dirroot.'/course/lib.php');
 
 $id         = required_param('id', PARAM_INT);
 $switchrole = optional_param('switchrole',-1, PARAM_INT);
-$returnurl  = optional_param('returnurl', false, PARAM_URL);
+$returnurl  = optional_param('returnurl', false, PARAM_LOCALURL);
 
 $PAGE->set_url('/course/switchrole.php', array('id'=>$id));
 
@@ -84,4 +84,4 @@ if ($returnurl === false) {
     $returnurl = new moodle_url('/course/view.php', array('id' => $course->id));
 }
 
-redirect($returnurl);
\ No newline at end of file
+redirect($returnurl);
index 7db80b8..0702c9e 100644 (file)
@@ -32,7 +32,7 @@
 
 define('CLI_SCRIPT', true);
 
-require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
+require(__DIR__.'/../../../config.php');
 require_once("$CFG->dirroot/enrol/category/locallib.php");
 require_once("$CFG->libdir/clilib.php");
 
@@ -49,7 +49,7 @@ if ($options['help']) {
         "Execute course category enrolment sync.
 
 Options:
--v, --verbose         Print verbose progess information
+-v, --verbose         Print verbose progress information
 -h, --help            Print out this help
 
 Example:
@@ -64,5 +64,12 @@ if (!enrol_is_enabled('category')) {
     cli_error('enrol_category plugin is disabled, synchronisation stopped', 2);
 }
 
-$verbose = !empty($options['verbose']);
-return enrol_category_sync_full($verbose);
+if (empty($options['verbose'])) {
+    $trace = new null_progress_trace();
+} else {
+    $trace = new text_progress_trace();
+}
+
+$result = enrol_category_sync_full($trace);
+
+exit($result);
index 4fed790..b806eae 100644 (file)
@@ -70,7 +70,8 @@ class enrol_category_plugin extends enrol_plugin {
         }
 
         require_once("$CFG->dirroot/enrol/category/locallib.php");
-        enrol_category_sync_full();
+        $trace = new null_progress_trace();
+        enrol_category_sync_full($trace);
     }
 
     /**
index 9e4ab0c..69ec05e 100644 (file)
@@ -252,14 +252,15 @@ function enrol_category_sync_course($course) {
  * - reorder categories
  * - disable enrol_category and enable it again
  *
- * @param bool $verbose
+ * @param progress_trace $trace
  * @return int exit code - 0 is ok, 1 means error, 2 if plugin disabled
  */
-function enrol_category_sync_full($verbose = false) {
+function enrol_category_sync_full(progress_trace $trace) {
     global $DB;
 
 
     if (!enrol_is_enabled('category')) {
+        $trace->finished();
         return 2;
     }
 
@@ -273,23 +274,20 @@ function enrol_category_sync_full($verbose = false) {
     // Any interesting roles worth synchronising?
     if (!$roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext)) {
         // yay, nothing to do, so let's remove all leftovers
-        if ($verbose) {
-            mtrace("No roles with 'enrol/category:synchronised' capability found.");
-        }
+        $trace->output("No roles with 'enrol/category:synchronised' capability found.");
         if ($instances = $DB->get_records('enrol', array('enrol'=>'category'))) {
+            $trace->output("Deleting all category enrol instances...");
             foreach ($instances as $instance) {
-                if ($verbose) {
-                    mtrace("  deleting category enrol instance from course {$instance->courseid}");
-                }
+                $trace->output("deleting category enrol instance from course {$instance->courseid}", 1);
                 $plugin->delete_instance($instance);
             }
+            $trace->output("...all instances deleted.");
         }
+        $trace->finished();
         return 0;
     }
     $rolenames = role_fix_names($roles, null, ROLENAME_SHORT, true);
-    if ($verbose) {
-        mtrace('Synchronising category enrolments for roles: '.implode(', ', $rolenames).'...');
-    }
+    $trace->output('Synchronising category enrolments for roles: '.implode(', ', $rolenames).'...');
 
     list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r');
     $params['courselevel'] = CONTEXT_COURSE;
@@ -357,9 +355,7 @@ function enrol_category_sync_full($verbose = false) {
         unset($instance->userid);
         unset($instance->estart);
         $plugin->enrol_user($instance, $userid, null, $estart);
-        if ($verbose) {
-            mtrace("  enrolling: user $userid ==> course $instance->courseid");
-        }
+        $trace->output("enrolling: user $userid ==> course $instance->courseid", 1);
     }
     $rs->close();
 
@@ -378,15 +374,12 @@ function enrol_category_sync_full($verbose = false) {
         $userid = $instance->userid;
         unset($instance->userid);
         $plugin->unenrol_user($instance, $userid);
-        if ($verbose) {
-            mtrace("  unenrolling: user $userid ==> course $instance->courseid");
-        }
+        $trace->output("unenrolling: user $userid ==> course $instance->courseid", 1);
     }
     $rs->close();
 
-    if ($verbose) {
-        mtrace('...user enrolment synchronisation finished.');
-    }
+    $trace->output('...user enrolment synchronisation finished.');
+    $trace->finished();
 
     return 0;
 }
index 3649764..ca8d4cd 100644 (file)
@@ -276,6 +276,8 @@ class enrol_category_testcase extends advanced_testcase {
 
         $this->resetAfterTest();
 
+        $trace = new null_progress_trace();
+
         // Setup a few courses and categories.
 
         $studentrole = $DB->get_record('role', array('shortname'=>'student'));
@@ -309,13 +311,13 @@ class enrol_category_testcase extends advanced_testcase {
         role_assign($managerrole->id, $user3->id, context_course::instance($course2->id));
         $this->assertEquals(0, $DB->count_records('user_enrolments', array()));
 
-        $result = enrol_category_sync_full();
+        $result = enrol_category_sync_full($trace);
         $this->assertSame(0, $result);
 
         $this->disable_plugin();
         role_assign($studentrole->id, $user1->id, context_coursecat::instance($cat2->id));
         $this->enable_plugin();
-        $result = enrol_category_sync_full();
+        $result = enrol_category_sync_full($trace);
         $this->assertSame(0, $result);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertTrue(is_enrolled(context_course::instance($course2->id), $user1->id));
@@ -329,7 +331,7 @@ class enrol_category_testcase extends advanced_testcase {
         role_assign($teacherrole->id, $user3->id, context_coursecat::instance($cat2->id));
         role_assign($managerrole->id, $user3->id, context_course::instance($course3->id));
         $this->enable_plugin();
-        $result = enrol_category_sync_full();
+        $result = enrol_category_sync_full($trace);
         $this->assertSame(0, $result);
         $this->assertEquals(5, $DB->count_records('user_enrolments', array()));
         $this->assertTrue(is_enrolled(context_course::instance($course1->id), $user2->id));
@@ -348,7 +350,7 @@ class enrol_category_testcase extends advanced_testcase {
         role_unassign_all(array('roleid'=>$managerrole->id));
         role_unassign_all(array('roleid'=>$teacherrole->id));
 
-        $result = enrol_category_sync_full();
+        $result = enrol_category_sync_full($trace);
         $this->assertSame(2, $result);
         $this->assertEquals(0, $DB->count_records('role_assignments', array()));
         $this->assertNotEmpty($DB->count_records('user_enrolments', array()));
@@ -356,7 +358,7 @@ class enrol_category_testcase extends advanced_testcase {
         $this->disable_role_sync($teacherrole->id);
 
         $this->enable_plugin();
-        $result = enrol_category_sync_full();
+        $result = enrol_category_sync_full($trace);
         $this->assertSame(0, $result);
         $this->assertEquals(0, $DB->count_records('role_assignments', array()));
         $this->assertEquals(0, $DB->count_records('user_enrolments', array()));
index 6eec6ce..42cc37e 100644 (file)
@@ -90,7 +90,9 @@ switch ($action) {
         }
         $enrol = enrol_get_plugin('cohort');
         $enrol->add_instance($manager->get_course(), array('customint1' => $cohortid, 'roleid' => $roleid));
-        enrol_cohort_sync($manager->get_course()->id);
+        $trace = new null_progress_trace();
+        enrol_cohort_sync($trace, $manager->get_course()->id);
+        $trace->finished();
         break;
     case 'enrolcohortusers':
         //TODO: this should be moved to enrol_manual, see MDL-35618.
index 4f1cd3b..032c34a 100644 (file)
@@ -30,8 +30,8 @@
 
 define('CLI_SCRIPT', true);
 
-require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
-require_once($CFG->libdir.'/clilib.php');
+require(__DIR__.'/../../../config.php');
+require_once("$CFG->libdir/clilib.php");
 require_once("$CFG->dirroot/enrol/cohort/locallib.php");
 
 // Now get cli options.
@@ -51,15 +51,20 @@ Options:
 -h, --help            Print out this help
 
 Example:
-\$sudo -u www-data /usr/bin/php enrol/cohort/cli/sync.php
+\$ sudo -u www-data /usr/bin/php enrol/cohort/cli/sync.php
 ";
 
     echo $help;
     die;
 }
 
-$verbose = !empty($options['verbose']);
+if (empty($options['verbose'])) {
+    $trace = new null_progress_trace();
+} else {
+    $trace = new text_progress_trace();
+}
 
-$result = enrol_cohort_sync(null, $verbose);
+$result = enrol_cohort_sync($trace, null);
+$trace->finished();
 
-exit($result);
\ No newline at end of file
+exit($result);
index ee97210..050bdff 100644 (file)
@@ -92,7 +92,9 @@ if ($mform->is_cancelled()) {
     }  else {
         $enrol->add_instance($course, array('name'=>$data->name, 'status'=>$data->status, 'customint1'=>$data->customint1, 'roleid'=>$data->roleid, 'customint2'=>$data->customint2));
     }
-    enrol_cohort_sync($course->id);
+    $trace = new null_progress_trace();
+    enrol_cohort_sync($trace, $course->id);
+    $trace->finished();
     redirect($returnurl);
 }
 
index 56dc5c9..a2370d4 100644 (file)
@@ -132,7 +132,9 @@ class enrol_cohort_plugin extends enrol_plugin {
         global $CFG;
 
         require_once("$CFG->dirroot/enrol/cohort/locallib.php");
-        enrol_cohort_sync();
+        $trace = new null_progress_trace();
+        enrol_cohort_sync($trace);
+        $trace->finished();
     }
 
     /**
@@ -160,7 +162,9 @@ class enrol_cohort_plugin extends enrol_plugin {
         parent::update_status($instance, $newstatus);
 
         require_once("$CFG->dirroot/enrol/cohort/locallib.php");
-        enrol_cohort_sync($instance->courseid);
+        $trace = new null_progress_trace();
+        enrol_cohort_sync($trace, $instance->courseid);
+        $trace->finished();
     }
 
     /**
@@ -279,7 +283,9 @@ class enrol_cohort_plugin extends enrol_plugin {
             $step->set_mapping('enrol', $oldid, $instanceid);
 
             require_once("$CFG->dirroot/enrol/cohort/locallib.php");
-            enrol_cohort_sync($course->id, false);
+            $trace = new null_progress_trace();
+            enrol_cohort_sync($trace, $course->id);
+            $trace->finished();
 
         } else if ($this->get_config('unenrolaction') == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
             $data->customint1 = 0;
@@ -294,7 +300,9 @@ class enrol_cohort_plugin extends enrol_plugin {
             $step->set_mapping('enrol', $oldid, $instanceid);
 
             require_once("$CFG->dirroot/enrol/cohort/locallib.php");
-            enrol_cohort_sync($course->id, false);
+            $trace = new null_progress_trace();
+            enrol_cohort_sync($trace, $course->id);
+            $trace->finished();
 
         } else {
             $step->set_mapping('enrol', $oldid, 0);
index 83dd982..4027af3 100644 (file)
@@ -151,19 +151,17 @@ class enrol_cohort_handler {
 
 /**
  * Sync all cohort course links.
+ * @param progress_trace $trace
  * @param int $courseid one course, empty mean all
- * @param bool $verbose verbose CLI output
  * @return int 0 means ok, 1 means error, 2 means plugin disabled
  */
-function enrol_cohort_sync($courseid = NULL, $verbose = false) {
+function enrol_cohort_sync(progress_trace $trace, $courseid = NULL) {
     global $CFG, $DB;
     require_once("$CFG->dirroot/group/lib.php");
 
     // Purge all roles if cohort sync disabled, those can be recreated later here by cron or CLI.
     if (!enrol_is_enabled('cohort')) {
-        if ($verbose) {
-            mtrace('Cohort sync plugin is disabled, unassigning all plugin roles and stopping.');
-        }
+        $trace->output('Cohort sync plugin is disabled, unassigning all plugin roles and stopping.');
         role_unassign_all(array('component'=>'enrol_cohort'));
         return 2;
     }
@@ -172,9 +170,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     @set_time_limit(0);
     raise_memory_limit(MEMORY_HUGE);
 
-    if ($verbose) {
-        mtrace('Starting user enrolment synchronisation...');
-    }
+    $trace->output('Starting user enrolment synchronisation...');
 
     $allroles = get_all_roles();
     $instances = array(); //cache
@@ -201,14 +197,10 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
         $instance = $instances[$ue->enrolid];
         if ($ue->status == ENROL_USER_SUSPENDED) {
             $plugin->update_user_enrol($instance, $ue->userid, ENROL_USER_ACTIVE);
-            if ($verbose) {
-                mtrace("  unsuspending: $ue->userid ==> $instance->courseid via cohort $instance->customint1");
-            }
+            $trace->output("unsuspending: $ue->userid ==> $instance->courseid via cohort $instance->customint1", 1);
         } else {
             $plugin->enrol_user($instance, $ue->userid);
-            if ($verbose) {
-                mtrace("  enrolling: $ue->userid ==> $instance->courseid via cohort $instance->customint1");
-            }
+            $trace->output("enrolling: $ue->userid ==> $instance->courseid via cohort $instance->customint1", 1);
         }
     }
     $rs->close();
@@ -229,9 +221,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
         if ($unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
             // Remove enrolment together with group membership, grades, preferences, etc.
             $plugin->unenrol_user($instance, $ue->userid);
-            if ($verbose) {
-                mtrace("  unenrolling: $ue->userid ==> $instance->courseid via cohort $instance->customint1");
-            }
+            $trace->output("unenrolling: $ue->userid ==> $instance->courseid via cohort $instance->customint1", 1);
 
         } else { // ENROL_EXT_REMOVED_SUSPENDNOROLES
             // Just disable and ignore any changes.
@@ -239,9 +229,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
                 $plugin->update_user_enrol($instance, $ue->userid, ENROL_USER_SUSPENDED);
                 $context = context_course::instance($instance->courseid);
                 role_unassign_all(array('userid'=>$ue->userid, 'contextid'=>$context->id, 'component'=>'enrol_cohort', 'itemid'=>$instance->id));
-                if ($verbose) {
-                    mtrace("  suspending and unsassigning all roles: $ue->userid ==> $instance->courseid");
-                }
+                $trace->output("suspending and unsassigning all roles: $ue->userid ==> $instance->courseid", 1);
             }
         }
     }
@@ -267,9 +255,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach($rs as $ra) {
         role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_cohort', $ra->itemid);
-        if ($verbose) {
-            mtrace("  assigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
-        }
+        $trace->output("assigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname, 1);
     }
     $rs->close();
 
@@ -291,9 +277,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach($rs as $ra) {
         role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_cohort', $ra->itemid);
-        if ($verbose) {
-            mtrace("  unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname);
-        }
+        $trace->output("unassigning role: $ra->userid ==> $ra->courseid as ".$allroles[$ra->roleid]->shortname, 1);
     }
     $rs->close();
 
@@ -314,9 +298,7 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach($rs as $gm) {
         groups_remove_member($gm->groupid, $gm->userid);
-        if ($verbose) {
-            mtrace("  removing user from group: $gm->userid ==> $gm->courseid - $gm->groupname");
-        }
+        $trace->output("removing user from group: $gm->userid ==> $gm->courseid - $gm->groupname", 1);
     }
     $rs->close();
 
@@ -333,16 +315,12 @@ function enrol_cohort_sync($courseid = NULL, $verbose = false) {
     $rs = $DB->get_recordset_sql($sql, $params);
     foreach($rs as $ue) {
         groups_add_member($ue->groupid, $ue->userid, 'enrol_cohort', $ue->enrolid);
-        if ($verbose) {
-            mtrace("  adding user to group: $ue->userid ==> $ue->courseid - $ue->groupname");
-        }
+        $trace->output("adding user to group: $ue->userid ==> $ue->courseid - $ue->groupname", 1);
     }
     $rs->close();
 
 
-    if ($verbose) {
-        mtrace('...user enrolment synchronisation finished.');
-    }
+    $trace->output('...user enrolment synchronisation finished.');
 
     return 0;
 }
index 75fc773..b21afc3 100644 (file)
@@ -260,6 +260,8 @@ class enrol_cohort_testcase extends advanced_testcase {
         global $DB;
         $this->resetAfterTest();
 
+        $trace = new null_progress_trace();
+
         // Setup a few courses and categories.
 
         $cohortplugin = enrol_get_plugin('cohort');
@@ -319,19 +321,19 @@ class enrol_cohort_testcase extends advanced_testcase {
 
         // Test sync of one course only.
 
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(2, $DB->count_records('role_assignments', array()));
         $this->assertEquals(2, $DB->count_records('user_enrolments', array()));
 
 
         $this->enable_plugin();
-        enrol_cohort_sync($course2->id, false);
+        enrol_cohort_sync($trace, $course2->id);
         $this->assertEquals(3, $DB->count_records('role_assignments', array()));
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $DB->delete_records('cohort_members', array('cohortid'=>$cohort3->id)); // Use low level DB api to prevent events!
         $DB->delete_records('cohort', array('id'=>$cohort3->id)); // Use low level DB api to prevent events!
 
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(7, $DB->count_records('user_enrolments', array()));
         $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
         $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user2->id)));
@@ -345,14 +347,14 @@ class enrol_cohort_testcase extends advanced_testcase {
 
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
         $DB->delete_records('cohort_members', array('cohortid'=>$cohort2->id, 'userid'=>$user3->id)); // Use low level DB api to prevent events!
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(7, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(6, $DB->count_records('role_assignments', array()));
         $this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
 
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
         $DB->delete_records('cohort_members', array('cohortid'=>$cohort1->id, 'userid'=>$user1->id)); // Use low level DB api to prevent events!
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(5, $DB->count_records('user_enrolments', array()));
         $this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
         $this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
@@ -363,12 +365,12 @@ class enrol_cohort_testcase extends advanced_testcase {
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
         $DB->delete_records('cohort_members', array('cohortid'=>$cohort1->id)); // Use low level DB api to prevent events!
         $DB->delete_records('cohort', array('id'=>$cohort1->id)); // Use low level DB api to prevent events!
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(5, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('role_assignments', array()));
 
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('role_assignments', array()));
 
@@ -400,7 +402,7 @@ class enrol_cohort_testcase extends advanced_testcase {
         cohort_add_member($cohort1->id, $user4->id);
         cohort_add_member($cohort2->id, $user4->id);
 
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
 
         $this->assertEquals(7, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(7, $DB->count_records('role_assignments', array()));
@@ -414,7 +416,7 @@ class enrol_cohort_testcase extends advanced_testcase {
         $cohortinstance1->customint2 = $group2->id;
         $DB->update_record('enrol', $cohortinstance1);
 
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertFalse(groups_is_member($group1->id, $user1->id));
         $this->assertTrue(groups_is_member($group2->id, $user1->id));
         $this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group2->id, 'userid'=>$user1->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
@@ -437,6 +439,8 @@ class enrol_cohort_testcase extends advanced_testcase {
 
         $this->resetAfterTest();
 
+        $trace = new null_progress_trace();
+
         // Setup a few courses and categories.
 
         $cohortplugin = enrol_get_plugin('cohort');
@@ -496,13 +500,13 @@ class enrol_cohort_testcase extends advanced_testcase {
 
         // Test sync of one course only.
 
-        enrol_cohort_sync(null, false);
+        enrol_cohort_sync($trace, null);
         $this->assertEquals(2, $DB->count_records('role_assignments', array()));
         $this->assertEquals(2, $DB->count_records('user_enrolments', array()));
 
 
         $this->enable_plugin();
-        enrol_cohort_sync(null, false);
+        enrol_cohort_sync($trace, null);
         $this->assertEquals(7, $DB->count_records('user_enrolments', array()));
         $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
         $this->assertTrue($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user2->id)));
@@ -516,14 +520,14 @@ class enrol_cohort_testcase extends advanced_testcase {
 
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
         $DB->delete_records('cohort_members', array('cohortid'=>$cohort2->id, 'userid'=>$user3->id)); // Use low level DB api to prevent events!
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(7, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(6, $DB->count_records('role_assignments', array()));
         $this->assertFalse($DB->record_exists('role_assignments', array('contextid'=>context_course::instance($course1->id)->id, 'userid'=>$user3->id, 'roleid'=>$teacherrole->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance2->id)));
 
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
         $DB->delete_records('cohort_members', array('cohortid'=>$cohort1->id, 'userid'=>$user1->id)); // Use low level DB api to prevent events!
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(5, $DB->count_records('user_enrolments', array()));
         $this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance2->id, 'userid'=>$user3->id)));
         $this->assertFalse($DB->record_exists('user_enrolments', array('enrolid'=>$cohortinstance1->id, 'userid'=>$user1->id)));
@@ -534,12 +538,12 @@ class enrol_cohort_testcase extends advanced_testcase {
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
         $DB->delete_records('cohort_members', array('cohortid'=>$cohort1->id)); // Use low level DB api to prevent events!
         $DB->delete_records('cohort', array('id'=>$cohort1->id)); // Use low level DB api to prevent events!
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(5, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('role_assignments', array()));
 
         $cohortplugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
-        enrol_cohort_sync($course1->id, false);
+        enrol_cohort_sync($trace, $course1->id);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('role_assignments', array()));
 
@@ -573,7 +577,7 @@ class enrol_cohort_testcase extends advanced_testcase {
 
         $this->enable_plugin();
 
-        enrol_cohort_sync(null, false);
+        enrol_cohort_sync($trace, null);
 
         $this->assertEquals(8, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(8, $DB->count_records('role_assignments', array()));
@@ -593,7 +597,7 @@ class enrol_cohort_testcase extends advanced_testcase {
         $cohortinstance3->customint2 = $group3->id;
         $DB->update_record('enrol', $cohortinstance3);
 
-        enrol_cohort_sync(null, false);
+        enrol_cohort_sync($trace, null);
         $this->assertFalse(groups_is_member($group1->id, $user1->id));
         $this->assertTrue(groups_is_member($group2->id, $user1->id));
         $this->assertTrue($DB->record_exists('groups_members', array('groupid'=>$group2->id, 'userid'=>$user1->id, 'component'=>'enrol_cohort', 'itemid'=>$cohortinstance1->id)));
index 420e3d7..890a0f4 100644 (file)
@@ -33,8 +33,8 @@
 
 define('CLI_SCRIPT', true);
 
-require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
-require_once($CFG->libdir.'/clilib.php');
+require(__DIR__.'/../../../config.php');
+require_once("$CFG->libdir/clilib.php");
 
 // Now get cli options.
 list($options, $unrecognized) = cli_get_params(array('verbose'=>false, 'help'=>false), array('v'=>'verbose', 'h'=>'help'));
@@ -50,15 +50,15 @@ if ($options['help']) {
 The enrol_database plugin must be enabled and properly configured.
 
 Options:
--v, --verbose         Print verbose progess information
+-v, --verbose         Print verbose progress information
 -h, --help            Print out this help
 
 Example:
-\$sudo -u www-data /usr/bin/php enrol/database/cli/sync.php
+\$ sudo -u www-data /usr/bin/php enrol/database/cli/sync.php
 
 Sample cron entry:
 # 5 minutes past 4am
-5 4 * * * \$sudo -u www-data /usr/bin/php /var/www/moodle/enrol/database/cli/sync.php
+5 4 * * * sudo -u www-data /usr/bin/php /var/www/moodle/enrol/database/cli/sync.php
 ";
 
     echo $help;
@@ -66,15 +66,20 @@ Sample cron entry:
 }
 
 if (!enrol_is_enabled('database')) {
-    echo('enrol_database plugin is disabled, sync is disabled'."\n");
-    exit(1);
+    cli_error('enrol_database plugin is disabled, synchronisation stopped', 2);
 }
 
-$verbose = !empty($options['verbose']);
+if (empty($options['verbose'])) {
+    $trace = new null_progress_trace();
+} else {
+    $trace = new text_progress_trace();
+}
+
+/** @var enrol_database_plugin $enrol  */
 $enrol = enrol_get_plugin('database');
 $result = 0;
 
-$result = $result | $enrol->sync_courses($verbose);
-$result = $result | $enrol->sync_enrolments($verbose);
+$result = $result | $enrol->sync_courses($trace);
+$result = $result | $enrol->sync_enrolments($trace);
 
-exit($result);
\ No newline at end of file
+exit($result);
index 398dfee..214c70a 100644 (file)
@@ -284,27 +284,25 @@ class enrol_database_plugin extends enrol_plugin {
     /**
      * Forces synchronisation of all enrolments with external database.
      *
-     * @param bool $verbose
+     * @param progress_trace $trace
      * @param null|int $onecourse limit sync to one course only (used primarily in restore)
      * @return int 0 means success, 1 db connect failure, 2 db read failure
      */
-    public function sync_enrolments($verbose = false, $onecourse = null) {
+    public function sync_enrolments(progress_trace $trace, $onecourse = null) {
         global $CFG, $DB;
 
         // We do not create courses here intentionally because it requires full sync and is slow.
         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('remoteenroltable') or !$this->get_config('remotecoursefield') or !$this->get_config('remoteuserfield')) {
-            if ($verbose) {
-                mtrace('User enrolment synchronisation skipped.');
-            }
+            $trace->output('User enrolment synchronisation skipped.');
+            $trace->finished();
             return 0;
         }
 
-        if ($verbose) {
-            mtrace('Starting user enrolment synchronisation...');
-        }
+        $trace->output('Starting user enrolment synchronisation...');
 
         if (!$extdb = $this->db_init()) {
-            mtrace('Error while communicating with external enrolment database');
+            $trace->output('Error while communicating with external enrolment database');
+            $trace->finished();
             return 1;
         }
 
@@ -380,13 +378,13 @@ class enrol_database_plugin extends enrol_plugin {
                 }
                 $rs->Close();
             } else {
-                mtrace('Error reading data from the external enrolment table');
+                $trace->output('Error reading data from the external enrolment table');
                 $extdb->Close();
                 return 2;
             }
             $preventfullunenrol = empty($externalcourses);
             if ($preventfullunenrol and $unenrolaction == ENROL_EXT_REMOVED_UNENROL) {
-                mtrace('  Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.');
+                $trace->output('Preventing unenrolment of all current users, because it might result in major data loss, there has to be at least one record in external enrol table, sorry.', 1);
             }
 
             // First find all existing courses with enrol instance.
@@ -431,9 +429,9 @@ class enrol_database_plugin extends enrol_plugin {
             $rs->close();
 
             // Print list of missing courses.
-            if ($verbose and $externalcourses) {
+            if ($externalcourses) {
                 $list = implode(', ', array_keys($externalcourses));
-                mtrace("  error: following courses do not exist - $list");
+                $trace->output("error: following courses do not exist - $list", 1);
                 unset($list);
             }
 
@@ -491,18 +489,14 @@ class enrol_database_plugin extends enrol_plugin {
                     while ($fields = $rs->FetchRow()) {
                         $fields = array_change_key_case($fields, CASE_LOWER);
                         if (empty($fields[$userfield_l])) {
-                            if ($verbose) {
-                                mtrace("  error: skipping user without mandatory $localuserfield in course '$course->mapping'");
-                            }
+                            $trace->output("error: skipping user without mandatory $localuserfield in course '$course->mapping'", 1);
                             continue;
                         }
                         $mapping = $fields[$userfield_l];
                         if (!isset($user_mapping[$mapping])) {
                             $usersearch[$localuserfield] = $mapping;
                             if (!$user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE)) {
-                                if ($verbose) {
-                                    mtrace("  error: skipping unknown user $localuserfield '$mapping' in course '$course->mapping'");
-                                }
+                                $trace->output("error: skipping unknown user $localuserfield '$mapping' in course '$course->mapping'", 1);
                                 continue;
                             }
                             $user_mapping[$mapping] = $user->id;
@@ -512,9 +506,7 @@ class enrol_database_plugin extends enrol_plugin {
                         }
                         if (empty($fields[$rolefield_l]) or !isset($roles[$fields[$rolefield_l]])) {
                             if (!$defaultrole) {
-                                if ($verbose) {
-                                    mtrace("  error: skipping user '$userid' in course '$course->mapping' - missing course and default role");
-                                }
+                                $trace->output("error: skipping user '$userid' in course '$course->mapping' - missing course and default role", 1);
                                 continue;
                             }
                             $roleid = $defaultrole;
@@ -527,7 +519,7 @@ class enrol_database_plugin extends enrol_plugin {
                 }
                 $rs->Close();
             } else {
-                mtrace("  error: skipping course '$course->mapping' - could not match with external database");
+                $trace->output("error: skipping course '$course->mapping' - could not match with external database", 1);
                 continue;
             }
             unset($user_mapping);
@@ -539,9 +531,7 @@ class enrol_database_plugin extends enrol_plugin {
                         $this->enrol_user($instance, $userid, $roleid, 0, 0, ENROL_USER_ACTIVE);
                         $current_roles[$userid][$roleid] = $roleid;
                         $current_status[$userid] = ENROL_USER_ACTIVE;
-                        if ($verbose) {
-                            mtrace("  enrolling: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname);
-                        }
+                        $trace->output("enrolling: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname, 1);
                     }
                 }
 
@@ -550,9 +540,7 @@ class enrol_database_plugin extends enrol_plugin {
                     if (empty($current_roles[$userid][$roleid])) {
                         role_assign($roleid, $userid, $context->id, 'enrol_database', $instance->id);
                         $current_roles[$userid][$roleid] = $roleid;
-                        if ($verbose) {
-                            mtrace("  assigning roles: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname);
-                        }
+                        $trace->output("assigning roles: $userid ==> $course->shortname as ".$allroles[$roleid]->shortname, 1);
                     }
                 }
 
@@ -561,18 +549,14 @@ class enrol_database_plugin extends enrol_plugin {
                     if (empty($userroles[$cr])) {
                         role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id);
                         unset($current_roles[$userid][$cr]);
-                        if ($verbose) {
-                            mtrace("  unsassigning roles: $userid ==> $course->shortname");
-                        }
+                        $trace->output("unsassigning roles: $userid ==> $course->shortname", 1);
                     }
                 }
 
                 // Reenable enrolment when previously disable enrolment refreshed.
                 if ($current_status[$userid] == ENROL_USER_SUSPENDED) {
                     $this->update_user_enrol($instance, $userid, ENROL_USER_ACTIVE);
-                    if ($verbose) {
-                        mtrace("  unsuspending: $userid ==> $course->shortname");
-                    }
+                    $trace->output("unsuspending: $userid ==> $course->shortname", 1);
                 }
             }
 
@@ -585,9 +569,7 @@ class enrol_database_plugin extends enrol_plugin {
                             continue;
                         }
                         $this->unenrol_user($instance, $userid);
-                        if ($verbose) {
-                            mtrace("  unenrolling: $userid ==> $course->shortname");
-                        }
+                        $trace->output("unenrolling: $userid ==> $course->shortname", 1);
                     }
                 }
 
@@ -602,15 +584,11 @@ class enrol_database_plugin extends enrol_plugin {
                     }
                     if ($status != ENROL_USER_SUSPENDED) {
                         $this->update_user_enrol($instance, $userid, ENROL_USER_SUSPENDED);
-                        if ($verbose) {
-                            mtrace("  suspending: $userid ==> $course->shortname");
-                        }
+                        $trace->output("suspending: $userid ==> $course->shortname", 1);
                     }
                     if ($unenrolaction == ENROL_EXT_REMOVED_SUSPENDNOROLES) {
                         role_unassign_all(array('contextid'=>$context->id, 'userid'=>$userid, 'component'=>'enrol_database', 'itemid'=>$instance->id));
-                        if ($verbose) {
-                            mtrace("  unsassigning all roles: $userid ==> $course->shortname");
-                        }
+                        $trace->output("unsassigning all roles: $userid ==> $course->shortname", 1);
                     }
                 }
             }
@@ -619,9 +597,8 @@ class enrol_database_plugin extends enrol_plugin {
         // Close db connection.
         $extdb->Close();
 
-        if ($verbose) {
-            mtrace('...user enrolment synchronisation finished.');
-        }
+        $trace->output('...user enrolment synchronisation finished.');
+        $trace->finished();
 
         return 0;
     }
@@ -632,30 +609,28 @@ class enrol_database_plugin extends enrol_plugin {
      * First it creates new courses if necessary, then
      * enrols and unenrols users.
      *
-     * @param bool $verbose
+     * @param progress_trace $trace
      * @return int 0 means success, 1 db connect failure, 4 db read failure
      */
-    public function sync_courses($verbose = false) {
+    public function sync_courses(progress_trace $trace) {
         global $CFG, $DB;
 
         // Make sure we sync either enrolments or courses.
         if (!$this->get_config('dbtype') or !$this->get_config('dbhost') or !$this->get_config('newcoursetable') or !$this->get_config('newcoursefullname') or !$this->get_config('newcourseshortname')) {
-            if ($verbose) {
-                mtrace('Course synchronisation skipped.');
-            }
+            $trace->output('Course synchronisation skipped.');
+            $trace->finished();
             return 0;
         }
 
-        if ($verbose) {
-            mtrace('Starting course synchronisation...');
-        }
+        $trace->output('Starting course synchronisation...');
 
         // We may need a lot of memory here.
         @set_time_limit(0);
         raise_memory_limit(MEMORY_HUGE);
 
         if (!$extdb = $this->db_init()) {
-            mtrace('Error while communicating with external enrolment database');
+            $trace->output('Error while communicating with external enrolment database');
+            $trace->finished();
             return 1;
         }
 
@@ -675,9 +650,7 @@ class enrol_database_plugin extends enrol_plugin {
         $defaultcategory    = $this->get_config('defaultcategory');
 
         if (!$DB->record_exists('course_categories', array('id'=>$defaultcategory))) {
-            if ($verbose) {
-                mtrace("  default course category does not exist!");
-            }
+            $trace->output("default course category does not exist!", 1);
             $categories = $DB->get_records('course_categories', array(), 'sortorder', 'id', 0, 1);
             $first = reset($categories);
             $defaultcategory = $first->id;
@@ -698,9 +671,7 @@ class enrol_database_plugin extends enrol_plugin {
                     $fields = array_change_key_case($fields, CASE_LOWER);
                     $fields = $this->db_decode($fields);
                     if (empty($fields[$shortname_l]) or empty($fields[$fullname_l])) {
-                        if ($verbose) {
-                            mtrace('  error: invalid external course record, shortname and fullname are mandatory: ' . json_encode($fields)); // Hopefully every geek can read JS, right?
-                        }
+                        $trace->output('error: invalid external course record, shortname and fullname are mandatory: ' . json_encode($fields), 1); // Hopefully every geek can read JS, right?
                         continue;
                     }
                     if ($DB->record_exists('course', array('shortname'=>$fields[$shortname_l]))) {
@@ -709,9 +680,7 @@ class enrol_database_plugin extends enrol_plugin {
                     }
                     // Allow empty idnumber but not duplicates.
                     if ($idnumber and $fields[$idnumber_l] !== '' and $fields[$idnumber_l] !== null and $DB->record_exists('course', array('idnumber'=>$fields[$idnumber_l]))) {
-                        if ($verbose) {
-                            mtrace('  error: duplicate idnumber, can not create course: '.$fields[$shortname_l].' ['.$fields[$idnumber_l].']');
-                        }
+                        $trace->output('error: duplicate idnumber, can not create course: '.$fields[$shortname_l].' ['.$fields[$idnumber_l].']', 1);
                         continue;
                     }
                     $course = new stdClass();
@@ -728,9 +697,7 @@ class enrol_database_plugin extends enrol_plugin {
                             unset($coursecategory);
                         } else {
                             // Bad luck, better not continue because unwanted ppl might get access to course in different category.
-                            if ($verbose) {
-                                mtrace('  error: invalid category '.$localcategoryfield.', can not create course: '.$fields[$shortname_l]);
-                            }
+                            $trace->output('error: invalid category '.$localcategoryfield.', can not create course: '.$fields[$shortname_l], 1);
                             continue;
                         }
                     } else {
@@ -741,8 +708,9 @@ class enrol_database_plugin extends enrol_plugin {
             }
             $rs->Close();
         } else {
-            mtrace('Error reading data from the external course table');
             $extdb->Close();
+            $trace->output('Error reading data from the external course table');
+            $trace->finished();
             return 4;
         }
         if ($createcourses) {
@@ -759,9 +727,7 @@ class enrol_database_plugin extends enrol_plugin {
                     unset($template->shortname);
                     unset($template->idnumber);
                 } else {
-                    if ($verbose) {
-                        mtrace("  can not find template for new course!");
-                    }
+                    $trace->output("can not find template for new course!", 1);
                 }
             }
             if (!$template) {
@@ -791,20 +757,14 @@ class enrol_database_plugin extends enrol_plugin {
                 // Detect duplicate data once again, above we can not find duplicates
                 // in external data using DB collation rules...
                 if ($DB->record_exists('course', array('shortname' => $newcourse->shortname))) {
-                    if ($verbose) {
-                        mtrace("  can not insert new course, duplicate shortname detected: ".$newcourse->shortname);
-                    }
+                    $trace->output("can not insert new course, duplicate shortname detected: ".$newcourse->shortname, 1);
                     continue;
                 } else if (!empty($newcourse->idnumber) and $DB->record_exists('course', array('idnumber' => $newcourse->idnumber))) {
-                    if ($verbose) {
-                        mtrace("  can not insert new course, duplicate idnumber detected: ".$newcourse->idnumber);
-                    }
+                    $trace->output("can not insert new course, duplicate idnumber detected: ".$newcourse->idnumber, 1);
                     continue;
                 }
                 $c = create_course($newcourse);
-                if ($verbose) {
-                    mtrace("  creating course: $c->id, $c->fullname, $c->shortname, $c->idnumber, $c->category");
-                }
+                $trace->output("creating course: $c->id, $c->fullname, $c->shortname, $c->idnumber, $c->category", 1);
             }
 
             unset($createcourses);
@@ -814,9 +774,8 @@ class enrol_database_plugin extends enrol_plugin {
         // Close db connection.
         $extdb->Close();
 
-        if ($verbose) {
-            mtrace('...course synchronisation finished.');
-        }
+        $trace->output('...course synchronisation finished.');
+        $trace->finished();
 
         return 0;
     }
@@ -920,7 +879,8 @@ class enrol_database_plugin extends enrol_plugin {
      * @param stdClass $course course record
      */
     public function restore_sync_course($course) {
-        $this->sync_enrolments(false, $course->id);
+        $trace = new null_progress_trace();
+        $this->sync_enrolments($trace, $course->id);
     }
 
     /**
index 8a32b6a..8009c46 100644 (file)
@@ -58,7 +58,11 @@ class enrol_database_testcase extends advanced_testcase {
                 set_config('dbsetupsql', "SET NAMES 'UTF-8'", 'enrol_database');
                 set_config('dbsybasequoting', '0', 'enrol_database');
                 if (!empty($CFG->dboptions['dbsocket'])) {
-                    set_config('dbtype', 'mysqli://'.rawurlencode($CFG->dbuser).':'.rawurlencode($CFG->dbpass).'@'.rawurlencode($CFG->dbhost).'/'.rawurlencode($CFG->dbname).'?socket='.rawurlencode($CFG->dboptions['dbsocket']), 'enrol_database');
+                    $dbsocket = $CFG->dboptions['dbsocket'];
+                    if ((strpos($dbsocket, '/') === false and strpos($dbsocket, '\\') === false)) {
+                        $dbsocket = ini_get('mysqli.default_socket');
+                    }
+                    set_config('dbtype', 'mysqli://'.rawurlencode($CFG->dbuser).':'.rawurlencode($CFG->dbpass).'@'.rawurlencode($CFG->dbhost).'/'.rawurlencode($CFG->dbname).'?socket='.rawurlencode($dbsocket), 'enrol_database');
                 }
                 break;
 
@@ -372,6 +376,8 @@ class enrol_database_testcase extends advanced_testcase {
 
         $plugin = enrol_get_plugin('database');
 
+        $trace = new null_progress_trace();
+
         // Test basic enrol sync for one user after login.
 
         $this->reset_enrol_database();
@@ -388,7 +394,7 @@ class enrol_database_testcase extends advanced_testcase {
         $this->assertEquals(0, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(0, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
 
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(2, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -398,7 +404,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $plugin->set_config('defaultrole', self::$roles['teacher']->id);
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>'userid3', 'courseid'=>'courseid3'));
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -412,7 +418,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $DB->delete_records('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'student'));
         $plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_KEEP);
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -423,7 +429,7 @@ class enrol_database_testcase extends advanced_testcase {
 
 
         $plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPEND);
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -433,7 +439,7 @@ class enrol_database_testcase extends advanced_testcase {
         $this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
 
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'student'));
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -445,7 +451,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $DB->delete_records('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'student'));
         $plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_SUSPENDNOROLES);
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -455,7 +461,7 @@ class enrol_database_testcase extends advanced_testcase {
         $this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
 
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'student'));
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -467,7 +473,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $DB->delete_records('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'student'));
         $plugin->set_config('unenrolaction', ENROL_EXT_REMOVED_UNENROL);
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -478,7 +484,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'student'));
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'teacher'));
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(5, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -489,7 +495,7 @@ class enrol_database_testcase extends advanced_testcase {
         $this->assertIsEnrolled(3, 3, ENROL_USER_ACTIVE, 'teacher');
 
         $DB->delete_records('enrol_database_test_enrols', array('userid'=>'userid1', 'courseid'=>'courseid1', 'roleid'=>'teacher'));
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(4, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(4, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -515,7 +521,7 @@ class enrol_database_testcase extends advanced_testcase {
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>self::$users[1]->id, 'courseid'=>self::$courses[2]->id, 'roleid'=>self::$roles['teacher']->id));
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>self::$users[2]->id, 'courseid'=>self::$courses[1]->id, 'roleid'=>self::$roles['student']->id));
 
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(2, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -533,7 +539,7 @@ class enrol_database_testcase extends advanced_testcase {
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>self::$users[1]->email, 'courseid'=>self::$courses[2]->shortname, 'roleid'=>self::$roles['teacher']->id));
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>self::$users[2]->email, 'courseid'=>self::$courses[1]->shortname, 'roleid'=>self::$roles['student']->id));
 
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(2, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -551,7 +557,7 @@ class enrol_database_testcase extends advanced_testcase {
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>self::$users[1]->username, 'courseid'=>self::$courses[2]->id, 'roleid'=>self::$roles['teacher']->id));
         $DB->insert_record('enrol_database_test_enrols', array('userid'=>self::$users[2]->username, 'courseid'=>self::$courses[1]->id, 'roleid'=>self::$roles['student']->id));
 
-        $plugin->sync_enrolments(false);
+        $plugin->sync_enrolments($trace);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(2, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -572,19 +578,19 @@ class enrol_database_testcase extends advanced_testcase {
         $this->assertEquals(0, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(0, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
 
-        $plugin->sync_enrolments(false, self::$courses[3]->id);
+        $plugin->sync_enrolments($trace, self::$courses[3]->id);
         $this->assertEquals(0, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(1, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(0, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
 
-        $plugin->sync_enrolments(false, self::$courses[1]->id);
+        $plugin->sync_enrolments($trace, self::$courses[1]->id);
         $this->assertEquals(2, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(2, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(2, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
         $this->assertIsEnrolled(1, 1, ENROL_USER_ACTIVE, 'student');
         $this->assertIsEnrolled(2, 1, ENROL_USER_ACTIVE, 'student');
 
-        $plugin->sync_enrolments(false, self::$courses[2]->id);
+        $plugin->sync_enrolments($trace, self::$courses[2]->id);
         $this->assertEquals(3, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(3, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -597,13 +603,13 @@ class enrol_database_testcase extends advanced_testcase {
 
         $DB->delete_records('enrol_database_test_enrols', array());
 
-        $plugin->sync_enrolments(false, self::$courses[1]->id);
+        $plugin->sync_enrolments($trace, self::$courses[1]->id);
         $this->assertEquals(1, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(1, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
         $this->assertIsEnrolled(1, 2, ENROL_USER_ACTIVE, 'teacher');
 
-        $plugin->sync_enrolments(false, self::$courses[2]->id);
+        $plugin->sync_enrolments($trace, self::$courses[2]->id);
         $this->assertEquals(0, $DB->count_records('user_enrolments', array()));
         $this->assertEquals(3, $DB->count_records('enrol', array('enrol'=>'database')));
         $this->assertEquals(0, $DB->count_records('role_assignments', array('component'=>'enrol_database')));
@@ -621,6 +627,8 @@ class enrol_database_testcase extends advanced_testcase {
 
         $plugin = enrol_get_plugin('database');
 
+        $trace = new null_progress_trace();
+
         $plugin->set_config('localcategoryfield', 'id');
         $coursecat = $this->getDataGenerator()->create_category(array('name'=>'Test category 1', 'idnumber'=>'tcid1'));
         $defcat = $DB->get_record('course_categories', array('id'=>$plugin->get_config('defaultcategory')));
@@ -642,7 +650,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $this->assertEquals(1+count(self::$courses), $DB->count_records('course'));
 
-        $plugin->sync_courses(false);
+        $plugin->sync_courses($trace);
 
         $this->assertEquals(4+1+count(self::$courses), $DB->count_records('course'));
 
@@ -662,7 +670,7 @@ class enrol_database_testcase extends advanced_testcase {
         $plugin->set_config('localcategoryfield', 'idnumber');
         $course7 = array('fullname'=>'New course 7', 'shortname'=>'nc7', 'idnumber'=>'ncid7', 'category'=>'tcid1');
         $DB->insert_record('enrol_database_test_courses', $course7);
-        $plugin->sync_courses(false);
+        $plugin->sync_courses($trace);
 
         $this->assertEquals(1+4+1+count(self::$courses), $DB->count_records('course'));
         $this->assertTrue($DB->record_exists('course', $course1));
@@ -678,7 +686,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $course8 = array('fullname'=>'New course 8', 'shortname'=>'nc8', 'idnumber'=>'ncid8', 'category'=>null);
         $DB->insert_record('enrol_database_test_courses', $course8);
-        $plugin->sync_courses(false);
+        $plugin->sync_courses($trace);
 
         $this->assertEquals(2+1+4+1+count(self::$courses), $DB->count_records('course'));
         $course8['category'] = $defcat->id;
@@ -691,7 +699,7 @@ class enrol_database_testcase extends advanced_testcase {
 
         $course9 = array('fullname'=>'New course 9', 'shortname'=>'nc9', 'idnumber'=>'ncid9', 'category'=>'xxxxxxx');
         $DB->insert_record('enrol_database_test_courses', $course9);
-        $plugin->sync_courses(false);
+        $plugin->sync_courses($trace);
         $this->assertEquals(2+1+4+1+count(self::$courses), $DB->count_records('course'));
         $this->assertFalse($DB->record_exists('course', array('idnumber'=>'ncid9')));
 
@@ -699,7 +707,7 @@ class enrol_database_testcase extends advanced_testcase {
         // Test when categories not specified.
 
         $plugin->set_config('newcoursecategory', '');
-        $plugin->sync_courses(false);
+        $plugin->sync_courses($trace);
         $this->assertEquals(1+2+1+4+1+count(self::$courses), $DB->count_records('course'));
         $this->assertTrue($DB->record_exists('course', array('idnumber'=>'ncid9')));
 
index 31224de..f602ffb 100644 (file)
@@ -653,7 +653,7 @@ class enrol_ldap_plugin extends enrol_plugin {
      * @param object role is a record from the mdl_role table.
      * @return array
      */
-    protected function find_ext_enrolments ($ldapconnection, $memberuid, $role) {
+    protected function find_ext_enrolments (&$ldapconnection, $memberuid, $role) {
         global $CFG;
         require_once($CFG->libdir.'/ldaplib.php');
 
@@ -718,13 +718,13 @@ class enrol_ldap_plugin extends enrol_plugin {
         // Get all contexts and look for first matching user
         $ldap_contexts = explode(';', $ldap_contexts);
         $ldap_pagedresults = ldap_paged_results_supported($this->get_config('ldap_version'));
-        $ldap_cookie = '';
         foreach ($ldap_contexts as $context) {
             $context = trim($context);
             if (empty($context)) {
                 continue;
             }
 
+            $ldap_cookie = '';
             $flat_records = array();
             do {
                 if ($ldap_pagedresults) {
index 2ee9a7b..c4a222e 100644 (file)
@@ -586,16 +586,29 @@ class course_enrolment_manager {
      */
     public function unassign_role_from_user($userid, $roleid) {
         global $DB;
-        require_capability('moodle/role:assign', $this->context);
-        $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
-        try {
-            role_unassign($roleid, $user->id, $this->context->id, '', NULL);
-        } catch (Exception $e) {
+        // Admins may unassign any role, others only those they could assign.
+        if (!is_siteadmin() and !array_key_exists($roleid, $this->get_assignable_roles())) {
             if (defined('AJAX_SCRIPT')) {
-                throw $e;
+                throw new moodle_exception('invalidrole');
             }
             return false;
         }
+        $user = $DB->get_record('user', array('id'=>$userid), '*', MUST_EXIST);
+        $ras = $DB->get_records('role_assignments', array('contextid'=>$this->context->id, 'userid'=>$user->id, 'roleid'=>$roleid));
+        foreach ($ras as $ra) {
+            if ($ra->component) {
+                if (strpos($ra->component, 'enrol_') !== 0) {
+                    continue;
+                }
+                if (!$plugin = enrol_get_plugin(substr($ra->component, 6))) {
+                    continue;
+                }
+                if ($plugin->roles_protected()) {
+                    continue;
+                }
+            }
+            role_unassign($ra->roleid, $ra->userid, $ra->contextid, $ra->component, $ra->itemid);
+        }
         return true;
     }
 
@@ -706,6 +719,7 @@ class course_enrolment_manager {
     public function get_user_roles($userid) {
         $roles = array();
         $ras = get_user_roles($this->context, $userid, true, 'c.contextlevel DESC, r.sortorder ASC');
+        $plugins = $this->get_enrolment_plugins(false);
         foreach ($ras as $ra) {
             if ($ra->contextid != $this->context->id) {
                 if (!array_key_exists($ra->roleid, $roles)) {
@@ -717,7 +731,18 @@ class course_enrolment_manager {
             if (array_key_exists($ra->roleid, $roles) && $roles[$ra->roleid] === false) {
                 continue;
             }
-            $roles[$ra->roleid] = ($ra->itemid == 0 and $ra->component === '');
+            $changeable = true;
+            if ($ra->component) {
+                $changeable = false;
+                if (strpos($ra->component, 'enrol_') === 0) {
+                    $plugin = substr($ra->component, 6);
+                    if (isset($plugins[$plugin])) {
+                        $changeable = !$plugins[$plugin]->roles_protected();
+                    }
+                }
+            }
+
+            $roles[$ra->roleid] = $changeable;
         }
         return $roles;
     }
@@ -807,6 +832,7 @@ class course_enrolment_manager {
 
         $userroles = $this->get_other_users($sort, $direction, $page, $perpage);
         $roles = $this->get_all_roles();
+        $plugins = $this->get_enrolment_plugins(false);
 
         $context    = $this->get_context();
         $now = time();
@@ -821,8 +847,17 @@ class course_enrolment_manager {
             }
             $a = new stdClass;
             $a->role = $roles[$userrole->roleid]->localname;
-            $changeable = ($userrole->component == '');
             if ($contextid == $this->context->id) {
+                $changeable = true;
+                if ($userrole->component) {
+                    $changeable = false;
+                    if (strpos($userrole->component, 'enrol_') === 0) {
+                        $plugin = substr($userrole->component, 6);
+                        if (isset($plugins[$plugin])) {
+                            $changeable = !$plugin[$plugin]->roles_protected();
+                        }
+                    }
+                }
                 $roletext = get_string('rolefromthiscourse', 'enrol', $a);
             } else {
                 $changeable = false;
@@ -888,7 +923,11 @@ class course_enrolment_manager {
             // Roles
             $details['roles'] = array();
             foreach ($this->get_user_roles($user->id) as $rid=>$rassignable) {
-                $details['roles'][$rid] = array('text'=>$allroles[$rid]->localname, 'unchangeable'=>(!$rassignable || !isset($assignable[$rid])));
+                $unchangeable = !$rassignable;
+                if (!is_siteadmin() and !isset($assignable[$rid])) {
+                    $unchangeable = true;
+                }
+                $details['roles'][$rid] = array('text'=>$allroles[$rid]->localname, 'unchangeable'=>$unchangeable);
             }
 
             // Users
@@ -926,7 +965,7 @@ class course_enrolment_manager {
                 $details['enrolments'][$ue->id] = array(
                     'text' => $ue->enrolmentinstancename,
                     'period' => $period,
-                    'dimmed' =>  ($periodoutside || $ue->status != ENROL_USER_ACTIVE),
+                    'dimmed' =>  ($periodoutside or $ue->status != ENROL_USER_ACTIVE or $ue->enrolmentinstance->status != ENROL_INSTANCE_ENABLED),
                     'actions' => $ue->enrolmentplugin->get_user_enrolment_actions($manager, $ue)
                 );
             }
index 3fd7f52..4decf20 100644 (file)
@@ -47,6 +47,7 @@ $PAGE->set_pagelayout('admin');
 $manager = new course_enrolment_manager($PAGE, $course, $filter);
 $table = new course_enrolment_other_users_table($manager, $PAGE);
 $PAGE->set_url('/enrol/otherusers.php', $manager->get_url_params()+$table->get_url_params());
+navigation_node::override_active_url(new moodle_url('/enrol/otherusers.php', array('id' => $id)));
 
 $userdetails = array (
     'picture' => false,
index 05e43f5..de8505c 100644 (file)
@@ -189,7 +189,7 @@ class core_enrol_renderer extends plugin_renderer_base {
         // get list of roles
         $rolesoutput = '';
         foreach ($roles as $roleid=>$role) {
-            if ($canassign && !$role['unchangeable']) {
+            if ($canassign and (is_siteadmin() or isset($assignableroles[$roleid])) and !$role['unchangeable']) {
                 $strunassign = get_string('unassignarole', 'role', $role['text']);
                 $icon = html_writer::empty_tag('img', array('alt'=>$strunassign, 'src'=>$iconenrolremove));
                 $url = new moodle_url($pageurl, array('action'=>'unassign', 'role'=>$roleid, 'user'=>$userid));
index 2a5e351..6927847 100644 (file)
@@ -188,7 +188,7 @@ class core_files_renderer extends plugin_renderer_base {
         $strdroptoupload = get_string('droptoupload', 'moodle');
         $icon_progress = $OUTPUT->pix_icon('i/loading_small', $strloading).'';
         $restrictions = $this->fm_print_restrictions($fm);
-        $strdndenabled = get_string('dndenabled_insentence', 'moodle').$OUTPUT->help_icon('dndenabled');
+        $strdndnotsupported = get_string('dndnotsupported_insentence', 'moodle').$OUTPUT->help_icon('dndnotsupported');
         $strdndenabledinbox = get_string('dndenabled_inbox', 'moodle');
         $loading = get_string('loading', 'repository');
 
@@ -196,7 +196,7 @@ class core_files_renderer extends plugin_renderer_base {
 <div id="filemanager-'.$client_id.'" class="filemanager fm-loading">
     <div class="fp-restrictions">
         '.$restrictions.'
-        <span class="dndupload-message"> - '.$strdndenabled.' </span>
+        <span class="dnduploadnotsupported-message"> - '.$strdndnotsupported.' </span>
     </div>
     <div class="fp-navbar">
         <div class="filemanager-toolbar">
index 8caef86..ea065a5 100644 (file)
@@ -26,6 +26,6 @@ function xmldb_filter_activitynames_install() {
     global $CFG;
     require_once("$CFG->libdir/filterlib.php");
 
-    filter_set_global_state('filter/activitynames', TEXTFILTER_ON);
+    filter_set_global_state('activitynames', TEXTFILTER_ON);
 }
 
index d451fbf..3939977 100644 (file)
@@ -7,7 +7,7 @@
 
     require_once("../../config.php");
 
-    if (!filter_is_enabled('filter/algebra')) {
+    if (!filter_is_enabled('algebra')) {
         print_error('filternotenabled');
     }
 
index a4f5843..c4f29d9 100644 (file)
@@ -9,7 +9,7 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
 
     require_once('../../config.php');
 
-    if (!filter_is_enabled('filter/algebra')) {
+    if (!filter_is_enabled('algebra')) {
         print_error('filternotenabled');
     }
 
index 04893f1..19a1e29 100644 (file)
@@ -27,7 +27,7 @@ require_once(dirname(__FILE__) . '/../config.php');
 require_once($CFG->libdir . '/adminlib.php');
 
 $contextid = required_param('contextid',PARAM_INT);
-$forfilter = optional_param('filter', '', PARAM_SAFEPATH);
+$forfilter = optional_param('filter', '', PARAM_SAFEDIR);
 
 list($context, $course, $cm) = get_context_info_array($contextid);
 
@@ -82,8 +82,8 @@ if ($forfilter) {
         print_error('filterdoesnothavelocalconfig', 'error', $forfilter);
     }
     require_once($CFG->dirroot . '/filter/local_settings_form.php');
-    require_once($CFG->dirroot . '/' . $forfilter . '/filterlocalsettings.php');
-    $formname = basename($forfilter) . '_filter_local_settings_form';
+    require_once($CFG->dirroot . '/filter/' . $forfilter . '/filterlocalsettings.php');
+    $formname = $forfilter . '_filter_local_settings_form';
     $settingsform = new $formname($CFG->wwwroot . '/filter/manage.php', $forfilter, $context);
     if ($settingsform->is_cancelled()) {
         redirect($baseurl);
@@ -96,7 +96,7 @@ if ($forfilter) {
 /// Process any form submission.
 if ($forfilter == '' && optional_param('savechanges', false, PARAM_BOOL) && confirm_sesskey()) {
     foreach ($availablefilters as $filter => $filterinfo) {
-        $newstate = optional_param(str_replace('/', '_', $filter), false, PARAM_INT);
+        $newstate = optional_param($filter, false, PARAM_INT);
         if ($newstate !== false && $newstate != $filterinfo->localstate) {
             filter_set_local_state($filter, $context->id, $newstate);
         }
@@ -181,9 +181,8 @@ if (empty($availablefilters)) {
         } else {
             $activechoices[TEXTFILTER_INHERIT] = $strdefaultoff;
         }
-        $filtername = str_replace('/', '_', $filter);
-        $select = html_writer::label($filterinfo->localstate, 'menu'. $filtername, false, array('class' => 'accesshide'));
-        $select .= html_writer::select($activechoices, $filtername, $filterinfo->localstate, false);
+        $select = html_writer::label($filterinfo->localstate, 'menu'. $filter, false, array('class' => 'accesshide'));
+        $select .= html_writer::select($activechoices, $filter, $filterinfo->localstate, false);
         $row[] = $select;
 
         // Settings link, if required
index 64e3fb9..c128f85 100644 (file)
@@ -27,6 +27,6 @@ function xmldb_filter_mediaplugin_install() {
     global $CFG;
     require_once("$CFG->libdir/filterlib.php");
 
-    filter_set_global_state('filter/mediaplugin', TEXTFILTER_ON);
+    filter_set_global_state('mediaplugin', TEXTFILTER_ON);
 }
 
index ddc6ee6..76eb64a 100644 (file)
@@ -29,7 +29,7 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
 
 require('../../config.php');
 
-if (!filter_is_enabled('filter/tex') and !filter_is_enabled('filter/algebra')) {
+if (!filter_is_enabled('tex') and !filter_is_enabled('algebra')) {
     print_error('filternotenabled');
 }
 
index deb43d5..4ae2cd0 100644 (file)
@@ -9,7 +9,7 @@ define('NO_MOODLE_COOKIES', true); // Because it interferes with caching
 
     require_once('../../config.php');
 
-    if (!filter_is_enabled('filter/tex')) {
+    if (!filter_is_enabled('tex')) {
         print_error('filternotenabled');
     }
 
index 3200449..0817d7e 100644 (file)
@@ -28,7 +28,7 @@
 
     require_once("../../config.php");
 
-    if (!filter_is_enabled('filter/tex')) {
+    if (!filter_is_enabled('tex')) {
         print_error('filternotenabled');
     }
 
index cf94ace..99cffdd 100644 (file)
@@ -1,6 +1,13 @@
 This file describes API changes in core filter API and plugins,
 information provided here is intended especially for developers.
 
+=== 2.5 ===
+
+* legacy_filter emulation was removed
+* support for 'mod/*' filters was removed
+* use short filter name instead of old path, ex.: 'filter/tex' ---> 'tex'
+  in all filter API functions and methods
+
 === 2.3 ===
 
 * new setup() method added to moodle_text_filter, invoked before
index 820148f..62c7a4f 100644 (file)
@@ -273,12 +273,6 @@ class grade_edit_tree {
                 $root = true;
             }
 
-            $row_count_offset = 0;
-
-            if (empty($category_total_item) && !$this->moving) {
-                $row_count_offset = -1;
-            }
-
             $levelclass = "level$level";
 
             $courseclass = '';
@@ -297,7 +291,7 @@ class grade_edit_tree {
             $headercell->scope = 'row';
             $headercell->attributes['title'] = $object->stripped_name;
             $headercell->attributes['class'] = 'cell rowspan ' . $levelclass;
-            $headercell->rowspan = $row_count+1+$row_count_offset;
+            $headercell->rowspan = $row_count + 1;
             $row->cells[] = $headercell;
 
             foreach ($this->columns as $column) {
index f7599d7..5963c6e 100644 (file)
@@ -579,7 +579,7 @@ class core_group_external extends external_api {
                             'courseid' => new external_value(PARAM_INT, 'id of course'),
                             'name' => new external_value(PARAM_TEXT, 'multilang compatible name, course unique'),
                             'description' => new external_value(PARAM_RAW, 'grouping description text'),
-                            'descriptionformat' => new external_format_value('descripiton', VALUE_DEFAULT)
+                            'descriptionformat' => new external_format_value('description', VALUE_DEFAULT)
                         )
                     ), 'List of grouping object. A grouping has a courseid, a name and a description.'
                 )
index 42835f0..eac8c62 100644 (file)
@@ -625,6 +625,28 @@ $string['localstringcustomization'] = 'Local string customization';
 $string['location'] = 'Location';
 $string['locationsettings'] = 'Location settings';
 $string['locked'] = 'locked';
+$string['lockoutduration'] = 'Account lockout duration';
+$string['lockoutduration_desc'] = 'Locked out account is automatically unlocked after this duration.';
+$string['lockoutemailbody'] = 'Your account with username {$a->username} on server \'{$a->sitename}\'
+was locked out after multiple invalid login attempts.
+
+To unlock the account immediately go to the following address
+
+{$a->link}
+
+In most mail programs, this should appear as a blue link
+which you can just click on.  If that doesn\'t work,
+then copy and paste the address into the address
+line at the top of your web browser window.
+
+If you need help, please contact the site administrator,
+{$a->admin}';
+$string['lockoutemailsubject'] = 'Your account on {$a} was locked out';
+$string['lockouterrorunlock'] = 'Invalid account unlock information supplied.';
+$string['lockoutthreshold'] = 'Account lockout threshold';
+$string['lockoutthreshold_desc'] = 'Select number of failed login attempts that result in account lockout. This feature may be abused in denial of service attacks.';
+$string['lockoutwindow'] = 'Account lockout observation window';
+$string['lockoutwindow_desc'] = 'Observation time for lockout threshold, if there are no failed attempts the threshold counter is reset after this time.';
 $string['log'] = 'Logs';
 $string['logguests'] = 'Log guest access';
 $string['logguests_help'] = 'This setting enables logging of actions by guest account and not logged in users. High profile sites may want to disable this logging for performance reasons. It is recommended to keep this setting enabled on production sites.';
@@ -989,6 +1011,7 @@ $string['unbookmarkthispage'] = 'Unbookmark this page';
 $string['unicoderecommended'] = 'Storing all your data in Unicode (UTF-8) is recommended. New installations should be performed into databases that have their default character set as Unicode.  If you are upgrading, you should perform the UTF-8 migration process (see the Admin page).';
 $string['unicoderequired'] = 'It is required that you store all your data in Unicode format (UTF-8). New installations must be performed into databases that have their default character set as Unicode.  If you are upgrading, you should perform the UTF-8 migration process (see the Admin page).';
 $string['uninstallplugin'] = 'Uninstall';
+$string['unlockaccount'] = 'Unlock account';
 $string['unsettheme'] = 'Unset theme';
 $string['unsupported'] = 'Unsupported';
 $string['unsuspenduser'] = 'Activate user account';
index a685f0e..d662beb 100644 (file)
@@ -39,6 +39,8 @@ $string['defaultweight'] = 'Default weight';
 $string['defaultweight_help'] = 'The default weight allows you to choose roughly where you want the block to appear in the chosen region, either at the top or the bottom. The final location is calculated from all the blocks in that region (for example, only one block can actually be at the top). This value can be overridden on specific pages if required.';
 $string['deletecheck'] = 'Delete {$a} block?';
 $string['deleteblockcheck'] = 'Are you sure that you want to delete this block titled {$a}?';
+$string['hidedockpanel'] = 'Hide the dock panel';
+$string['hidepanel'] = 'Hide panel';
 $string['moveblockhere'] = 'Move block here';
 $string['movingthisblockcancel'] = 'Moving this block ({$a})';
 $string['onthispage'] = 'On this page';
index b211788..0bf030b 100644 (file)
@@ -331,6 +331,7 @@ $string['invalidurl'] = 'Invalid URL';
 $string['invaliduser'] = 'Invalid user';
 $string['invaliduserid'] = 'Invalid user id';
 $string['invaliduserfield'] = 'Invalid user field: {$a}';
+$string['invalidusername'] = 'The given username contains invalid characters';
 $string['invalidxmlfile'] = '"{$a}" is not a valid XML file';
 $string['iplookupfailed'] = 'Cannot find geo information about this IP address {$a}';
 $string['iplookupprivate'] = 'Cannot display lookup of private IP address';
index 16430f7..12842c9 100644 (file)
@@ -468,10 +468,10 @@ $string['displayingfirst'] = 'Only the first {$a->count} {$a->things} are displa
 $string['displayingrecords'] = 'Displaying {$a} records';
 $string['displayingusers'] = 'Displaying users {$a->start} to {$a->end}';
 $string['displayonpage'] = 'Display on page';
-$string['dndenabled'] = 'Drag and drop available';
-$string['dndenabled_help'] = 'You can drag one or more files from your desktop and drop them onto the box below to upload them.<br />Note: this may not work with other web browsers';
-$string['dndenabled_insentence'] = 'drag and drop available';
 $string['dndenabled_inbox'] = 'You can drag and drop files here to add them.';
+$string['dndnotsupported'] = 'Drag and drop upload not supported';
+$string['dndnotsupported_help'] = 'Your browser does not support drag and drop upload.<br />This feature is available in all recent versions of Chrome, Firefox and Safari, as well as Internet Explorer v10 and above.';
+$string['dndnotsupported_insentence'] = 'drag and drop not supported';
 $string['dnduploadwithoutcontent'] = 'This upload does not have any content';
 $string['dndworkingfiletextlink'] = 'Drag and drop files, text or links onto course sections to upload them';
 $string['dndworkingfilelink'] = 'Drag and drop files or links onto course sections to upload them';
@@ -649,7 +649,6 @@ $string['error'] = 'Error';
 $string['errorcreatingactivity'] = 'Unable to create an instance of activity \'{$a}\'';
 $string['errorfiletoobig'] = 'The file was bigger than the limit of {$a} bytes';
 $string['errornouploadrepo'] = 'There is no upload repository enabled for this site';
-$string['errortoomanylogins'] = 'Sorry, you have exceeded the allowed number of login attempts. Restart your browser.';
 $string['errorwhenconfirming'] = 'You are not confirmed yet because an error occurred.  If you clicked on a link in an email to get here, make sure that the line in your email wasn\'t broken or wrapped. You may have to use cut and paste to reconstruct the link properly.';
 $string['everybody'] = 'Everybody';
 $string['executeat'] = 'Execute at';
index dfa1aeb..56d1a07 100644 (file)
@@ -306,6 +306,8 @@ $string['cannotloadquestion'] = 'Could not load question';
 $string['cannotpreview'] = 'You can\'t preview these questions!';
 $string['category'] = 'Category';
 $string['changeoptions'] = 'Change options';
+$string['attemptoptions'] = 'Attempt options';
+$string['displayoptions'] = 'Display options';
 $string['check'] = 'Check';
 $string['clearwrongparts'] = 'Clear incorrect responses';
 $string['closepreview'] = 'Close preview';
@@ -384,6 +386,7 @@ $string['requiresgrading'] = 'Requires grading';
 $string['responsehistory'] = 'Response history';
 $string['restart'] = 'Start again';
 $string['restartwiththeseoptions'] = 'Start again with these options';
+$string['updatedisplayoptions'] = 'Update display options';
 $string['rightanswer'] = 'Right answer';
 $string['rightanswer_help'] = 'an automatically generated summary of the correct response. This can be limited, so you may wish to consider explaining the correct solution in the general feedback for the question, and turning this option off.';
 $string['saved'] = 'Saved: {$a}';
index b430e56..fadce50 100644 (file)
@@ -100,6 +100,7 @@ $string['error'] = 'An unknown error occurred!';
 $string['errornotyourfile'] = 'You cannot pick file which is not added by your';
 $string['erroruniquename'] = 'Repository instance name should be unique';
 $string['errorpostmaxsize'] = 'The uploaded file may exceed max_post_size directive in php.ini.';
+$string['errorwhilecommunicatingwith'] = 'Error while communicating with the repository \'{$a}\'.';
 $string['errorwhiledownload'] = 'An error occured while downloading the file: {$a}';
 $string['existingrepository'] = 'This repository already exists';
 $string['federatedsearch'] = 'Federated search';
index ba9a666..cb2e4f3 100644 (file)
@@ -123,6 +123,9 @@ define('INSECURE_DATAROOT_ERROR', 2);
 function uninstall_plugin($type, $name) {
     global $CFG, $DB, $OUTPUT;
 
+    // This may take a long time.
+    @set_time_limit(0);
+
     // recursively uninstall all module/editor subplugins first
     if ($type === 'mod' || $type === 'editor') {
         $base = get_component_directory($type . '_' . $name);
@@ -929,6 +932,11 @@ class admin_category implements parentable_part_of_admin_tree {
                 debugging('error - parts of tree can be inserted only into parentable parts');
                 return false;
             }
+            if (debugging('', DEBUG_DEVELOPER) && !is_null($this->locate($something->name))) {
+                // The name of the node is already used, simply warn the developer that this should not happen.
+                // It is intentional to check for the debug level before performing the check.
+                debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
+            }
             $parent->children[] = $something;
             if (is_array($this->category_cache) and ($something instanceof admin_category)) {
                 if (isset($this->category_cache[$something->name])) {
@@ -6092,8 +6100,7 @@ class admin_page_managefilters extends admin_externalpage {
                 $found = true;
                 break;
             }
-            list($type, $filter) = explode('/', $path);
-            if (strpos($filter, $query) !== false) {
+            if (strpos($path, $query) !== false) {
                 $found = true;
                 break;
             }
index 7f691b7..f8aa820 100644 (file)
@@ -61,6 +61,22 @@ define('AUTH_REMOVEUSER_KEEP', 0);
 define('AUTH_REMOVEUSER_SUSPEND', 1);
 define('AUTH_REMOVEUSER_FULLDELETE', 2);
 
+/** Login attempt successful. */
+define('AUTH_LOGIN_OK', 0);
+
+/** Can not login because user does not exist. */
+define('AUTH_LOGIN_NOUSER', 1);
+
+/** Can not login because user is suspended. */
+define('AUTH_LOGIN_SUSPENDED', 2);
+
+/** Can not login, most probably password did not match. */
+define('AUTH_LOGIN_FAILED', 3);
+
+/** Can not login because user is locked out. */
+define('AUTH_LOGIN_LOCKOUT', 4);
+
+
 /**
  * Abstract authentication plugin.
  *
@@ -507,3 +523,178 @@ class auth_plugin_base {
     }
 
 }
+
+/**
+ * Verify if user is locked out.
+ *
+ * @param stdClass $user
+ * @return bool true if user locked out
+ */
+function login_is_lockedout($user) {
+    global $CFG;
+
+    if ($user->mnethostid != $CFG->mnet_localhost_id) {
+        return false;
+    }
+    if (isguestuser($user)) {
+        return false;
+    }
+
+    if (empty($CFG->lockoutthreshold)) {
+        // Lockout not enabled.
+        return false;
+    }
+
+    if (get_user_preferences('login_lockout_ignored', 0, $user)) {
+        // This preference may be used for accounts that must not be locked out.
+        return false;
+    }
+
+    $locked = get_user_preferences('login_lockout', 0, $user);
+    if (!$locked) {
+        return false;
+    }
+
+    if (empty($CFG->lockoutduration)) {
+        // Locked out forever.
+        return true;
+    }
+
+    if (time() - $locked < $CFG->lockoutduration) {
+        return true;
+    }
+
+    login_unlock_account($user);
+
+    return false;
+}
+
+/**
+ * To be called after valid user login.
+ * @param stdClass $user
+ */
+function login_attempt_valid($user) {
+    global $CFG;
+
+    if ($user->mnethostid != $CFG->mnet_localhost_id) {
+        return;
+    }
+    if (isguestuser($user)) {
+        return;
+    }
+
+    // Always unlock here, there might be some race conditions or leftovers when switching threshold.
+    login_unlock_account($user);
+}
+
+/**
+ * To be called after failed user login.
+ * @param stdClass $user
+ */
+function login_attempt_failed($user) {
+    global $CFG;
+
+    if ($user->mnethostid != $CFG->mnet_localhost_id) {
+        return;
+    }
+    if (isguestuser($user)) {
+        return;
+    }
+
+    if (empty($CFG->lockoutthreshold)) {
+        // No threshold means no lockout.
+        // Always unlock here, there might be some race conditions or leftovers when switching threshold.
+        login_unlock_account($user);
+        return;
+    }
+
+    $count = get_user_preferences('login_failed_count', 0, $user);
+    $last = get_user_preferences('login_failed_last', 0, $user);
+
+    if (!empty($CFG->lockoutwindow) and time() - $last > $CFG->lockoutwindow) {
+        $count = 0;
+    }
+
+    $count = $count+1;
+
+    set_user_preference('login_failed_count', $count, $user);
+    set_user_preference('login_failed_last', time(), $user);
+
+    if ($count >= $CFG->lockoutthreshold) {
+        login_lock_account($user);
+    }
+}
+
+/**
+ * Lockout user and send notification email.
+ *
+ * @param stdClass $user
+ */
+function login_lock_account($user) {
+    global $CFG, $SESSION;
+
+    if ($user->mnethostid != $CFG->mnet_localhost_id) {
+        return;
+    }
+    if (isguestuser($user)) {
+        return;
+    }
+
+    if (get_user_preferences('login_lockout_ignored', 0, $user)) {
+        // This user can not be locked out.
+        return;
+    }
+
+    $alreadylockedout = get_user_preferences('login_lockout', 0, $user);
+
+    set_user_preference('login_lockout', time(), $user);
+
+    if ($alreadylockedout == 0) {
+        $secret = random_string(15);
+        set_user_preference('login_lockout_secret', $secret, $user);
+
+        // Some nasty hackery to get strings and dates localised for target user.
+        $sessionlang = isset($SESSION->lang) ? $SESSION->lang : null;
+        if (get_string_manager()->translation_exists($user->lang, false)) {
+            $SESSION->lang = $user->lang;
+            moodle_setlocale();
+        }
+
+        $site = get_site();
+        $supportuser = generate_email_supportuser();
+
+        $data = new stdClass();
+        $data->firstname = $user->firstname;
+        $data->lastname  = $user->lastname;
+        $data->username  = $user->username;
+        $data->sitename  = format_string($site->fullname);
+        $data->link      = $CFG->wwwroot.'/login/unlock_account.php?u='.$user->id.'&s='.$secret;
+        $data->admin     = generate_email_signoff();
+
+        $message = get_string('lockoutemailbody', 'admin', $data);
+        $subject = get_string('lockoutemailsubject', 'admin', format_string($site->fullname));
+
+        if ($message) {
+            // Directly email rather than using the messaging system to ensure its not routed to a popup or jabber.
+            email_to_user($user, $supportuser, $subject, $message);
+        }
+
+        if ($SESSION->lang !== $sessionlang) {
+            $SESSION->lang = $sessionlang;
+            moodle_setlocale();
+        }
+    }
+}
+
+/**
+ * Unlock user account and reset timers.
+ *
+ * @param stdClass $user
+ */
+function login_unlock_account($user) {
+    unset_user_preference('login_lockout', $user);
+    unset_user_preference('login_failed_count', $user);
+    unset_user_preference('login_failed_last', $user);
+
+    // Note: do not clear the lockout secret because user might click on the link repeatedly.
+}
index f60af6c..bea2404 100644 (file)
     <TABLE NAME="filter_active" COMMENT="Stores information about which filters are active in which contexts. Also the filter sort order. See get_active_filters in lib/filterlib.php for how this data is used." PREVIOUS="course_format_options" NEXT="filter_config">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="filter"/>
-        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'filter/tex' or 'mod/glossary'." PREVIOUS="id" NEXT="contextid"/>
+        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'tex'." PREVIOUS="id" NEXT="contextid"/>
         <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="References context.id." PREVIOUS="filter" NEXT="active"/>
         <FIELD NAME="active" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="Whether this filter is active in this context. +1 = On, -1 = Off, no row with this contextid = inherit. As a special case, when contextid points to the system context, -9999 means this filter is completely disabled." PREVIOUS="contextid" NEXT="sortorder"/>
         <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false" COMMENT="Only relevant if contextid points to the system context. In other cases this field should contain 0. The order in which the filters should be applied." PREVIOUS="active"/>
     <TABLE NAME="filter_config" COMMENT="Stores per-context configuration settings for filters which have them." PREVIOUS="filter_active" NEXT="event">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="filter"/>
-        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'filter/tex' or 'mod/glossary'." PREVIOUS="id" NEXT="contextid"/>
+        <FIELD NAME="filter" TYPE="char" LENGTH="32" NOTNULL="true" SEQUENCE="false" COMMENT="The filter internal name, like 'tex'." PREVIOUS="id" NEXT="contextid"/>
         <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="References context.id." PREVIOUS="filter" NEXT="name"/>
         <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" COMMENT="The config variable name." PREVIOUS="contextid" NEXT="value"/>
         <FIELD NAME="value" TYPE="text" NOTNULL="false" SEQUENCE="false" COMMENT="The correspoding config variable value." PREVIOUS="name"/>
index 48a78a8..3dcc1d1 100644 (file)
@@ -1524,5 +1524,46 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint(true, 2012120300.04);
     }
 
+    if ($oldversion < 2012120300.07) {
+        // Purge removed module filters and all their settings.
+
+        $tables = array('filter_active', 'filter_config');
+        foreach ($tables as $table) {
+            $DB->delete_records_select($table, "filter LIKE 'mod/%'");
+            $filters = $DB->get_records_sql("SELECT DISTINCT filter FROM {{$table}} WHERE filter LIKE 'filter/%'");
+            foreach ($filters as $filter) {
+                $DB->set_field($table, 'filter', substr($filter->filter, 7), array('filter'=>$filter->filter));
+            }
+        }
+
+        $configs = array('stringfilters', 'filterall');
+        foreach ($configs as $config) {
+            if ($filters = get_config(null, $config)) {
+                $filters = explode(',', $filters);
+                $newfilters = array();
+                foreach($filters as $filter) {
+                    if (strpos($filter, '/') === false) {
+                        $newfilters[] = $filter;
+                    } else if (strpos($filter, 'filter/') === 0) {
+                        $newfilters[] = substr($filter, 7);
+                    }
+                }
+                $filters = implode(',', $newfilters);
+                set_config($config, $filters);
+            }
+        }
+
+        unset($tables);
+        unset($table);
+        unset($configs);
+        unset($newfilters);
+        unset($filters);
+        unset($filter);
+
+        // Main savepoint reached.
+        upgrade_main_savepoint(true, 2012120300.07);
+    }
+
+
     return true;
 }
index ea2621f..1112baf 100644 (file)
@@ -300,6 +300,8 @@ class database_manager {
         }
 
         if ($xmldb_tables = $structure->getTables()) {
+            // Delete in opposite order, this should help with foreign keys in the future.
+            $xmldb_tables = array_reverse($xmldb_tables);
             foreach($xmldb_tables as $table) {
                 if ($this->table_exists($table)) {
                     $this->drop_table($table);
index 900b6e7..4ace5ed 100644 (file)
 
 defined('MOODLE_INTERNAL') || die();
 
+/**
+ * Not used any more, the account lockout handling is now
+ * part of authenticate_user_login().
+ * @deprecated
+ */
+function update_login_count() {
+    // TODO: delete function in Moodle 2.6
+    debugging('update_login_count() is deprecated, all calls need to be removed');
+}
+
+/**
+ * Not used any more, replaced by proper account lockout.
+ * @deprecated
+ */
+function reset_login_count() {
+    // TODO: delete function in Moodle 2.6
+    debugging('reset_login_count() is deprecated, all calls need to be removed');
+}
+
 /**
  * Unsupported session id rewriting.
  * @deprecated
index 6c2eac2..d4b8ac7 100644 (file)
@@ -33,7 +33,7 @@ class tinymce_dragmath extends editor_tinymce_plugin {
         if ($this->get_config('requiretex', 1)) {
             // If TeX filter is disabled, do not add button.
             $filters = filter_get_active_in_context($context);
-            if (!array_key_exists('filter/tex', $filters)) {
+            if (!array_key_exists('tex', $filters)) {
                 return;
             }
         }
index f61c9ea..c2751a5 100644 (file)
@@ -34,7 +34,7 @@ class tinymce_moodleemoticon extends editor_tinymce_plugin {
         if ($this->get_config('requireemoticon', 1)) {
             // If emoticon filter is disabled, do not add button.
             $filters = filter_get_active_in_context($context);
-            if (!array_key_exists('filter/emoticon', $filters)) {
+            if (!array_key_exists('emoticon', $filters)) {
                 return;
             }
         }
index ed606cf..afe04fb 100644 (file)
@@ -1,3 +1,5 @@
+Version 2.0.6.1 (2012-11-16)\r
+       Fixed security issue with google spellchecker.\r
 Version 2.0.6 (2011-09-29)\r
        Fixed incorrect position of suggestion menu.\r
        Fixed handling of mispelled words with no suggestions in PSpellShell engine.\r
index e3acf2d..5edf76a 100644 (file)
@@ -51,6 +51,8 @@ class GoogleSpell extends SpellChecker {
        }\r
 \r
        function &_getMatches($lang, $str) {\r
+               $lang = preg_replace('/[^a-z\-]/i', '', $lang); // Sanitize, remove everything but a-z or -\r
+               $str = preg_replace('/[\x00-\x1F\x7F]/', '', $str); // Sanitize, remove all control characters\r
                $server = "www.google.com";\r
                $port = 443;\r
                $path = "/tbproxy/spell?lang=" . $lang . "&hl=en";\r
@@ -126,6 +128,7 @@ class GoogleSpell extends SpellChecker {
        }\r
 \r
        function _unhtmlentities($string) {\r
+        return textlib::entities_to_utf8($string); // Moodle hack\r
                $string = preg_replace('~&#x([0-9a-f]+);~ei', 'chr(hexdec("\\1"))', $string);\r
                $string = preg_replace('~&#([0-9]+);~e', 'chr(\\1)', $string);\r
 \r
index ffd1bee..deb2af9 100644 (file)
@@ -7,6 +7,4 @@ List of changes:
 * Modified config file to use moodle $CFG.
 * Moved static files to /tinymce/ subfolder.
 * MDL-25736 - French spellchecker fixes.
-
-Commits:
-https://github.com/moodle/custom-tinymce_spellchecker_php/commits/MOODLE_22_2.0.6b
+* Fix htmlentities conversion in GoogleSpell.php
index 2bdb84d..5696839 100644 (file)
@@ -2,6 +2,11 @@ This files describes API changes in /lib/editor/tinymce/* - TinyMCE editor,
 information provided here is intended especially for developers.
 
 
+=== 2.5 ===
+
+* update filter related code to use short filter names instead
+  of original paths, ex.: 'filter/tex' ---> 'tex'
+
 === 2.4 ===
 
 new features:
index 7b9f619..c9d8c05 100644 (file)
@@ -813,7 +813,7 @@ class file_info_area_backup_section extends file_info {
             'component' => 'backup',
             'filearea' => 'section',
             'emptyfilename' => '.');
-        $sql1 = "SELECT DISTINCT cs.id sectionid FROM {files} f, {course_sections} cs
+        $sql1 = "SELECT DISTINCT cs.id AS sectionid FROM {files} f, {course_sections} cs
             WHERE cs.course = :courseid
             AND f.contextid = :contextid
             AND f.component = :component
index 09e7cb3..02b1774 100644 (file)
@@ -204,7 +204,7 @@ class file_info_context_coursecat extends file_info {
             return $cnt;
         }
 
-        $rs = $DB->get_recordset_sql('SELECT ctx.id contextid, c.visible
+        $rs = $DB->get_recordset_sql('SELECT ctx.id AS contextid, c.visible
                 FROM {context} ctx, {course} c
                 WHERE ctx.instanceid = c.id
                 AND ctx.contextlevel = :courselevel
@@ -226,7 +226,7 @@ class file_info_context_coursecat extends file_info {
             return $cnt;
         }
 
-        $rs = $DB->get_recordset_sql('SELECT ctx.id contextid, cat.visible
+        $rs = $DB->get_recordset_sql('SELECT ctx.id AS contextid, cat.visible
                 FROM {context} ctx, {course_categories} cat
                 WHERE ctx.instanceid = cat.id
                 AND ctx.contextlevel = :catlevel
index ab120b3..3f6cb45 100644 (file)
@@ -2715,7 +2715,7 @@ function file_modify_html_header($text) {
     }*/
 
     $ufo = '';
-    if (filter_is_enabled('filter/mediaplugin')) {
+    if (filter_is_enabled('mediaplugin')) {
         // this script is needed by most media filter plugins.
         $attributes = array('type'=>'text/javascript', 'src'=>$CFG->httpswwwroot . '/lib/ufo.js');
         $ufo = html_writer::tag('script', '', $attributes) . "\n";
index de80835..b8760c0 100644 (file)
@@ -1,5 +1,4 @@
 <?php
-
 // This file is part of Moodle - http://moodle.org/
 //
 // Moodle is free software: you can redistribute it and/or modify
@@ -18,8 +17,7 @@
 /**
  * Library functions for managing text filter plugins.
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -50,8 +48,7 @@ define('TEXTFILTER_EXCL_SEPARATOR', '-%-');
  *
  * This class is a singleton.
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -115,33 +112,27 @@ class filter_manager {
     }
 
     /**
-     * Factory method for creating a filter
+     * Factory method for creating a filter.
      *
-     * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
-     * @param object $context context object.
+     * @param string $filtername The filter name, for example 'tex'.
+     * @param context $context context object.
      * @param array $localconfig array of local configuration variables for this filter.
-     * @return object moodle_text_filter The filter, or null, if this type of filter is
+     * @return moodle_text_filter The filter, or null, if this type of filter is
      *      not recognised or could not be created.
      */
     protected function make_filter_object($filtername, $context, $localconfig) {
         global $CFG;
-        $path = $CFG->dirroot .'/'. $filtername .'/filter.php';
+        $path = $CFG->dirroot .'/filter/'. $filtername .'/filter.php';
         if (!is_readable($path)) {
             return null;
         }
         include_once($path);
 
-        $filterclassname = 'filter_' . basename($filtername);
+        $filterclassname = 'filter_' . $filtername;
         if (class_exists($filterclassname)) {
             return new $filterclassname($context, $localconfig);
         }
 
-        // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
-        $legacyfunctionname = basename($filtername) . '_filter';
-        if (function_exists($legacyfunctionname)) {
-            return new legacy_filter($legacyfunctionname, $context, $localconfig);
-        }
-
         return null;
     }
 
@@ -193,7 +184,7 @@ class filter_manager {
      */
     public function filter_text($text, $context, array $options = array()) {
         $text = $this->apply_filter_chain($text, $this->get_text_filters($context), $options);
-        /// <nolink> tags removed for XHTML compatibility
+        // <nolink> tags removed for XHTML compatibility
         $text = str_replace(array('<nolink>', '</nolink>'), '', $text);
         return $text;
     }
@@ -202,7 +193,7 @@ class filter_manager {
      * Filter a piece of string
      *
      * @param string $string The text to filter
-     * @param object $context
+     * @param context $context
      * @return string resulting string
      */
     public function filter_string($string, $context) {
@@ -211,7 +202,7 @@ class filter_manager {
 
     /**
      * @todo Document this function
-     * @param object $context
+     * @param context $context
      * @return object A string filter
      */
     public function text_filtering_hash($context) {
@@ -253,8 +244,7 @@ class filter_manager {
  *
  * @todo Document this class
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -286,8 +276,7 @@ class null_filter_manager {
  *
  * @todo Document this class
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -351,8 +340,7 @@ class performance_measuring_filter_manager extends filter_manager {
  * Base class for text filters. You just need to override this class and
  * implement the filter method.
  *
- * @package    core
- * @subpackage filter
+ * @package    core_filter
  * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -364,9 +352,9 @@ abstract class moodle_text_filter {
 
     /**
      * Set any context-specific configuration for this filter.
-     * @param object $context The current course id.
-     * @param object $context The current context.
-     * @param array $config Any context-specific configuration for this filter.
+     *
+     * @param context $context The current context.
+     * @param array $localconfig Any context-specific configuration for this filter.
      */
     public function __construct($context, array $localconfig) {
         $this->context = $context;
@@ -408,50 +396,6 @@ abstract class moodle_text_filter {
     public abstract function filter($text, array $options = array());
 }
 
-/**
- * moodle_text_filter implementation that encapsulates an old-style filter that
- * only defines a function, not a class.
- *
- * @deprecated since 2.2, see MDL-29995
- * @todo will be out in 2.3, see MDL-29996
- * @package    core
- * @subpackage filter
- * @copyright  1999 onwards Martin Dougiamas  {@link http://moodle.com}
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class legacy_filter extends moodle_text_filter {
-    /** @var string */
-    protected $filterfunction;
-    protected $courseid;
-
-    /**
-     * Set any context-specific configuration for this filter.
-     *
-     * @param string $filterfunction
-     * @param object $context The current context.
-     * @param array $config Any context-specific configuration for this filter.
-     */
-    public function __construct($filterfunction, $context, array $localconfig) {
-        parent::__construct($context, $localconfig);
-        $this->filterfunction = $filterfunction;
-        $this->courseid = get_courseid_from_context($this->context);
-    }
-
-    /**
-     * @param string $text
-     * @param array $options options - not supported for legacy filters
-     * @return mixed
-     */
-    public function filter($text, array $options = array()) {
-        if ($this->courseid) {
-            // old filters are called only when inside courses
-            return call_user_func($this->filterfunction, $this->courseid, $text);
-        } else {
-            return $text;
-        }
-    }
-}
-
 /**
  * This is just a little object to define a phrase and some instructions
  * for how to process it.  Filters can create an array of these to pass
@@ -509,66 +453,40 @@ class filterobject {
 }
 
 /**
- * Look up the name of this filter in the most appropriate location.
- * If $filterlocation = 'mod' then does get_string('filtername', $filter);
- * else if $filterlocation = 'filter' then does get_string('filtername', 'filter_' . $filter);
- * with a fallback to get_string('filtername', $filter) for backwards compatibility.
- * These are the only two options supported at the moment.
+ * Look up the name of this filter
  *
- * @param string $filter the folder name where the filter lives.
+ * @param string $filter the filter name
  * @return string the human-readable name for this filter.
  */
 function filter_get_name($filter) {
-    // TODO: should we be using pluginname here instead? , see MDL-29998
-    list($type, $filter) = explode('/', $filter);
-    switch ($type) {
-        case 'filter':
-            $strfiltername = get_string('filtername', 'filter_' . $filter);
-            if (substr($strfiltername, 0, 2) != '[[') {
-                // found a valid string.
-                return $strfiltername;
-            }
-            // Fall through to try the legacy location.
-
-        // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
-        case 'mod':
-            $strfiltername = get_string('filtername', $filter);
-            if (substr($strfiltername, 0, 2) == '[[') {
-                $strfiltername .= ' (' . $type . '/' . $filter . ')';
-            }
-            return $strfiltername;
+    if (strpos($filter, 'filter/') === 0) {
+        debugging("Old '$filter'' parameter used in filter_get_name()");
+        $filter = substr($filter, 7);
+    } else if (strpos($filter, '/') !== false) {
+        throw new coding_exception('Unknown filter type ' . $filter);
+    }
 
-        default:
-            throw new coding_exception('Unknown filter type ' . $type);
+    if (get_string_manager()->string_exists('filtername', 'filter_' . $filter)) {
+        return get_string('filtername', 'filter_' . $filter);
+    } else {
+        return $filter;
     }
 }
 
 /**
  * Get the names of all the filters installed in this Moodle.
  *
- * @global object
  * @return array path => filter name from the appropriate lang file. e.g.
- * array('mod/glossary' => 'Glossary Auto-linking', 'filter/tex' => 'TeX Notation');
+ * array('tex' => 'TeX Notation');
  * sorted in alphabetical order of name.
  */
 function filter_get_all_installed() {
     global $CFG;
+
     $filternames = array();
-    // TODO: deprecated since 2.2, will be out in 2.3, see MDL-29996
-    $filterlocations = array('mod', 'filter');
-    foreach ($filterlocations as $filterlocation) {
-        // TODO: move get_list_of_plugins() to get_plugin_list()
-        $filters = get_list_of_plugins($filterlocation);
-        foreach ($filters as $filter) {
-            // MDL-29994 - Ignore mod/data and mod/glossary filters forever, this will be out in 2.3
-            if ($filterlocation == 'mod' && ($filter == 'data' || $filter == 'glossary')) {
-                continue;
-            }
-            $path = $filterlocation . '/' . $filter;
-            if (is_readable($CFG->dirroot . '/' . $path . '/filter.php')) {
-                $strfiltername = filter_get_name($path);
-                $filternames[$path] = $strfiltername;
-            }
+    foreach (get_list_of_plugins('filter') as $filter) {
+        if (is_readable("$CFG->dirroot/filter/$filter/filter.php")) {
+            $filternames[$filter] = filter_get_name($filter);
         }
     }
     collatorlib::asort($filternames);
@@ -578,17 +496,11 @@ function filter_get_all_installed() {
 /**
  * Set the global activated state for a text filter.
  *
- * @global object
- * @param string $filter The filter name, for example 'filter/tex' or 'mod/glossary'.
- * @param integer $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED.
- * @param integer $sortorder (optional) a position in the sortorder to place this filter.
- *      If not given defaults to:
- *      No change in order if we are updating an existing record, and not changing to or from TEXTFILTER_DISABLED.
- *      Just after the last currently active filter when adding an unknown filter
- *          in state TEXTFILTER_ON or TEXTFILTER_OFF, or enabling/disabling an existing filter.
- *      Just after the very last filter when adding an unknown filter in state TEXTFILTER_DISABLED
+ * @param string $filtername The filter name, for example 'tex'.
+ * @param int $state One of the values TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED.
+ * @param int $move 1 means up, 0 means the same, -1 means down
  */
-function filter_set_global_state($filter, $state, $sortorder = false) {
+function filter_set_global_state($filtername, $state, $move = 0) {
     global $DB;
 
     // Check requested state is valid.
@@ -597,84 +509,141 @@ function filter_set_global_state($filter, $state, $sortorder = false) {
                 "Must be one of TEXTFILTER_ON, TEXTFILTER_OFF or TEXTFILTER_DISABLED.");
     }
 
-    // Check sortorder is valid.
-    if ($sortorder !== false) {
-        if ($sortorder < 1 || $sortorder > $DB->get_field('filter_active', 'MAX(sortorder)', array()) + 1) {
-            throw new coding_exception("Invalid sort order passed to filter_set_global_state.");
-        }
+    if ($move > 0) {
+        $move = 1;
+    } else if ($move < 0) {
+        $move = -1;
+    }
+
+    if (strpos($filtername, 'filter/') === 0) {
+        //debugging("Old filtername '$filtername' parameter used in filter_set_global_state()", DEBUG_DEVELOPER);
+        $filtername = substr($filtername, 7);
+    } else if (strpos($filtername, '/') !== false) {
+        throw new coding_exception("Invalid filter name '$filtername' used in filter_set_global_state()");
     }
 
-    // See if there is an existing record.
+    $transaction = $DB->start_delegated_transaction();
+
     $syscontext = context_system::instance();
-    $rec = $DB->get_record('filter_active', array('filter' => $filter, 'contextid' => $syscontext->id));
-    if (empty($rec)) {
-        $insert = true;
-        $rec = new stdClass;
-        $rec->filter = $filter;
-        $rec->contextid = $syscontext->id;
-    } else {
-        $insert = false;
-        if ($sortorder === false && !($rec->active == TEXTFILTER_DISABLED xor $state == TEXTFILTER_DISABLED)) {
-            $sortorder = $rec->sortorder;
+    $filters = $DB->get_records('filter_active', array('contextid' => $syscontext->id), 'sortorder ASC');
+
+    $on = array();
+   &nb