From df997f841f93d676d782a46cfb8547ebc7319102 Mon Sep 17 00:00:00 2001
From: Petr Skoda <skodak@moodle.org>
Date: Mon, 21 Jun 2010 15:30:49 +0000
Subject: [PATCH] MDL-21782 reworked enrolment framework, the core
 infrastructure is in place, the basic plugins are all implemented; see the
 tracker issue for list of unfinished bits, expect more changes and
 improvements during the next week

AMOS START
    MOV [sendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage,enrol_self]
    MOV [configsendcoursewelcomemessage,core_admin],[sendcoursewelcomemessage_desc,enrol_self]
    MOV [enrolstartdate,core],[enrolstartdate,enrol_self]
    MOV [enrolenddate,core],[enrolenddate,enrol_self]
    CPY [welcometocourse,core],[welcometocourse,enrol_self]
    CPY [welcometocoursetext,core],[welcometocoursetext,enrol_self]
    MOV [notenrollable,core],[notenrollable,core_enrol]
    MOV [enrolenddaterror,core],[enrolenddaterror,enrol_self]
    MOV [enrolmentkeyhint,core],[passwordinvalidhint,enrol_self]
    MOV [coursemanager,core_admin],[coursecontact,core_admin]
    MOV [configcoursemanager,core_admin],[coursecontact_desc,core_admin]
    MOV [enrolledincourserole,core],[enrolledincourserole,enrol_manual]
    MOV [enrolme,core],[enrolme,core_enrol]
    MOV [unenrol,core],[unenrol,core_enrol]
    MOV [unenrolme,core],[unenrolme,core_enrol]
    MOV [enrolmentnew,core],[enrolmentnew,core_enrol]
    MOV [enrolmentnewuser,core],[enrolmentnewuser,core_enrol]
    MOV [enrolments,core],[enrolments,core_enrol]
    MOV [enrolperiod,core],[enrolperiod,core_enrol]
    MOV [unenrolroleusers,core],[unenrolroleusers,core_enrol]
AMOS END
---
 admin/bloglevelupgrade.php                    |    2 +-
 admin/cron.php                                |   88 +-
 admin/enrol.php                               |  239 ++-
 admin/enrol_config.php                        |   70 -
 admin/generator.php                           |   21 +-
 admin/mnet/enr_course_enrol.php               |    9 +-
 admin/mnet/enr_courses.php                    |    7 +-
 admin/mnet/enr_hosts.php                      |    6 +-
 .../security/lang/en/report_security.php      |   18 -
 admin/report/security/lib.php                 |  191 +--
 admin/roles/assign.php                        |  111 +-
 admin/roles/check.php                         |    8 +-
 admin/roles/lib.php                           |   56 +-
 admin/roles/manage.php                        |    1 -
 admin/roles/override.php                      |    9 +-
 admin/roles/permissions.php                   |   11 -
 admin/roles/tabs.php                          |  116 --
 admin/roles/usersroles.php                    |    2 +-
 admin/settings/appearance.php                 |    6 +-
 admin/settings/courses.php                    |   48 -
 admin/settings/frontpage.php                  |    1 -
 admin/settings/plugins.php                    |   82 +-
 admin/settings/security.php                   |    1 +
 admin/settings/users.php                      |   60 +-
 admin/uploaduser.php                          |  214 ++-
 admin/uploaduser_form.php                     |   44 +-
 admin/user/user_bulk_enrol.php                |   16 +-
 admin/webservice/testclient_forms.php         |  131 +-
 auth/cas/auth.php                             |   10 +-
 auth/fc/auth.php                              |    4 +-
 auth/ldap/auth.php                            |   10 +-
 auth/mnet/auth.php                            |   28 +-
 auth/shibboleth/index.php                     |    2 +-
 backup/backuplib.php                          |    5 +-
 backup/moodle2/backup_course_task.class.php   |    4 +-
 backup/moodle2/backup_final_task.class.php    |    2 +
 backup/moodle2/backup_stepslib.php            |   22 +-
 backup/restore_check.html                     |   13 +-
 backup/restore_form.html                      |   31 +-
 backup/restorelib.php                         |   12 +-
 blocks/course_list/block_course_list.php      |    5 +-
 .../course_overview/block_course_overview.php |    2 +-
 .../course_summary/block_course_summary.php   |    2 +-
 blocks/course_summary/db/upgrade.php          |    2 +-
 calendar/export_execute.php                   |    2 +-
 calendar/lib.php                              |    7 +-
 calendar/view.php                             |    2 +-
 cohort/edit.php                               |    1 +
 cohort/lib.php                                |   59 +-
 course/category.php                           |   19 +-
 course/edit.php                               |  268 ++--
 course/edit_form.php                          |  307 +---
 course/enrol.php                              |  111 +-
 course/external.php                           |  749 ----------
 course/externallib.php                        |    6 +-
 course/importstudents.html                    |   80 -
 course/importstudents.php                     |  170 ---
 course/index.php                              |   23 +-
 course/info.php                               |   18 +-
 course/lib.php                                |  389 ++---
 course/pending.php                            |    7 +-
 course/renderer.php                           |   18 +-
 course/report/completion/lib.php              |    2 +
 course/report/log/lib.php                     |    9 +-
 course/request_form.php                       |    5 -
 course/reset_form.php                         |    8 +-
 course/unenrol.php                            |  123 --
 course/user.php                               |    3 +-
 enrol/authorize/db/upgrade.php                |    2 +-
 enrol/authorize/enrol.php                     |    6 +-
 enrol/authorize/locallib.php                  |    6 +-
 enrol/authorize/uploadcsv.php                 |    3 +-
 enrol/category/cli/sync.php                   |   38 +
 enrol/category/db/access.php                  |   38 +
 enrol/category/db/events.php                  |   40 +
 enrol/category/db/install.php                 |   69 +
 enrol/category/lang/en/enrol_category.php     |   28 +
 enrol/category/lib.php                        |   97 ++
 enrol/category/locallib.php                   |  323 +++++
 enrol/category/settings.php                   |   37 +
 enrol/category/version.php                    |   27 +
 enrol/cohort/addinstance.php                  |   60 +
 enrol/cohort/addinstance_form.php             |   72 +
 enrol/cohort/db/access.php                    |   37 +
 enrol/cohort/db/events.php                    |   45 +
 enrol/cohort/lang/en/enrol_cohort.php         |   28 +
 enrol/cohort/lib.php                          |  118 ++
 enrol/cohort/locallib.php                     |  189 +++
 enrol/cohort/settings.php                     |   44 +
 enrol/cohort/version.php                      |   27 +
 enrol/database/cli/sync.php                   |   40 +
 enrol/database/config.html                    |  251 ----
 enrol/database/db/access.php                  |   45 +
 enrol/database/db/install.php                 |  107 ++
 enrol/database/enrol.php                      |  681 ---------
 enrol/database/enrol_database_sync.php        |   41 -
 enrol/database/lang/en/enrol_database.php     |   93 +-
 enrol/database/lib.php                        |  487 +++++++
 enrol/database/settings.php                   |  115 ++
 enrol/database/version.php                    |   27 +
 enrol/enrol.class.php                         |   22 -
 enrol/externallib.php                         |  163 ++-
 enrol/flatfile/enrol.php                      |    5 +-
 enrol/guest/addinstance.php                   |   43 +
 enrol/guest/db/access.php                     |   39 +
 enrol/guest/lang/en/enrol_guest.php           |   40 +
 enrol/guest/lib.php                           |  291 ++++
 enrol/guest/locallib.php                      |   76 +
 enrol/guest/settings.php                      |   56 +
 enrol/guest/version.php                       |   26 +
 enrol/imsenterprise/enrol.php                 |   12 +-
 enrol/index.html                              |    1 -
 enrol/index.php                               |   99 ++
 enrol/instances.php                           |  237 +++
 enrol/ldap/enrol.php                          |   16 +-
 enrol/ldap/enrol_ldap_sync.php                |    5 -
 enrol/manual/addinstance.php                  |   43 +
 enrol/manual/config.html                      |   63 -
 enrol/manual/db/access.php                    |   64 +
 enrol/manual/db/install.php                   |   32 +
 enrol/manual/enrol.html                       |   47 -
 enrol/manual/enrol.php                        |  455 ------
 enrol/manual/lang/en/enrol_manual.php         |   29 +-
 enrol/manual/lib.php                          |  222 +++
 enrol/manual/locallib.php                     |  154 ++
 enrol/manual/manage.php                       |  188 +++
 enrol/manual/settings.php                     |   60 +
 enrol/manual/unenrolself.php                  |   58 +
 enrol/manual/version.php                      |   26 +
 enrol/meta/addinstance.php                    |   60 +
 enrol/meta/addinstance_form.php               |   96 ++
 enrol/meta/db/access.php                      |   45 +
 enrol/meta/db/events.php                      |   57 +
 enrol/meta/db/install.php                     |   42 +
 enrol/meta/lang/en/enrol_meta.php             |   32 +
 enrol/meta/lib.php                            |  107 ++
 enrol/meta/locallib.php                       |  317 ++++
 enrol/meta/settings.php                       |   41 +
 enrol/meta/version.php                        |   27 +
 enrol/mnet/enrol.php                          |    4 +-
 enrol/otherusers.php                          |   91 ++
 enrol/paypal/enrol.php                        |    3 -
 enrol/paypal/ipn.php                          |    2 +-
 enrol/paypal/return.php                       |    2 +-
 enrol/self/addinstance.php                    |   43 +
 enrol/self/db/access.php                      |   52 +
 enrol/self/db/install.php                     |   10 +
 enrol/self/lang/en/enrol_self.php             |   63 +
 enrol/self/lib.php                            |  433 ++++++
 enrol/self/locallib.php                       |   84 ++
 enrol/self/settings.php                       |   79 +
 enrol/self/unenrolself.php                    |   58 +
 enrol/self/version.php                        |   26 +
 enrol/users.php                               |  518 +++++++
 enrol/users_forms.php                         |  203 +++
 grade/querylib.php                            |    2 +-
 grade/report/overview/lib.php                 |    4 +-
 group/assign.php                              |    6 +-
 group/autogroup_form.php                      |   10 +-
 group/group_form.php                          |    4 +-
 group/index.php                               |    6 +-
 group/lib.php                                 |   24 +-
 index.php                                     |    2 +-
 lang/en/admin.php                             |   21 +-
 lang/en/enrol.php                             |   63 +
 lang/en/error.php                             |    7 +-
 lang/en/group.php                             |    3 +
 lang/en/moodle.php                            |   85 +-
 lang/en/role.php                              |   11 +-
 lib/accesslib.php                             | 1081 +++++---------
 lib/adminlib.php                              |  263 +++-
 .../completion_criteria_duration.php          |    2 +
 lib/completion/cron.php                       |    2 +
 lib/datalib.php                               |  445 +-----
 lib/db/access.php                             |   54 +-
 lib/db/events.php                             |   32 +-
 lib/db/install.php                            |    7 +-
 lib/db/install.xml                            |  165 ++-
 lib/db/services.php                           |   17 +-
 lib/db/upgrade.php                            |  840 +++++++++--
 lib/ddl/mysql_sql_generator.php               |    3 +-
 lib/deprecatedlib.php                         |   33 +
 lib/enrollib.php                              | 1284 +++++++++++++++++
 lib/eventslib.php                             |    6 +
 lib/externallib.php                           |   22 +-
 lib/file/file_browser.php                     |    4 +-
 lib/file/file_info_course.php                 |    2 +-
 lib/gradelib.php                              |   28 +
 lib/moodlelib.php                             |  685 ++++-----
 lib/navigationlib.php                         |  136 +-
 lib/outputrequirementslib.php                 |    6 +-
 lib/sessionlib.php                            |   14 +-
 lib/setup.php                                 |    1 +
 lib/setuplib.php                              |   13 +-
 lib/simpletest/broken_testfilelib.php         |    2 +-
 lib/simpletest/testaccesslib.php              |   35 +-
 lib/upgradelib.php                            |    6 +-
 message/lib.php                               |    2 +-
 mod/assignment/db/events.php                  |   36 +
 mod/choice/view.php                           |    2 +-
 mod/feedback/show_entries.php                 |   30 +-
 mod/feedback/show_nonrespondents.php          |   32 +-
 mod/forum/db/events.php                       |   39 +
 mod/forum/db/upgrade.php                      |   28 -
 mod/forum/lib.php                             |   45 +-
 mod/forum/post.php                            |    4 +-
 notes/index.php                               |    2 +-
 pluginfile.php                                |   25 +-
 question/type/randomsamatch/questiontype.php  |    2 +-
 repository/lib.php                            |    2 +-
 rss/file.php                                  |   24 +-
 search/documents/label_document.php           |    2 +-
 search/documents/resource_document.php        |    2 +-
 search/querylib.php                           |    2 +-
 tag/coursetagslib.php                         |    3 +-
 tag/locallib.php                              |    2 +-
 user/action_redir.php                         |    2 -
 user/extendenrol.php                          |  188 ---
 user/groupextendenrol.php                     |  185 ---
 user/index.php                                |   99 +-
 user/lib.php                                  |    5 +-
 user/profile.php                              |    9 +-
 user/selector/lib.php                         |   11 +-
 user/view.php                                 |    9 +-
 version.php                                   |    2 +-
 225 files changed, 11021 insertions(+), 7605 deletions(-)
 delete mode 100644 admin/enrol_config.php
 delete mode 100755 admin/roles/tabs.php
 delete mode 100644 course/external.php
 delete mode 100644 course/importstudents.html
 delete mode 100644 course/importstudents.php
 delete mode 100644 course/unenrol.php
 create mode 100644 enrol/category/cli/sync.php
 create mode 100644 enrol/category/db/access.php
 create mode 100644 enrol/category/db/events.php
 create mode 100644 enrol/category/db/install.php
 create mode 100644 enrol/category/lang/en/enrol_category.php
 create mode 100644 enrol/category/lib.php
 create mode 100644 enrol/category/locallib.php
 create mode 100644 enrol/category/settings.php
 create mode 100644 enrol/category/version.php
 create mode 100644 enrol/cohort/addinstance.php
 create mode 100644 enrol/cohort/addinstance_form.php
 create mode 100644 enrol/cohort/db/access.php
 create mode 100644 enrol/cohort/db/events.php
 create mode 100644 enrol/cohort/lang/en/enrol_cohort.php
 create mode 100644 enrol/cohort/lib.php
 create mode 100644 enrol/cohort/locallib.php
 create mode 100644 enrol/cohort/settings.php
 create mode 100644 enrol/cohort/version.php
 create mode 100644 enrol/database/cli/sync.php
 delete mode 100644 enrol/database/config.html
 create mode 100644 enrol/database/db/access.php
 create mode 100644 enrol/database/db/install.php
 delete mode 100644 enrol/database/enrol.php
 delete mode 100644 enrol/database/enrol_database_sync.php
 create mode 100644 enrol/database/lib.php
 create mode 100644 enrol/database/settings.php
 create mode 100644 enrol/database/version.php
 delete mode 100644 enrol/enrol.class.php
 create mode 100644 enrol/guest/addinstance.php
 create mode 100644 enrol/guest/db/access.php
 create mode 100644 enrol/guest/lang/en/enrol_guest.php
 create mode 100644 enrol/guest/lib.php
 create mode 100644 enrol/guest/locallib.php
 create mode 100644 enrol/guest/settings.php
 create mode 100644 enrol/guest/version.php
 delete mode 100644 enrol/index.html
 create mode 100644 enrol/index.php
 create mode 100644 enrol/instances.php
 create mode 100644 enrol/manual/addinstance.php
 delete mode 100644 enrol/manual/config.html
 create mode 100644 enrol/manual/db/access.php
 create mode 100644 enrol/manual/db/install.php
 delete mode 100644 enrol/manual/enrol.html
 delete mode 100644 enrol/manual/enrol.php
 create mode 100644 enrol/manual/lib.php
 create mode 100644 enrol/manual/locallib.php
 create mode 100644 enrol/manual/manage.php
 create mode 100644 enrol/manual/settings.php
 create mode 100644 enrol/manual/unenrolself.php
 create mode 100644 enrol/manual/version.php
 create mode 100644 enrol/meta/addinstance.php
 create mode 100644 enrol/meta/addinstance_form.php
 create mode 100644 enrol/meta/db/access.php
 create mode 100644 enrol/meta/db/events.php
 create mode 100644 enrol/meta/db/install.php
 create mode 100644 enrol/meta/lang/en/enrol_meta.php
 create mode 100644 enrol/meta/lib.php
 create mode 100644 enrol/meta/locallib.php
 create mode 100644 enrol/meta/settings.php
 create mode 100644 enrol/meta/version.php
 create mode 100644 enrol/otherusers.php
 create mode 100644 enrol/self/addinstance.php
 create mode 100644 enrol/self/db/access.php
 create mode 100644 enrol/self/db/install.php
 create mode 100644 enrol/self/lang/en/enrol_self.php
 create mode 100644 enrol/self/lib.php
 create mode 100644 enrol/self/locallib.php
 create mode 100644 enrol/self/settings.php
 create mode 100644 enrol/self/unenrolself.php
 create mode 100644 enrol/self/version.php
 create mode 100644 enrol/users.php
 create mode 100644 enrol/users_forms.php
 create mode 100644 lang/en/enrol.php
 create mode 100644 lib/enrollib.php
 create mode 100644 mod/assignment/db/events.php
 create mode 100644 mod/forum/db/events.php
 delete mode 100644 user/extendenrol.php
 delete mode 100755 user/groupextendenrol.php

diff --git a/admin/bloglevelupgrade.php b/admin/bloglevelupgrade.php
index f833c176915a..b04d1dc46d19 100644
--- a/admin/bloglevelupgrade.php
+++ b/admin/bloglevelupgrade.php
@@ -48,7 +48,7 @@
     $a->blogcount = 0;
 
     foreach ($bloggers as $blogger) {
-        $courses = get_my_courses($blogger->userid);
+        $courses = enrol_get_users_courses($blogger->userid, true, 'groupmode,groupmodeforce');
         $blogentries = $DB->get_records('post', array('module' => 'blog', 'userid' => $blogger->userid));
 
         foreach ($courses as $course) {
diff --git a/admin/cron.php b/admin/cron.php
index ef41ce01e464..79def22767d4 100644
--- a/admin/cron.php
+++ b/admin/cron.php
@@ -205,34 +205,6 @@
     }
     mtrace('Finished admin reports');
 
-    mtrace('Removing expired enrolments ...', '');     // See MDL-8785
-    $timenow = time();
-    $somefound = false;
-    // The preferred way saves memory, datablib
-    // find courses where limited enrolment is enabled
-    $sql = "SELECT ra.roleid, ra.userid, ra.contextid
-              FROM {course} c
-              JOIN {context} cx ON cx.instanceid = c.id
-              JOIN {role_assignments} ra ON ra.contextid = cx.id
-             WHERE cx.contextlevel = '".CONTEXT_COURSE."'
-                   AND ra.timeend > 0
-                   AND ra.timeend < ?
-                   AND c.enrolperiod > 0";
-    if ($rs = $DB->get_recordset_sql($sql, array($timenow))) {
-        foreach ($rs as $oldenrolment) {
-            role_unassign($oldenrolment->roleid, $oldenrolment->userid, 0, $oldenrolment->contextid);
-            $somefound = true;
-        }
-        $rs->close();
-    }
-    if ($somefound) {
-        mtrace('Done');
-    } else {
-        mtrace('none found');
-    }
-
-
-
     mtrace('Starting main gradebook job ...');
     grade_cron();
     mtrace('done.');
@@ -270,41 +242,6 @@
     if ($random100 < 20) {     // Approximately 20% of the time.
         mtrace("Running clean-up tasks...");
 
-        /// Unenrol users who haven't logged in for $CFG->longtimenosee
-
-        if ($CFG->longtimenosee) { // value in days
-            $cuttime = $timenow - ($CFG->longtimenosee * 3600 * 24);
-            $rs = $DB->get_recordset_sql ("SELECT id, userid, courseid
-                                             FROM {user_lastaccess}
-                                            WHERE courseid != ".SITEID."
-                                                  AND timeaccess < ?", array($cuttime));
-            foreach ($rs as $assign) {
-                if ($context = get_context_instance(CONTEXT_COURSE, $assign->courseid)) {
-                    if (role_unassign(0, $assign->userid, 0, $context->id)) {
-                        mtrace("removing user $assign->userid from course $assign->courseid as they have not accessed the course for over $CFG->longtimenosee days");
-                    }
-                }
-            }
-            $rs->close();
-        /// Execute the same query again, looking for remaining records and deleting them
-        /// if the user hasn't moodle/course:participate in the CONTEXT_COURSE context (orphan records)
-            $rs = $DB->get_recordset_sql ("SELECT id, userid, courseid
-                                             FROM {user_lastaccess}
-                                            WHERE courseid != ".SITEID."
-                                                  AND timeaccess < ?", array($cuttime));
-            foreach ($rs as $assign) {
-                if ($context = get_context_instance(CONTEXT_COURSE, $assign->courseid)) {
-                    if (!is_enrolled($context, $assign->userid) and !is_viewing($context, $assign->userid)) {
-                        $DB->delete_records('user_lastaccess', array('userid'=>$assign->userid, 'courseid'=>$assign->courseid));
-                        mtrace("Deleted orphan user_lastaccess for user $assign->userid from course $assign->courseid");
-                    }
-                }
-            }
-            $rs->close();
-        }
-        flush();
-
-
         /// Delete users who haven't confirmed within required period
 
         if (!empty($CFG->deleteunconfirmed)) {
@@ -370,9 +307,6 @@
         }
         flush();
 
-        sync_metacourses();
-        mtrace('Synchronised metacourses');
-
         //
         // generate new password emails for users
         //
@@ -464,20 +398,16 @@
         unset($authplugin);
     }
 
-/// Run the enrolment cron, if any
-    if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) {
-        $plugins = array($CFG->enrol);
-    }
-    require_once($CFG->dirroot .'/enrol/enrol.class.php');
-    foreach ($plugins as $p) {
-        $enrol = enrolment_factory::factory($p);
-        if (method_exists($enrol, 'cron')) {
-            $enrol->cron();
-        }
-        if (!empty($enrol->log)) {
-            mtrace($enrol->log);
+    mtrace("Running enrol crons if required...");
+    $enrols = enrol_get_plugins(true);
+    foreach($enrols as $ename=>$enrol) {
+        // do this for all plugins, disabled plugins might want to cleanup stuff such as roles
+        if (!$enrol->is_cron_required()) {
+            continue;
         }
-        unset($enrol);
+        mtrace("Running cron for enrol_$ename...");
+        $enrol->cron();
+        $enrol->set_config('lastcron', time());
     }
 
     if (!empty($CFG->enablestats) and empty($CFG->disablestatsprocessing)) {
diff --git a/admin/enrol.php b/admin/enrol.php
index 0bd4866c760f..26e430ef8f70 100644
--- a/admin/enrol.php
+++ b/admin/enrol.php
@@ -1,141 +1,120 @@
 <?php
-       // enrol.php - allows admin to edit all enrollment variables
-       //             Yes, enrol is correct English spelling.
-
-    require_once('../config.php');
-    require_once($CFG->libdir.'/adminlib.php');
-
-    $enrol        = optional_param('enrol', $CFG->enrol, PARAM_SAFEDIR);
-    $savesettings = optional_param('savesettings', 0, PARAM_BOOL);
-
-    admin_externalpage_setup('enrolment');
-
-    if (!isset($CFG->sendcoursewelcomemessage)) {
-        set_config('sendcoursewelcomemessage', 1);
-    }
-
-
-    require_once("$CFG->dirroot/enrol/enrol.class.php");   /// Open the factory class
-
-/// Save settings
-
-    if ($frm = data_submitted() and !$savesettings) {
-        if (!confirm_sesskey()) {
-            print_error('confirmsesskeybad', 'error');
+// 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/>.
+
+/**
+ * Enrol config manipulation script.
+ *
+ * @package    core
+ * @subpackage enrol
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once($CFG->libdir.'/adminlib.php');
+
+$action  = required_param('action', PARAM_ACTION);
+$enrol   = required_param('enrol', PARAM_SAFEDIR);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+$PAGE->set_url('/admin/enrol.php');
+
+require_login();
+require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
+require_sesskey();
+
+$enabled = enrol_get_plugins(true);
+$all     = enrol_get_plugins(false);
+
+$return = new moodle_url('/admin/settings.php', array('section'=>'manageenrols'));
+
+switch ($action) {
+    case 'disable':
+        unset($enabled[$enrol]);
+        set_config('enrol_plugins_enabled', implode(',', array_keys($enabled)));
+        break;
+
+    case 'enable':
+        if (!isset($all[$enrol])) {
+            break;
         }
-        if (empty($frm->enable)) {
-            $frm->enable = array();
+        $enabled = array_keys($enabled);
+        $enabled[] = $enrol;
+        set_config('enrol_plugins_enabled', implode(',', $enabled));
+        break;
+
+    case 'up':
+        if (!isset($enabled[$enrol])) {
+            break;
         }
-        if (empty($frm->default)) {
-            $frm->default = '';
-        }
-        if ($frm->default && $frm->default != 'manual' && !in_array($frm->default, $frm->enable)) {
-            $frm->enable[] = $frm->default;
-        }
-        asort($frm->enable);
-        $frm->enable = array_merge(array('manual'), $frm->enable); // make sure manual plugin is called first
-        set_config('enrol_plugins_enabled', implode(',', $frm->enable));
-        set_config('enrol', $frm->default);
-        redirect("enrol.php", get_string("changessaved"), 1);
-
-    } else if ($frm = data_submitted() and $savesettings) {
-        if (!confirm_sesskey()) {
-            print_error('confirmsesskeybad', 'error');
-        }
-        set_config('sendcoursewelcomemessage', required_param('sendcoursewelcomemessage', PARAM_BOOL));
-    }
-
-/// Print the form
-
-    $str = get_strings(array('enrolmentplugins', 'users', 'administration', 'settings', 'edit'));
-
-    echo $OUTPUT->header();
-
-    $modules = get_plugin_list('enrol');
-    $options = array();
-    foreach ($modules as $module => $moduledir) {
-        $options[$module] = get_string("enrolname", "enrol_$module");
-    }
-    asort($options);
-
-    echo $OUTPUT->box(get_string('configenrolmentplugins', 'admin'));
-
-    echo "<form id=\"enrolmenu\" method=\"post\" action=\"enrol.php\">";
-    echo "<div>";
-    echo "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />";
-
-    $table = new html_table();
-    $table->head = array(get_string('name'), get_string('enable'), get_string('default'), $str->settings);
-    $table->align = array('left', 'center', 'center', 'center');
-    $table->size = array('60%', '', '', '15%');
-    $table->attributes['class'] = 'generaltable enrolplugintable';
-    $table->data = array();
-
-    $enabledplugins = explode(',', $CFG->enrol_plugins_enabled);
-    foreach ($modules as $module => $moduledir) {
-
-        // skip if directory is empty
-        if (!file_exists("$moduledir/enrol.php")) {
-            continue;
+        $enabled = array_keys($enabled);
+        $enabled = array_flip($enabled);
+        $current = $enabled[$enrol];
+        if ($current == 0) {
+            break; //already at the top
         }
-
-        $name = get_string("enrolname", "enrol_$module");
-        $plugin = enrolment_factory::factory($module);
-        $enable = '<input type="checkbox" name="enable[]" value="'.$module.'"';
-        if (in_array($module, $enabledplugins)) {
-            $enable .= ' checked="checked"';
+        $enabled = array_flip($enabled);
+        $enabled[$current] = $enabled[$current - 1];
+        $enabled[$current - 1] = $enrol;
+        set_config('enrol_plugins_enabled', implode(',', $enabled));
+        break;
+
+    case 'down':
+        if (!isset($enabled[$enrol])) {
+            break;
         }
-        if ($module == 'manual') {
-            $enable .= ' disabled="disabled"';
+        $enabled = array_keys($enabled);
+        $enabled = array_flip($enabled);
+        $current = $enabled[$enrol];
+        if ($current == count($enabled) - 1) {
+            break; //already at the end
         }
-        $enable .= ' />';
-        if (method_exists($plugin, 'print_entry')) {
-            $default = '<input type="radio" name="default" value="'.$module.'"';
-            if ($CFG->enrol == $module) {
-                $default .= ' checked="checked"';
-            }
-            $default .= ' />';
+        $enabled = array_flip($enabled);
+        $enabled[$current] = $enabled[$current + 1];
+        $enabled[$current + 1] = $enrol;
+        set_config('enrol_plugins_enabled', implode(',', $enabled));
+        break;
+
+    case 'uninstall':
+        echo $OUTPUT->header();
+        echo $OUTPUT->heading(get_string('enrolments', 'enrol'));
+
+        if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
+            $strplugin = get_string('pluginname', 'enrol_'.$enrol);
         } else {
-            $default = '';
+            $strplugin = $enrol;
         }
-        $table->data[$name] = array($name, $enable, $default,
-                                '<a href="enrol_config.php?enrol='.$module.'">'.$str->edit.'</a>');
-    }
-    asort($table->data);
-
-    echo html_writer::table($table);
-
-    echo "<div style=\"text-align:center\"><input type=\"submit\" value=\"".get_string("savechanges")."\" /></div>\n";
-    echo "</div>";
-    echo "</form>";
 
-    echo '<hr />';
-
-    $yesnooptions = array(0=>get_string('no'), 1=>get_string('yes'));
-
-    echo '<form id="adminsettings" method="post" action="enrol.php">';
-    echo '<div class="settingsform clearfix">';
-    echo $OUTPUT->heading(get_string('commonsettings', 'admin'));
-    echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
-    echo '<input type="hidden" name="savesettings" value="1" />';
-    echo '<fieldset>';
-    echo '<div class="form-item clearfix" id="admin-sendcoursewelcomemessage">';
-
-    echo '<div class="form-label"><label for = "menusendcoursewelcomemessage">' . get_string('sendcoursewelcomemessage', 'admin');
-    echo '<span class="form-shortname">sendcoursewelcomemessage</span>';
-    echo '</label></div>';
-    echo '<div class="form-setting"><div class="form-checkbox defaultsnext">';
-    echo html_writer::select($yesnooptions, 'sendcoursewelcomemessage', $CFG->sendcoursewelcomemessage, false);
-    echo '</div><div class="form-defaultinfo">'.get_string('defaultsettinginfo', 'admin', get_string('yes')).'</div></div>';
-    echo '<div class="form-description">' . get_string('configsendcoursewelcomemessage', 'admin') . '</div>';
-    echo '</div>';
-
-    echo '</fieldset>';
-
-    echo '<div class="form-buttons"><input class="form-submit" type="submit" value="'.get_string('savechanges', 'admin').'" /></div>';
-    echo '</div>';
-    echo '</form>';
-
-    echo $OUTPUT->footer();
+        if (!$confirm) {
+            $uurl = new moodle_url('/admin/enrol.php', array('action'=>'uninstall', 'enrol'=>$enrol, 'sesskey'=>sesskey(), 'confirm'=>1));
+            echo $OUTPUT->confirm(get_string('uninstallconfirm', 'enrol', $strplugin), $uurl, $return);
+            echo $OUTPUT->footer();
+            exit;
+
+        } else {  // Delete everything!!
+            uninstall_plugin('enrol', $enrol);
+
+            $a->plugin = $strplugin;
+            $a->directory = "$CFG->dirroot/enrol/$enrol";
+            echo $OUTPUT->notification(get_string('uninstalldeletefiles', 'enrol', $a), 'notifysuccess');
+            echo $OUTPUT->continue_button($return);
+            echo $OUTPUT->footer();
+            exit;
+        }
+}
 
 
+redirect($return);
\ No newline at end of file
diff --git a/admin/enrol_config.php b/admin/enrol_config.php
deleted file mode 100644
index cf8e60aee7be..000000000000
--- a/admin/enrol_config.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-       // enrol_config.php - allows admin to edit all enrollment variables
-       //                    Yes, enrol is correct English spelling.
-
-    require_once("../config.php");
-    require_once($CFG->libdir.'/adminlib.php');
-
-    admin_externalpage_setup('enrolment');
-
-    $enrol = required_param('enrol', PARAM_ALPHA);
-    $PAGE->set_pagetype('admin-enrol-' . $enrol);
-
-    require_once("$CFG->dirroot/enrol/enrol.class.php");   /// Open the factory class
-
-    $enrolment = enrolment_factory::factory($enrol);
-
-/// If data submitted, then process and store.
-
-    if ($frm = data_submitted()) {
-        if (!confirm_sesskey()) {
-            print_error('confirmsesskeybad', 'error');
-        }
-        if ($enrolment->process_config($frm)) {
-            redirect("enrol.php?sesskey=".sesskey(), get_string("changessaved"), 1);
-        }
-    } else {
-        $frm = $CFG;
-    }
-
-/// Otherwise fill and print the form.
-
-    /// get language strings
-    $str = get_strings(array('enrolmentplugins', 'configuration', 'users', 'administration'));
-
-    unset($options);
-
-    $modules = get_plugin_list('enrol');
-    foreach ($modules as $module => $enroldir) {
-        $options[$module] = get_string("enrolname", "enrol_$module");
-    }
-    asort($options);
-
-    echo $OUTPUT->header();
-
-    echo "<form id=\"enrolmenu\" method=\"post\" action=\"enrol_config.php\">";
-    echo "<div>";
-    echo "<input type=\"hidden\" name=\"sesskey\" value=\"".sesskey()."\" />";
-    echo "<input type=\"hidden\" name=\"enrol\" value=\"".$enrol."\" />";
-
-/// Print current enrolment type description
-    echo $OUTPUT->box_start();
-    echo $OUTPUT->heading($options[$enrol]);
-
-    echo $OUTPUT->box_start('informationbox');
-    print_string("description", "enrol_$enrol");
-    echo $OUTPUT->box_end();
-
-    echo "<hr />";
-
-    $enrolment->config_form($frm);
-
-    echo "<p class=\"centerpara\"><input type=\"submit\" value=\"".get_string("savechanges")."\" /></p>\n";
-    echo $OUTPUT->box_end();
-    echo "</div>";
-    echo "</form>";
-
-    echo $OUTPUT->footer();
-
-    exit;
-
diff --git a/admin/generator.php b/admin/generator.php
index 315d44e9063c..96e3b35b137a 100755
--- a/admin/generator.php
+++ b/admin/generator.php
@@ -709,23 +709,16 @@ public function generate_role_assignments($users, $courses) {
 
                 $context = get_context_instance(CONTEXT_COURSE, $courseid);
                 foreach ($users_to_assign as $random_user) {
-                    $success = role_assign(5, $random_user, 0, $context->id);
+                    role_assign(5, $random_user, $context->id);
 
-                    if ($success) {
-                        $assigned_count++;
-                        $course_users[$courseid][] = $random_user;
-                        if (!isset($assigned_users[$random_user])) {
-                            $assigned_users[$random_user] = 1;
-                        } else {
-                            $assigned_users[$random_user]++;
-                        }
-                        $this->verbose("Student $random_user was assigned to course $courseid.");
+                    $assigned_count++;
+                    $course_users[$courseid][] = $random_user;
+                    if (!isset($assigned_users[$random_user])) {
+                        $assigned_users[$random_user] = 1;
                     } else {
-                        $this->verbose("Could not assign student $random_user to course $courseid!");
-                        if (!$this->get('ignore_errors')) {
-                            die();
-                        }
+                        $assigned_users[$random_user]++;
                     }
+                    $this->verbose("Student $random_user was assigned to course $courseid.");
                 }
             }
 
diff --git a/admin/mnet/enr_course_enrol.php b/admin/mnet/enr_course_enrol.php
index 6cea4621f125..6404a9994eb2 100644
--- a/admin/mnet/enr_course_enrol.php
+++ b/admin/mnet/enr_course_enrol.php
@@ -1,7 +1,9 @@
 <?PHP
-       // enrol_config.php - allows admin to edit all enrollment variables
+       // enrol_config.php - allows admin to edit all enrolment variables
        //                    Yes, enrol is correct English spelling.
 
+die('TODO: MDL-22787 mnet enrolments are not reimplemented yet, sorry.');
+
     require_once(dirname(__FILE__) . "/../../config.php");
     require_once($CFG->libdir.'/adminlib.php');
     include_once($CFG->dirroot.'/mnet/xmlrpc/client.php');
@@ -12,8 +14,7 @@
 
     admin_externalpage_setup('mnetenrol');
 
-    require_once("$CFG->dirroot/enrol/enrol.class.php");   /// Open the factory class
-    $enrolment = enrolment_factory::factory('mnet');
+    $enrolment = enrol_get_plugin('mnet');
 
     $mnethostid = required_param('host', PARAM_INT);
     $courseid = required_param('courseid', PARAM_INT);
@@ -278,7 +279,7 @@
 /// Print the page
 
 /// get language strings
-$str = get_strings(array('enrolmentplugins', 'configuration', 'users', 'administration'));
+$str = get_strings(array('configuration', 'users', 'administration'));
 /// Get some language strings
 
 $strpotentialusers = get_string('potentialusers', 'role');
diff --git a/admin/mnet/enr_courses.php b/admin/mnet/enr_courses.php
index cee043ede119..0ca015932bb8 100644
--- a/admin/mnet/enr_courses.php
+++ b/admin/mnet/enr_courses.php
@@ -1,7 +1,9 @@
 <?PHP
-       // enrol_config.php - allows admin to edit all enrollment variables
+       // enrol_config.php - allows admin to edit all enrolment variables
        //                    Yes, enrol is correct English spelling.
 
+die('TODO: ment enrolments are not reimplemented yet, sorry.');
+
     require_once(dirname(__FILE__) . "/../../config.php");
     require_once($CFG->libdir.'/adminlib.php');
     require_once($CFG->dirroot.'/mnet/lib.php');
@@ -13,8 +15,7 @@
 
     admin_externalpage_setup('mnetenrol');
 
-    require_once("$CFG->dirroot/enrol/enrol.class.php");   /// Open the factory class
-    $enrolment = enrolment_factory::factory('mnet');
+    $enrolment = enrol_get_plugin('mnet');
 
     $mnethost = required_param('host', PARAM_INT);
     $host = $DB->get_record('mnet_host', array('id'=>$mnethost));
diff --git a/admin/mnet/enr_hosts.php b/admin/mnet/enr_hosts.php
index ae964f5551e9..b97ddd779955 100644
--- a/admin/mnet/enr_hosts.php
+++ b/admin/mnet/enr_hosts.php
@@ -6,11 +6,11 @@
     require_once($CFG->libdir.'/adminlib.php');
     require_once($CFG->dirroot.'/mnet/lib.php');
 
-    admin_externalpage_setup('mnetenrol');
+die('TODO: MDL-22787 mnet enrolments are not reimplemented yet, sorry.');
 
-    require_once("$CFG->dirroot/enrol/enrol.class.php");   /// Open the factory class
+    admin_externalpage_setup('mnetenrol');
 
-    $enrolment = enrolment_factory::factory('mnet');
+    $enrolment = enrol_get_plugin('mnet');
 
 /// Otherwise fill and print the form.
 
diff --git a/admin/report/security/lang/en/report_security.php b/admin/report/security/lang/en/report_security.php
index e8c11903ef59..e80d85f50b55 100644
--- a/admin/report/security/lang/en/report_security.php
+++ b/admin/report/security/lang/en/report_security.php
@@ -35,24 +35,6 @@
 $string['check_cookiesecure_error'] = 'Please enable secure cookies';
 $string['check_cookiesecure_name'] = 'Secure cookies';
 $string['check_cookiesecure_ok'] = 'Secure cookies enabled.';
-$string['check_courserole_anything'] = 'The "doanything" capability must not be allowed in this <a href="{$a}">context</a>.';
-$string['check_courserole_details'] = '<p>Each course has one default enrolment role specified. Please make sure no risky capabilities are allowed for this role.</p>
-<p>The only supported legacy type for the default course role is <em>Student</em>.</p>';
-$string['check_courserole_error'] = 'Incorrectly defined default course roles detected!';
-$string['check_courserole_name'] = 'Default roles (courses)';
-$string['check_courserole_notyet'] = 'Used only default course role.';
-$string['check_courserole_ok'] = 'Default course role definitions is OK.';
-$string['check_courserole_risky'] = 'Risky capabilities detected in <a href="{$a}">context</a>.';
-$string['check_courserole_riskylegacy'] = 'Risky legacy type detected in <a href="{$a->url}">{$a->shortname}</a>.';
-$string['check_defaultcourserole_anything'] = 'The "doanything" capability must not be allowed in this <a href="{$a}">context</a>.';
-$string['check_defaultcourserole_details'] = '<p>The default student role for course enrolment specifies the default role for courses. Please make sure no risky capabilities are allowed in this role.</p>
-<p>The only supported legacy type for default role is <em>Student</em>.</p>';
-$string['check_defaultcourserole_error'] = 'Incorrectly defined default course role "{$a}" detected!';
-$string['check_defaultcourserole_legacy'] = 'Risky legacy type detected.';
-$string['check_defaultcourserole_name'] = 'Default course role (global)';
-$string['check_defaultcourserole_notset'] = 'Default role is not set.';
-$string['check_defaultcourserole_ok'] = 'Site default role definition is OK.';
-$string['check_defaultcourserole_risky'] = 'Risky capabilities detected in <a href="{$a}">context</a>.';
 $string['check_defaultuserrole_details'] = '<p>All logged in users are given capabilities of the default user role. Please make sure no risky capabilities are allowed in this role.</p>
 <p>The only supported legacy type for the default user role is <em>Authenticated user</em>. The course view capability must not be enabled.</p>';
 $string['check_defaultuserrole_error'] = 'The default user role "{$a}" is incorrectly defined!';
diff --git a/admin/report/security/lib.php b/admin/report/security/lib.php
index 2d59c764369f..b3053ffab8ff 100644
--- a/admin/report/security/lib.php
+++ b/admin/report/security/lib.php
@@ -58,8 +58,6 @@ function report_security_get_issue_list() {
         'report_security_check_defaultuserrole',
         'report_security_check_guestrole',
         'report_security_check_frontpagerole',
-        'report_security_check_defaultcourserole',
-        'report_security_check_courserole',
 
     );
 }
@@ -587,9 +585,6 @@ function report_security_check_defaultuserrole($detailed=false) {
 
     $riskycount = $DB->count_records_sql($sql, $params);
 
-    // default role can not have view cap in all courses - this would break moodle badly
-    $viewcap = $DB->record_exists('role_capabilities', array('roleid'=>$default_role->id, 'permission'=>CAP_ALLOW, 'capability'=>'moodle/course:participate'));
-
     // it may have either none or 'user' archetype - nothing else, or else it would break during upgrades badly
     if ($default_role->archetype === '' or $default_role->archetype === 'user') {
         $legacyok = true;
@@ -597,7 +592,7 @@ function report_security_check_defaultuserrole($detailed=false) {
         $legacyok = false;
     }
 
-    if ($riskycount or $viewcap or !$legacyok) {
+    if ($riskycount or !$legacyok) {
         $result->status  = REPORT_SECURITY_CRITICAL;
         $result->info    = get_string('check_defaultuserrole_error', 'report_security', format_string($default_role->name));
 
@@ -730,190 +725,6 @@ function report_security_check_frontpagerole($detailed=false) {
     return $result;
 }
 
-/**
- * Verifies sanity of site default course role.
- * @param bool $detailed
- * @return object result
- */
-function report_security_check_defaultcourserole($detailed=false) {
-    global $DB, $CFG;
-
-    $problems = array();
-
-    $result = new object();
-    $result->issue   = 'report_security_check_defaultcourserole';
-    $result->name    = get_string('check_defaultcourserole_name', 'report_security');
-    $result->info    = null;
-    $result->details = null;
-    $result->status  = null;
-    $result->link    = "<a href=\"$CFG->wwwroot/$CFG->admin/settings.php?section=userpolicies\">".get_string('userpolicies', 'admin').'</a>';;
-
-    if ($detailed) {
-        $result->details = get_string('check_defaultcourserole_details', 'report_security');
-    }
-
-    if (!$student_role = $DB->get_record('role', array('id'=>$CFG->defaultcourseroleid))) {
-        $result->status  = REPORT_SECURITY_WARNING;
-        $result->info    = get_string('check_defaultcourserole_notset', 'report_security');
-        $result->details = get_string('check_defaultcourserole_details', 'report_security');
-
-        return $result;
-    }
-
-    // risky caps - usually very dangerous
-    $params = array('capallow'=>CAP_ALLOW, 'roleid'=>$student_role->id);
-    $sql = "SELECT DISTINCT rc.contextid
-              FROM {role_capabilities} rc
-              JOIN {capabilities} cap ON cap.name = rc.capability
-             WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
-                   AND rc.permission = :capallow
-                   AND rc.roleid = :roleid";
-
-    if ($riskycontexts = $DB->get_records_sql($sql, $params)) {
-        foreach($riskycontexts as $contextid=>$unused) {
-            if ($contextid == SYSCONTEXTID) {
-                $a = "$CFG->wwwroot/$CFG->admin/roles/define.php?action=view&amp;roleid=$CFG->defaultcourseroleid";
-            } else {
-                $a = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$contextid&amp;roleid=$CFG->defaultcourseroleid";
-            }
-            $problems[] = get_string('check_defaultcourserole_risky', 'report_security', $a);
-        }
-    }
-
-    // course creator or administrator does not make any sense here
-    if ($student_role->archetype === 'coursecreator' or $student_role->archetype === 'manager') {
-        $problems[] = get_string('check_defaultcourserole_legacy', 'report_security');
-    }
-
-    if ($problems) {
-        $result->status  = REPORT_SECURITY_CRITICAL;
-        $result->info    = get_string('check_defaultcourserole_error', 'report_security', format_string($student_role->name));
-        if ($detailed) {
-            $result->details .= "<ul>";
-            foreach ($problems as $problem) {
-                $result->details .= "<li>$problem</li>";
-            }
-            $result->details .= "</ul>";
-        }
-
-    } else {
-        $result->status  = REPORT_SECURITY_OK;
-        $result->info    = get_string('check_defaultcourserole_ok', 'report_security');
-    }
-
-    return $result;
-}
-
-/**
- * Verifies sanity of default roles in courses.
- * @param bool $detailed
- * @return object result
- */
-function report_security_check_courserole($detailed=false) {
-    global $DB, $CFG, $SITE;
-
-    $problems = array();
-
-    $result = new object();
-    $result->issue   = 'report_security_check_courserole';
-    $result->name    = get_string('check_courserole_name', 'report_security');
-    $result->info    = null;
-    $result->details = null;
-    $result->status  = null;
-    $result->link    = null;
-
-    if ($detailed) {
-        $result->details = get_string('check_courserole_details', 'report_security');
-    }
-
-    // get list of all student roles selected in courses excluding the default course role
-    $params = array('siteid'=>$SITE->id, 'defaultcourserole'=>$CFG->defaultcourseroleid);
-    $sql = "SELECT r.*
-              FROM {role} r
-              JOIN {course} c ON c.defaultrole = r.id
-             WHERE c.id <> :siteid AND r.id <> :defaultcourserole";
-
-    if (!$student_roles = $DB->get_records_sql($sql, $params)) {
-        $result->status  = REPORT_SECURITY_OK;
-        $result->info    = get_string('check_courserole_notyet', 'report_security');
-        $result->details = get_string('check_courserole_details', 'report_security');
-
-        return $result;
-    }
-
-    $roleids = array_keys($student_roles);
-
-    $sql = "SELECT DISTINCT rc.roleid
-              FROM {role_capabilities} rc
-              JOIN {role} r ON r.id = rc.roleid
-             WHERE (r.archetype = :coursecreator OR r.archetype = :teacher OR r.archetype = :editingteacher OR r.archetype = :manager)";
-    $params = array('coursecreator'  => 'coursecreator',
-                    'teacher'        => 'teacher',
-                    'editingteacher' => 'editingteacher',
-                    'manager'        => 'manager');
-
-    $riskyroleids = $DB->get_records_sql($sql, $params);
-    $riskyroleids = array_keys($riskyroleids);
-
-    // any XSS legacy cap does not make any sense here!
-    list($inroles, $params) = $DB->get_in_or_equal($roleids, SQL_PARAMS_NAMED, 'r0', true);
-    $sql = "SELECT DISTINCT c.id, c.shortname
-              FROM {course} c
-             WHERE c.defaultrole $inroles
-          ORDER BY c.sortorder";
-    if ($courses = $DB->get_records_sql($sql, $params)) {
-        foreach ($courses as $course) {
-            $a = (object)array('url'=>"$CFG->wwwroot/course/edit.php?id=$course->id", 'shortname'=>$course->shortname);
-            $problems[] = get_string('check_courserole_riskylegacy', 'report_security', $a);
-        }
-    }
-
-    // risky caps in any level - usually very dangerous!!
-    if ($checkroles = array_diff($roleids, $riskyroleids)) {
-        list($inroles, $params) = $DB->get_in_or_equal($checkroles, SQL_PARAMS_NAMED, 'r0', true);
-        $params = array_merge($params, array('capallow'=>CAP_ALLOW));
-        $sql = "SELECT rc.roleid, rc.contextid
-                  FROM {role_capabilities} rc
-                  JOIN {capabilities} cap ON cap.name = rc.capability
-                 WHERE ".$DB->sql_bitand('cap.riskbitmask', (RISK_XSS | RISK_CONFIG | RISK_DATALOSS))." <> 0
-                       AND rc.permission = :capallow
-                       AND rc.roleid $inroles
-              GROUP BY rc.roleid, rc.contextid
-              ORDER BY rc.roleid, rc.contextid";
-        $rs = $DB->get_recordset_sql($sql, $params);
-        foreach($rs as $res) {
-            $roleid    = $res->roleid;
-            $contextid = $res->contextid;
-            if ($contextid == SYSCONTEXTID) {
-                $a = "$CFG->wwwroot/$CFG->admin/roles/define.php?action=view&amp;roleid=$roleid";
-            } else {
-                $a = "$CFG->wwwroot/$CFG->admin/roles/override.php?contextid=$contextid&amp;roleid=$roleid";
-            }
-            $problems[] = get_string('check_courserole_risky', 'report_security', $a);
-        }
-        $rs->close();
-    }
-
-
-    if ($problems) {
-        $result->status  = REPORT_SECURITY_CRITICAL;
-        $result->info    = get_string('check_courserole_error', 'report_security');
-        if ($detailed) {
-            $result->details .= "<ul>";
-            foreach ($problems as $problem) {
-                $result->details .= "<li>$problem</li>";
-            }
-            $result->details .= "</ul>";
-        }
-
-    } else {
-        $result->status  = REPORT_SECURITY_OK;
-        $result->info    = get_string('check_courserole_ok', 'report_security');
-    }
-
-    return $result;
-}
-
 /**
  * Lists all admins.
  * @param bool $detailed
diff --git a/admin/roles/assign.php b/admin/roles/assign.php
index a03840b1bb56..6b17a6022771 100755
--- a/admin/roles/assign.php
+++ b/admin/roles/assign.php
@@ -31,8 +31,6 @@
 
 $contextid      = required_param('contextid',PARAM_INT);
 $roleid         = optional_param('roleid', 0, PARAM_INT);
-$extendperiod   = optional_param('extendperiod', 0, PARAM_INT);
-$extendbase     = optional_param('extendbase', 3, PARAM_INT);
 
 list($context, $course, $cm) = get_context_info_array($contextid);
 
@@ -58,7 +56,6 @@
 
 $contextname = print_context_name($context);
 $courseid = $course->id;
-$inmeta = $course->metacourse;
 $isfrontpage = ($course->id == SITEID);
 
 // These are needed early because of tabs.php
@@ -87,40 +84,6 @@
     }
 }
 
-// Build the list of options for the enrolment period dropdown.
-$unlimitedperiod = get_string('unlimited');
-for ($i=1; $i<=365; $i++) {
-    $seconds = $i * 86400;
-    $periodmenu[$seconds] = get_string('numdays', '', $i);
-}
-// Work out the apropriate default setting.
-if ($extendperiod) {
-    $defaultperiod = $extendperiod;
-} else {
-    $defaultperiod = $course->enrolperiod;
-}
-
-// Build the list of options for the starting from dropdown.
-$timeformat = get_string('strftimedatefullshort');
-$today = time();
-$today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
-
-// MDL-12420, preventing course start date showing up as an option at system context and front page roles.
-if ($course->startdate > 0) {
-    $basemenu[2] = get_string('coursestart') . ' (' . userdate($course->startdate, $timeformat) . ')';
-}
-if ($course->enrollable != 2 || ($course->enrolstartdate == 0 || $course->enrolstartdate <= $today) && ($course->enrolenddate == 0 || $course->enrolenddate > $today)) {
-    $basemenu[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
-}
-if ($course->enrollable == 2) {
-    if($course->enrolstartdate > 0) {
-        $basemenu[4] = get_string('courseenrolstart') . ' (' . userdate($course->enrolstartdate, $timeformat) . ')';
-    }
-    if($course->enrolenddate > 0) {
-        $basemenu[5] = get_string('courseenrolend') . ' (' . userdate($course->enrolenddate, $timeformat) . ')';
-    }
-}
-
 // Process any incoming role assignments before printing the header.
 if ($roleid) {
 
@@ -138,46 +101,9 @@
 
             foreach ($userstoassign as $adduser) {
                 $allow = true;
-                if ($inmeta) {
-                    if (has_capability('moodle/course:managemetacourse', $context, $adduser->id)) {
-                        //ok
-                    } else {
-                        $managerroles = get_roles_with_capability('moodle/course:managemetacourse', CAP_ALLOW, $context);
-                        if (!empty($managerroles) and !array_key_exists($roleid, $managerroles)) {
-                            $erruser = $DB->get_record('user', array('id'=>$adduser->id), 'id, firstname, lastname');
-                            $errors[] = get_string('metaassignerror', 'role', fullname($erruser));
-                            $allow = false;
-                        }
-                    }
-                }
 
                 if ($allow) {
-                    switch($extendbase) {
-                        case 2:
-                            $timestart = $course->startdate;
-                            break;
-                        case 3:
-                            $timestart = $today;
-                            break;
-                        case 4:
-                            $timestart = $course->enrolstartdate;
-                            break;
-                        case 5:
-                            $timestart = $course->enrolenddate;
-                            break;
-                    }
-
-                    if($extendperiod > 0) {
-                        $timeend = $timestart + $extendperiod;
-                    } else {
-                        $timeend = 0;
-                    }
-                    if (! role_assign($roleid, $adduser->id, 0, $context->id, $timestart, $timeend)) {
-                        $a = new stdClass;
-                        $a->role = $assignableroles[$roleid];
-                        $a->user = fullname($adduser);
-                        $errors[] = get_string('assignerror', 'role', $a);
-                    }
+                    role_assign($roleid, $adduser->id, $context->id);
                 }
             }
 
@@ -197,18 +123,8 @@
         if (!empty($userstounassign)) {
 
             foreach ($userstounassign as $removeuser) {
-                if (! role_unassign($roleid, $removeuser->id, 0, $context->id)) {
-                    $a = new stdClass;
-                    $a->role = $assignableroles[$roleid];
-                    $a->user = fullname($removeuser);
-                    $errors[] = get_string('unassignerror', 'role', $a);
-                } else if ($inmeta) {
-                    sync_metacourse($courseid);
-                    $newroles = get_user_roles($context, $removeuser->id, false);
-                    if (empty($newroles) || array_key_exists($roleid, $newroles)) {
-                        $errors[] = get_string('metaunassignerror', 'role', fullname($removeuser));
-                    }
-                }
+                //unassign only roles that are added manually, no messing with other components!!!
+                role_unassign($roleid, $removeuser->id, $context->id, '');
             }
 
             $potentialuserselector->invalidate_selected_users();
@@ -224,14 +140,12 @@
 
 $PAGE->set_pagelayout('admin');
 $PAGE->set_title($title);
-$tabfile = $CFG->dirroot.'/'.$CFG->admin.'/roles/tabs.php';
 
 switch ($context->contextlevel) {
     case CONTEXT_SYSTEM:
         admin_externalpage_setup('assignroles', '', array('contextid' => $contextid, 'roleid' => $roleid));
         break;
     case CONTEXT_USER:
-        $tabfile = null;
         if ($isfrontpage) {
             $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
             $PAGE->set_heading($fullname);
@@ -260,10 +174,6 @@
 }
 
 echo $OUTPUT->header();
-if ($tabfile) {
-    $currenttab = 'assign';
-    include($tabfile);
-}
 
 // Print heading.
 echo $OUTPUT->heading_with_help($title, 'assignroles', 'role');
@@ -290,16 +200,6 @@
       <td id="buttonscell">
           <div id="addcontrols">
               <input name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow().'&nbsp;'.get_string('add'); ?>" title="<?php print_string('add'); ?>" /><br />
-
-              <?php print_collapsible_region_start('', 'assignoptions', get_string('enrolmentoptions', 'role'),
-                    'assignoptionscollapse', true); ?>
-
-              <p><label for="extendperiod"><?php print_string('enrolperiod') ?></label><br />
-              <?php echo html_writer::select($periodmenu, 'extendperiod', $defaultperiod, $unlimitedperiod); ?></p>
-
-              <p><label for="extendbase"><?php print_string('startingfrom') ?></label><br />
-              <?php echo html_writer::select($basemenu, 'extendbase', $extendbase, false); ?></p>
-              <?php print_collapsible_region_end(); ?>
           </div>
 
           <div id="removecontrols">
@@ -352,11 +252,6 @@
     // Print instruction
     echo $OUTPUT->heading(get_string('chooseroletoassign', 'role'), 3);
 
-    // sync metacourse enrolments if needed
-    if ($inmeta) {
-        sync_metacourse($course);
-    }
-
     // Get the names of role holders for roles with between 1 and MAX_USERS_TO_LIST_PER_ROLE users,
     // and so determine whether to show the extra column.
     $roleholdernames = array();
diff --git a/admin/roles/check.php b/admin/roles/check.php
index cad9fe512d98..27ebd970c21d 100755
--- a/admin/roles/check.php
+++ b/admin/roles/check.php
@@ -73,14 +73,12 @@
 
 $PAGE->set_pagelayout('admin');
 $PAGE->set_title($title);
-$tabfile = $CFG->dirroot.'/'.$CFG->admin.'/roles/tabs.php';
 
 switch ($context->contextlevel) {
     case CONTEXT_SYSTEM:
-        admin_externalpage_setup('assignroles', '', array('contextid' => $contextid, 'roleid' => $roleid));
+        admin_externalpage_setup('assignroles', '', array('contextid' => $contextid));
         break;
     case CONTEXT_USER:
-        $tabfile = null;
         if ($isfrontpage) {
             $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
             $PAGE->set_heading($fullname);
@@ -112,10 +110,6 @@
 // These are needed early because of tabs.php
 $assignableroles = get_assignable_roles($context, ROLENAME_BOTH);
 $overridableroles = get_overridable_roles($context, ROLENAME_BOTH);
-if ($tabfile) {
-    $currenttab = 'check';
-    include($tabfile);
-}
 
 // Print heading.
 echo $OUTPUT->heading($title);
diff --git a/admin/roles/lib.php b/admin/roles/lib.php
index 203abc8b3525..33493acdd511 100644
--- a/admin/roles/lib.php
+++ b/admin/roles/lib.php
@@ -1020,56 +1020,27 @@ protected function get_options() {
  * when we are assigning in a context below the course level. (CONTEXT_MODULE and
  * some CONTEXT_BLOCK).
  *
- * In this case we replicate part of get_users_by_capability() get the users
- * with moodle/course:participate. We can't use
- * get_users_by_capability() becuase
- *   1) get_users_by_capability() does not deal with searching by name
- *   2) exceptions array can be potentially large for large courses
+ * This returns only enrolled users in this context.
  */
 class potential_assignees_below_course extends role_assign_user_selector_base {
     public function find_users($search) {
         global $DB;
 
-        // Get roles with some assignement to the 'moodle/course:participate' capability.
-        $possibleroles = get_roles_with_capability('moodle/course:participate', CAP_ALLOW, $this->context);
-        if (empty($possibleroles)) {
-            // If there aren't any, we are done.
-            return array();
-        }
-
-        // Now exclude the admin roles, and check the actual permission on
-        // 'moodle/course:participate' to make sure it is allow.
-        $validroleids = array();
-
-        foreach ($possibleroles as $possiblerole) {
-            if ($caps = role_context_capabilities($possiblerole->id, $this->context, 'moodle/course:participate')) { // resolved list
-                if (isset($caps['moodle/course:participate']) && $caps['moodle/course:participate'] > 0) { // resolved capability > 0
-                    $validroleids[] = $possiblerole->id;
-                }
-            }
-        }
-
-        // If there are no valid roles, we are done.
-        if (!$validroleids) {
-            return array();
-        }
+        list($enrolsql, $eparams) = get_enrolled_sql($this->context);
 
         // Now we have to go to the database.
         list($wherecondition, $params) = $this->search_sql($search, 'u');
+        $params = array_merge($params, $eparams);
+
         if ($wherecondition) {
             $wherecondition = ' AND ' . $wherecondition;
         }
-        $roleids =  '('.implode(',', $validroleids).')';
 
-        $fields      = 'SELECT DISTINCT ' . $this->required_fields_sql('u');
-        $countfields = 'SELECT COUNT(DISTINCT u.id)';
+        $fields      = 'SELECT ' . $this->required_fields_sql('u');
+        $countfields = 'SELECT COUNT(u.id)';
 
         $sql   = " FROM {user} u
-                   JOIN {role_assignments} ra ON ra.userid = u.id
-                   JOIN {role} r ON r.id = ra.roleid
-                  WHERE ra.contextid " . get_related_contexts_string($this->context)."
-                        $wherecondition
-                        AND ra.roleid IN $roleids
+                  WHERE u.id IN ($enrolsql) $wherecondition
                         AND u.id NOT IN (
                            SELECT u.id
                              FROM {role_assignments} r, {user} u
@@ -1174,7 +1145,7 @@ public function find_users($search) {
         $params = array_merge($params, $ctxparams);
         $params['roleid'] = $this->roleid;
 
-        $sql = "SELECT ra.id as raid," . $this->required_fields_sql('u') . ",ra.contextid
+        $sql = "SELECT ra.id as raid," . $this->required_fields_sql('u') . ",ra.contextid,ra.component
                 FROM {role_assignments} ra
                 JOIN {user} u ON u.id = ra.userid
                 JOIN {context} ctx ON ra.contextid = ctx.id
@@ -1182,7 +1153,7 @@ public function find_users($search) {
                     $wherecondition AND
                     ctx.id $ctxcondition AND
                     ra.roleid = :roleid
-                ORDER BY ctx.depth DESC, u.lastname, u.firstname";
+                ORDER BY ctx.depth DESC, ra.component, u.lastname, u.firstname";
         $contextusers = $DB->get_records_sql($sql, $params);
 
         // No users at all.
@@ -1196,6 +1167,7 @@ public function find_users($search) {
         $dummyuser = new stdClass;
         $dummyuser->contextid = 0;
         $dummyuser->id = 0;
+        $dummyuser->component = '';
         $contextusers[] = $dummyuser;
         $results = array(); // The results array we are building up.
         $doneusers = array(); // Ensures we only list each user at most once.
@@ -1223,6 +1195,11 @@ public function find_users($search) {
             if ($currentcontextid != $this->context->id) {
                 $user->disabled = true;
             }
+            if ($user->component !== '') {
+                // bad luck, you can tweak only manual role assignments
+                $user->disabled = true;
+            }
+            unset($user->component);
             $currentgroup[$user->id] = $user;
         }
 
@@ -1465,8 +1442,9 @@ public function __construct() {
     }
 
     protected function load_required_roles() {
+        global $DB;
         parent::load_required_roles();
-        $this->allowedtargetroles = get_allowed_switchable_roles();
+        $this->allowedtargetroles = $DB->get_records_menu('roles', NULL, 'id', 'roleid, 1');
     }
 
     protected function set_allow($fromroleid, $targetroleid) {
diff --git a/admin/roles/manage.php b/admin/roles/manage.php
index c0a946bc9101..f9b093553754 100755
--- a/admin/roles/manage.php
+++ b/admin/roles/manage.php
@@ -60,7 +60,6 @@
     $undeletableroles[$CFG->notloggedinroleid] = 1;
     $undeletableroles[$CFG->guestroleid] = 1;
     $undeletableroles[$CFG->defaultuserroleid] = 1;
-    $undeletableroles[$CFG->defaultcourseroleid] = 1;
 
 ///.Process submitted data.
     $confirmed = optional_param('confirm', false, PARAM_BOOL) && data_submitted() && confirm_sesskey();
diff --git a/admin/roles/override.php b/admin/roles/override.php
index 2d21254a9148..29737d5226a2 100755
--- a/admin/roles/override.php
+++ b/admin/roles/override.php
@@ -65,7 +65,7 @@
 
 $role = $DB->get_record('role', array('id'=>$roleid), '*', MUST_EXIST);
 
-// These are needed early 
+// These are needed early
 $assignableroles  = get_assignable_roles($context, ROLENAME_BOTH);
 list($overridableroles, $overridecounts, $nameswithcounts) = get_overridable_roles($context, ROLENAME_BOTH, true);
 
@@ -75,7 +75,6 @@
 $a = (object)array('context' => $contextname, 'role' => $overridableroles[$roleid]);
 $title = get_string('overridepermissionsforrole', 'role', $a);
 
-$tabfile = $CFG->dirroot.'/'.$CFG->admin.'/roles/tabs.php';
 $currenttab = 'permissions';
 
 $PAGE->set_pagelayout('admin');
@@ -86,7 +85,6 @@
         print_error('cannotoverridebaserole', 'error');
         break;
     case CONTEXT_USER:
-        $tabfile = null;
         if ($isfrontpage) {
             $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
             $PAGE->set_heading($fullname);
@@ -135,9 +133,6 @@
 
 // Finally start page output
 echo $OUTPUT->header();
-if ($tabfile) {
-    include($tabfile);
-}
 echo $OUTPUT->heading_with_help($title, 'overridepermissions', 'role');
 
 // Show UI for overriding roles.
@@ -152,7 +147,7 @@
     echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'sesskey', 'value'=>sesskey()));
     echo html_writer::empty_tag('input', array('type'=>'hidden', 'name'=>'roleid', 'value'=>$roleid));
     echo html_writer::tag('p', get_string('highlightedcellsshowinherit', 'role'), array('class'=>'overridenotice'));
-    
+
     $overridestable->display();
     if ($overridestable->has_locked_capabiltites()) {
         echo '<p class="overridenotice">' . get_string('safeoverridenotice', 'role') . "</p>\n";
diff --git a/admin/roles/permissions.php b/admin/roles/permissions.php
index cbd2ee6c0ff2..bfdd13759439 100644
--- a/admin/roles/permissions.php
+++ b/admin/roles/permissions.php
@@ -75,7 +75,6 @@
 $contextname = print_context_name($context);
 $title = get_string('permissionsincontext', 'role', $contextname);
 $straction = get_string('permissions', 'role'); // Used by tabs.php
-$tabfile = $CFG->dirroot.'/'.$CFG->admin.'/roles/tabs.php';
 $currenttab = 'permissions';
 
 $PAGE->set_pagelayout('admin');
@@ -85,7 +84,6 @@
         print_error('cannotoverridebaserole', 'error');
         break;
     case CONTEXT_USER:
-        $tabfile = null;
         if ($isfrontpage) {
             $fullname = fullname($user, has_capability('moodle/site:viewfullnames', $context));
             $PAGE->set_heading($fullname);
@@ -143,9 +141,6 @@
         }
         // Display and print
         echo $OUTPUT->header();
-        if ($tabfile) {
-            include($tabfile);
-        }
         echo $OUTPUT->heading($title);
         echo $OUTPUT->confirm($message, $continueurl, $PAGE->url);
         echo $OUTPUT->footer();
@@ -184,9 +179,6 @@
             }
         }
         echo $OUTPUT->header();
-        if ($tabfile) {
-            include($tabfile);
-        }
         echo $OUTPUT->heading($title);
         echo $OUTPUT->box($message);
         $mform->display();
@@ -196,9 +188,6 @@
 }
 
 echo $OUTPUT->header();
-if ($tabfile) {
-    include($tabfile);
-}
 echo $OUTPUT->heading($title);
 
 $table = new permissions_table($context, $contextname, $allowoverrides, $allowsafeoverrides, $overridableroles);
diff --git a/admin/roles/tabs.php b/admin/roles/tabs.php
deleted file mode 100755
index 6c69a06efb99..000000000000
--- a/admin/roles/tabs.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Handles headers and tabs for the roles control at any level apart from SYSTEM level
- * We assume that $currenttab, $assignableroles and $overridableroles are defined
- *
- * @package    moodlecore
- * @subpackage role
- * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.'); // It must be included from a Moodle page
-}
-
-if (!isset($availablefilters)) {
-    $availablefilters  = array();
-    if (in_array($context->contextlevel, array(CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_MODULE)) &&
-            !($context->contextlevel == CONTEXT_COURSE && $context->instanceid == SITEID) &&
-            has_capability('moodle/filter:manage', $context)) {
-        $availablefilters = filter_get_available_in_context($context);
-    }
-}
-
-$toprow = array();
-$inactive = array();
-$activetwo = array();
-$secondrow = array();
-
-$permissionsrow = array();
-
-if ($context->contextlevel != CONTEXT_SYSTEM) {    // Print tabs for anything except SYSTEM context
-
-    if ($context->contextlevel == CONTEXT_MODULE) {  // Only show update button if module
-        $url = new moodle_url('/course/mod.php', array('update'=>$context->instanceid, 'return'=>'true', 'sesskey'=>sesskey()));
-        $toprow[] = new tabobject('update', $url, get_string('settings'));
-    }
-
-    if (!empty($assignableroles) || $currenttab=='assign') {
-        $url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$context->id));
-        $toprow[] = new tabobject('assign', $url, get_string('localroles', 'role'), '', true);
-    }
-
-    if (has_capability('moodle/role:review', $context) or !empty($overridableroles)) {
-        $url = new moodle_url('/admin/roles/permissions.php', array('contextid'=>$context->id));
-        $permissionsrow['permissions'] = new tabobject('permissions', $url, get_string('permissions', 'role'), '', true);
-    }
-
-    if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride', 'moodle/role:override', 'moodle/role:assign'), $context)) {
-        $url = new moodle_url('/admin/roles/check.php', array('contextid'=>$context->id));
-        $permissionsrow['check'] = new tabobject('check', $url, get_string('checkpermissions', 'role'));
-    }
-
-    if ($permissionsrow) {
-        $firstpermissionrow = reset($permissionsrow);
-        $toprow[] = new tabobject('toppermissions', $firstpermissionrow->link, get_string('permissions', 'role'), '', true);
-        if (!empty($permissionsrow[$currenttab])) {
-            $secondrow = array_values($permissionsrow);
-            $inactive  = array('toppermissions');
-            $activetwo = array('toppermissions');
-        }
-    }
-
-    if (!empty($availablefilters)) {
-        $url = new moodle_url('/filter/manage.php', array('contextid'=>$context->id));
-        $toprow[] = new tabobject('filters', $url, get_string('filters', 'admin'));
-    }
-}
-unset($permissionsrow);
-
-/// Here other core tabs should go (always calling tabs.php files)
-/// All the logic to decide what to show must be self-contained in the tabs file
-/// eg:
-/// include_once($CFG->dirroot . '/grades/tabs.php');
-
-/// Finally, we support adding some 'on-the-fly' tabs here
-/// All the logic to decide what to show must be self-cointained in the tabs file
-if (!empty($CFG->extratabs)) {
-    if ($extratabs = explode(',', $CFG->extratabs)) {
-        asort($extratabs);
-        foreach($extratabs as $extratab) {
-        /// Each extra tab must be one $CFG->dirroot relative file
-            if (file_exists($CFG->dirroot . '/' . $extratab)) {
-                include($CFG->dirroot . '/' . $extratab);
-            }
-        }
-    }
-}
-
-$inactive[] = $currenttab;
-
-$tabs = array($toprow);
-
-/// If there are any secondrow defined, let's introduce it
-if (!empty($secondrow)) {
-    $tabs[] = $secondrow;
-}
-
-print_tabs($tabs, $currenttab, $inactive, $activetwo);
-
diff --git a/admin/roles/usersroles.php b/admin/roles/usersroles.php
index a25d6ddbde70..7412c89f14fa 100644
--- a/admin/roles/usersroles.php
+++ b/admin/roles/usersroles.php
@@ -55,7 +55,7 @@
 
 /// Now get the role assignments for this user.
 $sql = "SELECT
-        ra.id, ra.userid, ra.contextid, ra.roleid, ra.enrol,
+        ra.id, ra.userid, ra.contextid, ra.roleid, ra.component, ra.itemid
         c.path,
         r.name AS rolename,
         COALESCE(rn.name, r.name) AS localname
diff --git a/admin/settings/appearance.php b/admin/settings/appearance.php
index 464784232d99..65106dc0f563 100644
--- a/admin/settings/appearance.php
+++ b/admin/settings/appearance.php
@@ -135,9 +135,9 @@
     $temp = new admin_externalpage('profilepage', get_string('myprofile', 'admin'), $CFG->wwwroot . '/user/profilesys.php');
     $ADMIN->add('appearance', $temp);
 
-    // coursemanager is the person responsible for course - usually manages enrolments, receives notification, etc.
-    $temp = new admin_settingpage('coursemanager', get_string('coursemanager', 'admin'));
-    $temp->add(new admin_setting_special_coursemanager());
+    // coursecontact is the person responsible for course - usually manages enrolments, receives notification, etc.
+    $temp = new admin_settingpage('coursecontact', get_string('coursecontact', 'admin'));
+    $temp->add(new admin_setting_special_coursecontact());
     $ADMIN->add('appearance', $temp);
 
     $temp = new admin_settingpage('ajax', get_string('ajaxuse'));
diff --git a/admin/settings/courses.php b/admin/settings/courses.php
index e1e7504dbd8b..291941395478 100644
--- a/admin/settings/courses.php
+++ b/admin/settings/courses.php
@@ -11,8 +11,6 @@
     $ADMIN->add('courses', new admin_externalpage('coursemgmt', get_string('coursemgmt', 'admin'), $CFG->wwwroot . '/course/index.php?categoryedit=on',
             array('moodle/category:manage', 'moodle/course:create')));
 
-    $ADMIN->add('courses', new admin_enrolment_page());
-
 /// Course Default Settings Page
 /// NOTE: these settings must be applied after all other settings because they depend on them
     ///main course settings
@@ -41,45 +39,6 @@
         $choices = get_max_upload_sizes();
     }
     $temp->add(new admin_setting_configselect('moodlecourse/maxbytes', get_string('maximumupload'), get_string('coursehelpmaximumupload'), key($choices), $choices));
-    $temp->add(new admin_setting_configselect('moodlecourse/metacourse', get_string('metacourse'), get_string('coursehelpmetacourse'), 0,array(0 => get_string('no'), 1 => get_string('yes'))));
-
-    ///enrolement course settings
-    $temp->add(new admin_setting_heading('enrolhdr', get_string('enrolments'), ''));
-    require_once($CFG->dirroot.'/enrol/enrol.class.php');
-    $choices = array();
-    $modules = explode(',', $CFG->enrol_plugins_enabled);
-    foreach ($modules as $module) {
-        $name = get_string('enrolname', "enrol_$module");
-        $plugin = enrolment_factory::factory($module);
-        if (method_exists($plugin, 'print_entry')) {
-            $choices[$name] = $module;
-        }
-    }
-    asort($choices);
-    $choices = array_flip($choices);
-    $choices = array_merge(array('' => get_string('sitedefault').' ('.get_string('enrolname', "enrol_$CFG->enrol").')'), $choices);
-    $temp->add(new admin_setting_configselect('moodlecourse/enrol', get_string('enrolmentplugins'), get_string('coursehelpenrolmentplugins'), key($choices),$choices));
-    $choices = array(0 => get_string('no'), 1 => get_string('yes'), 2 => get_string('enroldate'));
-    $temp->add(new admin_setting_configselect('moodlecourse/enrollable', get_string('enrollable'), get_string('coursehelpenrollable'), 1,$choices));
-    $periodmenu=array();
-    $periodmenu[0] = get_string('unlimited');
-    for ($i=1; $i<=365; $i++) {
-        $seconds = $i * 86400;
-        $periodmenu[$seconds] = get_string('numdays', '', $i);
-    }
-    $temp->add(new admin_setting_configselect('moodlecourse/enrolperiod', get_string('enrolperiod'), '', 0,$periodmenu));
-
-    ///
-    $temp->add(new admin_setting_heading('expirynotifyhdr', get_string('expirynotify'), ''));
-    $temp->add(new admin_setting_configselect('moodlecourse/expirynotify', get_string('notify'), get_string('coursehelpnotify'), 0,array(0 => get_string('no'), 1 => get_string('yes'))));
-    $temp->add(new admin_setting_configselect('moodlecourse/notifystudents', get_string('expirynotifystudents'), get_string('coursehelpexpirynotifystudents'), 0,array(0 => get_string('no'), 1 => get_string('yes'))));
-    $thresholdmenu=array();
-    for ($i=1; $i<=30; $i++) {
-        $seconds = $i * 86400;
-        $thresholdmenu[$seconds] = get_string('numdays', '', $i);
-    }
-    $temp->add(new admin_setting_configselect('moodlecourse/expirythreshold', get_string('expirythreshold'), get_string('coursehelpexpirythreshold'), 10 * 86400,$thresholdmenu));
-
 
     $temp->add(new admin_setting_heading('groups', get_string('groups', 'group'), ''));
     $choices = array();
@@ -95,12 +54,6 @@
     $choices['0'] = get_string('courseavailablenot');
     $choices['1'] = get_string('courseavailable');
     $temp->add(new admin_setting_configselect('moodlecourse/visible', get_string('visible'), '', 1,$choices));
-    $temp->add(new admin_setting_configpasswordunmask('moodlecourse/enrolpassword', get_string('enrolmentkey'), get_string('coursehelpenrolmentkey'),''));
-    $choices = array();
-    $choices['0'] = get_string('guestsno');
-    $choices['1'] = get_string('guestsyes');
-    $choices['2'] = get_string('guestskey');
-    $temp->add(new admin_setting_configselect('moodlecourse/guest', get_string('opentoguests'), '', 0,$choices));
 
 
     $temp->add(new admin_setting_heading('language', get_string('language'), ''));
@@ -151,7 +104,6 @@
     $temp = new admin_settingpage('scheduled', get_string('scheduledsettings','backup'), 'moodle/backup:backupcourse');
     $temp->add(new admin_setting_configcheckbox('backup/backup_sche_modules', get_string('includemodules'), get_string('backupincludemoduleshelp'), 0));
     $temp->add(new admin_setting_configcheckbox('backup/backup_sche_withuserdata', get_string('includemoduleuserdata'), get_string('backupincludemoduleuserdatahelp'), 0));
-    $temp->add(new admin_setting_configcheckbox('backup/backup_sche_metacourse', get_string('metacourse'), get_string('backupmetacoursehelp'), 0));
     $temp->add(new admin_setting_configselect('backup/backup_sche_users', get_string('users'), get_string('backupusershelp'),
             0, array(0 => get_string('all'), 1 => get_string('course'))));
     $temp->add(new admin_setting_configcheckbox('backup/backup_sche_logs', get_string('logs'), get_string('backuplogshelp'), 0));
diff --git a/admin/settings/frontpage.php b/admin/settings/frontpage.php
index 24a537689fe2..052611b24b47 100644
--- a/admin/settings/frontpage.php
+++ b/admin/settings/frontpage.php
@@ -50,7 +50,6 @@
         $temp->add(new admin_setting_configtext('commentsperpage', get_string('commentsperpage', 'admin'), '', 15, PARAM_INT));
 
         $temp->add(new admin_setting_configtext('coursesperpage', get_string('coursesperpage', 'admin'), get_string('configcoursesperpage', 'admin'), 20, PARAM_INT));
-        $temp->add(new admin_setting_configcheckbox('allowvisiblecoursesinhiddencategories', get_string('allowvisiblecoursesinhiddencategories', 'admin'), get_string('configvisiblecourses', 'admin'), 0));
 
         // front page default role
         $roleoptions = array(0=>get_string('none')); // roles to choose from
diff --git a/admin/settings/plugins.php b/admin/settings/plugins.php
index cf77a1cf0f0d..6168fff65cea 100644
--- a/admin/settings/plugins.php
+++ b/admin/settings/plugins.php
@@ -50,6 +50,84 @@
         }
     }
 
+    // authentication plugins
+    $ADMIN->add('modules', new admin_category('authsettings', get_string('authentication', 'admin')));
+
+    $temp = new admin_settingpage('manageauths', get_string('authsettings', 'admin'));
+    $temp->add(new admin_setting_manageauths());
+    $temp->add(new admin_setting_heading('manageauthscommonheading', get_string('commonsettings', 'admin'), ''));
+    $temp->add(new admin_setting_special_registerauth());
+    $temp->add(new admin_setting_configselect('guestloginbutton', get_string('guestloginbutton', 'auth'),
+                                              get_string('showguestlogin', 'auth'), '1', array('0'=>get_string('hide'), '1'=>get_string('show'))));
+    $temp->add(new admin_setting_configtext('alternateloginurl', get_string('alternateloginurl', 'auth'),
+                                            get_string('alternatelogin', 'auth', htmlspecialchars(get_login_url())), ''));
+    $temp->add(new admin_setting_configtext('forgottenpasswordurl', get_string('forgottenpasswordurl', 'auth'),
+                                            get_string('forgottenpassword', 'auth'), ''));
+    $temp->add(new admin_setting_confightmleditor('auth_instructions', get_string('instructions', 'auth'),
+                                                get_string('authinstructions', 'auth'), ''));
+    $temp->add(new admin_setting_configtext('allowemailaddresses', get_string('allowemailaddresses', 'admin'), get_string('configallowemailaddresses', 'admin'), '', PARAM_NOTAGS));
+    $temp->add(new admin_setting_configtext('denyemailaddresses', get_string('denyemailaddresses', 'admin'), get_string('configdenyemailaddresses', 'admin'), '', PARAM_NOTAGS));
+    $temp->add(new admin_setting_configcheckbox('verifychangedemail', get_string('verifychangedemail', 'admin'), get_string('configverifychangedemail', 'admin'), 1));
+
+    $temp->add(new admin_setting_configtext('recaptchapublickey', get_string('recaptchapublickey', 'admin'), get_string('configrecaptchapublickey', 'admin'), '', PARAM_NOTAGS));
+    $temp->add(new admin_setting_configtext('recaptchaprivatekey', get_string('recaptchaprivatekey', 'admin'), get_string('configrecaptchaprivatekey', 'admin'), '', PARAM_NOTAGS));
+    $ADMIN->add('authsettings', $temp);
+
+
+    if ($auths = get_plugin_list('auth')) {
+        $authsenabled = get_enabled_auth_plugins();
+        $authbyname = array();
+
+        foreach ($auths as $auth => $authdir) {
+            $strauthname = get_string('pluginname', "auth_{$auth}");
+            $authbyname[$strauthname] = $auth;
+        }
+        ksort($authbyname);
+
+        foreach ($authbyname as $strauthname=>$authname) {
+            if (file_exists($authdir.'/settings.php')) {
+                // do not show disabled auths in tree, keep only settings link on manage page
+                $settings = new admin_settingpage('authsetting'.$authname, $strauthname, 'moodle/site:config', !in_array($authname, $authsenabled));
+                if ($ADMIN->fulltree) {
+                    include($authdir.'/settings.php');
+                }
+                // TODO: finish implementation of common settings - locking, etc.
+                $ADMIN->add('authsettings', $settings);
+
+            } else {
+                $ADMIN->add('authsettings', new admin_externalpage('authsetting'.$authname, $strauthname, "$CFG->wwwroot/$CFG->admin/auth_config.php?auth=$authname", 'moodle/site:config', !in_array($authname, $authsenabled)));
+            }
+        }
+    }
+
+
+    // Enrolment plugins
+    $ADMIN->add('modules', new admin_category('enrolments', get_string('enrolments', 'enrol')));
+    $temp = new admin_settingpage('manageenrols', get_string('manageenrols', 'enrol'));
+    $temp->add(new admin_setting_manageenrols());
+    if (empty($CFG->enrol_plugins_enabled)) {
+        $enabled = array();
+    } else {
+        $enabled = explode(',', $CFG->enrol_plugins_enabled);
+    }
+    $enrols = get_plugin_list('enrol');
+    $ADMIN->add('enrolments', $temp);
+    foreach($enrols as $enrol=>$enrolpath) {
+        if (!file_exists("$enrolpath/settings.php")) {
+            continue;
+        }
+
+        $settings = new admin_settingpage('enrolsettings'.$enrol, get_string('pluginname', 'enrol_'.$enrol), 'moodle/site:config', !in_array($enrol, $enabled));
+        // settings.php may create a subcategory or unset the settings completely
+        include("$enrolpath/settings.php");
+        if ($settings) {
+            $ADMIN->add('enrolments', $settings);
+        }
+
+    }
+    unset($enabled);
+    unset($enrols);
+
 
 /// Editor plugins
     $ADMIN->add('modules', new admin_category('editorsettings', get_string('editors', 'editor')));
@@ -63,7 +141,7 @@
         if (file_exists($CFG->dirroot . '/lib/editor/'.$editor.'/settings.php')) {
             $editor_setting = new admin_externalpage('editorsettings'.$editor, $editorstr, $url.'&amp;action=edit&amp;editor='.$editor);
             $ADMIN->add('editorsettings', $editor_setting);
-        } 
+        }
     }
 /// License types
     $ADMIN->add('modules', new admin_category('licensesettings', get_string('license')));
@@ -284,7 +362,7 @@
         $temp->add(new admin_setting_heading('webservicesaredisabled', '', get_string('disabledwarning', 'webservice')));
     }
     $ADMIN->add('webservicesettings', $temp);
-    
+
 
 if ($hassiteconfig || has_capability('moodle/question:config', $systemcontext)) {
     // Question type settings.
diff --git a/admin/settings/security.php b/admin/settings/security.php
index ca4102c4e1fd..4d0d6aae2e16 100644
--- a/admin/settings/security.php
+++ b/admin/settings/security.php
@@ -67,6 +67,7 @@
     $temp->add(new admin_setting_configtext('minpasswordupper', get_string('minpasswordupper', 'admin'), get_string('configminpasswordupper', 'admin'), 1, PARAM_INT));
     $temp->add(new admin_setting_configtext('minpasswordnonalphanum', get_string('minpasswordnonalphanum', 'admin'), get_string('configminpasswordnonalphanum', 'admin'), 1, PARAM_INT));
     $temp->add(new admin_setting_configtext('maxconsecutiveidentchars', get_string('maxconsecutiveidentchars', 'admin'), get_string('configmaxconsecutiveidentchars', 'admin'), 0, PARAM_INT));
+    $temp->add(new admin_setting_configcheckbox('groupenrolmentkeypolicy', get_string('groupenrolmentkeypolicy', 'admin'), get_string('groupenrolmentkeypolicy_desc', 'admin'), 1));
     $temp->add(new admin_setting_configcheckbox('disableuserimages', get_string('disableuserimages', 'admin'), get_string('configdisableuserimages', 'admin'), 0));
     $temp->add(new admin_setting_configcheckbox('emailchangeconfirmation', get_string('emailchangeconfirmation', 'admin'), get_string('configemailchangeconfirmation', 'admin'), 1));
     $ADMIN->add('security', $temp);
diff --git a/admin/settings/users.php b/admin/settings/users.php
index 36a1ecba2c78..cade4eee3a8b 100644
--- a/admin/settings/users.php
+++ b/admin/settings/users.php
@@ -2,7 +2,6 @@
 
 // This file defines settingpages and externalpages under the "users" category
 
-$ADMIN->add('users', new admin_category('authsettings', get_string('authentication','admin')));
 $ADMIN->add('users', new admin_category('accounts', get_string('accounts', 'admin')));
 $ADMIN->add('users', new admin_category('roles', get_string('permissions', 'role')));
 
@@ -17,54 +16,6 @@
  or has_capability('moodle/cohort:view', $systemcontext)) { // speedup for non-admins, add all caps used on this page
 
 
-    $temp = new admin_settingpage('manageauths', get_string('authsettings', 'admin'));
-    $temp->add(new admin_setting_manageauths());
-    $temp->add(new admin_setting_heading('manageauthscommonheading', get_string('commonsettings', 'admin'), ''));
-    $temp->add(new admin_setting_special_registerauth());
-    $temp->add(new admin_setting_configselect('guestloginbutton', get_string('guestloginbutton', 'auth'),
-                                              get_string('showguestlogin', 'auth'), '1', array('0'=>get_string('hide'), '1'=>get_string('show'))));
-    $temp->add(new admin_setting_configtext('alternateloginurl', get_string('alternateloginurl', 'auth'),
-                                            get_string('alternatelogin', 'auth', htmlspecialchars(get_login_url())), ''));
-    $temp->add(new admin_setting_configtext('forgottenpasswordurl', get_string('forgottenpasswordurl', 'auth'),
-                                            get_string('forgottenpassword', 'auth'), ''));
-    $temp->add(new admin_setting_confightmleditor('auth_instructions', get_string('instructions', 'auth'),
-                                                get_string('authinstructions', 'auth'), ''));
-    $temp->add(new admin_setting_configtext('allowemailaddresses', get_string('allowemailaddresses', 'admin'), get_string('configallowemailaddresses', 'admin'), '', PARAM_NOTAGS));
-    $temp->add(new admin_setting_configtext('denyemailaddresses', get_string('denyemailaddresses', 'admin'), get_string('configdenyemailaddresses', 'admin'), '', PARAM_NOTAGS));
-    $temp->add(new admin_setting_configcheckbox('verifychangedemail', get_string('verifychangedemail', 'admin'), get_string('configverifychangedemail', 'admin'), 1));
-
-    $temp->add(new admin_setting_configtext('recaptchapublickey', get_string('recaptchapublickey', 'admin'), get_string('configrecaptchapublickey', 'admin'), '', PARAM_NOTAGS));
-    $temp->add(new admin_setting_configtext('recaptchaprivatekey', get_string('recaptchaprivatekey', 'admin'), get_string('configrecaptchaprivatekey', 'admin'), '', PARAM_NOTAGS));
-    $ADMIN->add('authsettings', $temp);
-
-
-    if ($auths = get_plugin_list('auth')) {
-        $authsenabled = get_enabled_auth_plugins();
-        $authbyname = array();
-
-        foreach ($auths as $auth => $authdir) {
-            $strauthname = get_string('pluginname', "auth_{$auth}");
-            $authbyname[$strauthname] = $auth;
-        }
-        ksort($authbyname);
-
-        foreach ($authbyname as $strauthname=>$authname) {
-            if (file_exists($authdir.'/settings.php')) {
-                // do not show disabled auths in tree, keep only settings link on manage page
-                $settings = new admin_settingpage('authsetting'.$authname, $strauthname, 'moodle/site:config', !in_array($authname, $authsenabled));
-                if ($ADMIN->fulltree) {
-                    include($authdir.'/settings.php');
-                }
-                // TODO: finish implementation of common settings - locking, etc.
-                $ADMIN->add('authsettings', $settings);
-
-            } else {
-                $ADMIN->add('authsettings', new admin_externalpage('authsetting'.$authname, $strauthname, "$CFG->wwwroot/$CFG->admin/auth_config.php?auth=$authname", 'moodle/site:config', !in_array($authname, $authsenabled)));
-            }
-        }
-    }
-
-
     if (empty($CFG->loginhttps)) {
         $securewwwroot = $CFG->wwwroot;
     } else {
@@ -94,7 +45,7 @@
             $studentroles    = array();
             $teacherroles    = array();
             $creatornewroles = array();
-            
+
             foreach (get_all_roles() as $role) {
                 $rolename = strip_tags(format_string($role->name)) . ' ('. $role->shortname . ')';
                 $allroles[$role->id] = $rolename;
@@ -135,7 +86,7 @@
             $defaultstudentid = key($studentroles);
             reset($teacherroles);
             $defaultteacherid = key($teacherroles);
-            
+
             if ($userroles) {
                 reset($userroles);
                 $defaultuserid = key($userroles);
@@ -155,19 +106,12 @@
         $temp->add(new admin_setting_configcheckbox('nodefaultuserrolelists', get_string('nodefaultuserrolelists', 'admin'), get_string('confignodefaultuserrolelists', 'admin'), 0));
 
         if (!during_initial_install()) {
-            $temp->add(new admin_setting_configselect('defaultcourseroleid', get_string('defaultcourseroleid', 'admin'),
-                          get_string('configdefaultcourseroleid', 'admin'), $defaultstudentid, $allroles));
             $temp->add(new admin_setting_configselect('creatornewroleid', get_string('creatornewroleid', 'admin'),
                           get_string('configcreatornewroleid', 'admin'), $defaultteacherid, $creatornewroles));
         }
 
         $temp->add(new admin_setting_configcheckbox('autologinguests', get_string('autologinguests', 'admin'), get_string('configautologinguests', 'admin'), 0));
 
-        if (!during_initial_install()) {
-            $temp->add(new admin_setting_configmultiselect('nonmetacoursesyncroleids', get_string('nonmetacoursesyncroleids', 'admin'),
-                      get_string('confignonmetacoursesyncroleids', 'admin'), array(), $allroles));
-        }
-
         $temp->add(new admin_setting_configmultiselect('hiddenuserfields', get_string('hiddenuserfields', 'admin'),
                    get_string('confighiddenuserfields', 'admin'), array(),
                        array('description' => get_string('description'),
diff --git a/admin/uploaduser.php b/admin/uploaduser.php
index c58c998b0612..bc2d1d55d7b1 100755
--- a/admin/uploaduser.php
+++ b/admin/uploaduser.php
@@ -162,26 +162,21 @@
     $weakpasswords = 0;
 
     // caches
-    $ccache    = array(); // course cache - do not fetch all courses here, we  will not probably use them all anyway!
-    $rolecache = array(); // roles lookup cache
+    $ccache       = array(); // course cache - do not fetch all courses here, we  will not probably use them all anyway!
+    $rolecache    = uu_allowed_roles_cache(); // roles lookup cache
+    $manualcacche = array(); // cache of used manual enrol plugins in each course
 
     $allowedauths   = uu_allowed_auths();
     $allowedauths   = array_keys($allowedauths);
     $availableauths = get_plugin_list('auth');
     $availableauths = array_keys($availableauths);
 
-    $allowedroles = uu_allowed_roles(true);
-    foreach ($allowedroles as $rid=>$rname) {
-        $rolecache[$rid] = new object();
-        $rolecache[$rid]->id = $rid;
-        $rolecache[$rid]->name = $rname;
-        if (!is_numeric($rname)) { // only non-numeric shornames are supported!!!
-            $rolecache[$rname] = new object();
-            $rolecache[$rname]->id = $rid;
-            $rolecache[$rname]->name = $rname;
-        }
+    // we use only manual enrol plugin here, if it is disabled no enrol is done
+    if (enrol_is_enabled('manual')) {
+        $manual = enrol_get_plugin('manual');
+    } else {
+        $manual = NULL;
     }
-    unset($allowedroles);
 
     // clear bulk selection
     if ($bulk) {
@@ -210,7 +205,7 @@
         // add fields to user object
         foreach ($line as $key => $value) {
             if ($value !== '') {
-                $key = $columns[$key];                
+                $key = $columns[$key];
                 // password is special field
                 if ($key == 'password') {
                     if ($value !== '') {
@@ -365,16 +360,11 @@
                     $renameerrors++;
                     continue;
                 }
-                if ($DB->set_field('user', 'username', $user->username, array('id'=>$olduser->id))) {
-                    $upt->track('username', '', 'normal', false); // clear previous
-                    $upt->track('username', $oldusername.'-->'.$user->username, 'info');
-                    $upt->track('status', $struserrenamed);
-                    $renames++;
-                } else {
-                    $upt->track('status', $strusernotrenamedexists, 'error');
-                    $renameerrors++;
-                    continue;
-                }
+                $DB->set_field('user', 'username', $user->username, array('id'=>$olduser->id));
+                $upt->track('username', '', 'normal', false); // clear previous
+                $upt->track('username', $oldusername.'-->'.$user->username, 'info');
+                $upt->track('status', $struserrenamed);
+                $renames++;
             } else {
                 $upt->track('status', $strusernotrenamedmissing, 'error');
                 $renameerrors++;
@@ -501,14 +491,9 @@
                     $upt->track('auth', $struserauthunsupported, 'warning');
                 }
 
-                if ($DB->update_record('user', $existinguser)) {
-                    $upt->track('status', $struserupdated);
-                    $usersupdated++;
-                } else {
-                    $upt->track('status', $strusernotupdated, 'error');
-                    $userserrors++;
-                    continue;
-                }
+                $DB->update_record('user', $existinguser);
+                $upt->track('status', $struserupdated);
+                $usersupdated++;
                 // save custom profile fields data from csv file
                 profile_save_data($existinguser);
             }
@@ -555,26 +540,21 @@
                 }
             }
 
-            if ($user->id = $DB->insert_record('user', $user)) {
-                $info = ': ' . $user->username .' (ID = ' . $user->id . ')';
-                $upt->track('status', $struseradded);
-                $upt->track('id', $user->id, 'normal', false);
-                $usersnew++;
-                if ($createpasswords and empty($user->password)) {
-                    // passwords will be created and sent out on cron
-                    set_user_preference('create_password', 1, $user->id);
-                    set_user_preference('auth_forcepasswordchange', 1, $user->id);
-                    $upt->track('password', get_string('new'));
-                }
-                if ($forcechangepassword) {
-                    set_user_preference('auth_forcepasswordchange', 1, $user->id);
-                }
-            } else {
-                // Record not added -- possibly some other error
-                $upt->track('status', $strusernotaddederror, 'error');
-                $userserrors++;
-                continue;
+            $user->id = $DB->insert_record('user', $user);
+            $info = ': ' . $user->username .' (ID = ' . $user->id . ')';
+            $upt->track('status', $struseradded);
+            $upt->track('id', $user->id, 'normal', false);
+            $usersnew++;
+            if ($createpasswords and empty($user->password)) {
+                // passwords will be created and sent out on cron
+                set_user_preference('create_password', 1, $user->id);
+                set_user_preference('auth_forcepasswordchange', 1, $user->id);
+                $upt->track('password', get_string('new'));
             }
+            if ($forcechangepassword) {
+                set_user_preference('auth_forcepasswordchange', 1, $user->id);
+            }
+
             // save custom profile fields data
             profile_save_data($user);
 
@@ -595,9 +575,12 @@
             }
             $i = substr($column, 6);
 
+            if (empty($user->{'course'.$i})) {
+                continue;
+            }
             $shortname = $user->{'course'.$i};
             if (!array_key_exists($shortname, $ccache)) {
-                if (!$course = $DB->get_record('course', array('shortname'=>$shortname), 'id, shortname, defaultrole')) {
+                if (!$course = $DB->get_record('course', array('shortname'=>$shortname), 'id, shortname')) {
                     $upt->track('enrolments', get_string('unknowncourse', 'error', $shortname), 'error');
                     continue;
                 }
@@ -606,62 +589,61 @@
             }
             $courseid      = $ccache[$shortname]->id;
             $coursecontext = get_context_instance(CONTEXT_COURSE, $courseid);
-
-            // find role
-            $rid = false;
-            if (!empty($user->{'role'.$i})) {
-                $addrole = $user->{'role'.$i};
-                if (array_key_exists($addrole, $rolecache)) {
-                    $rid = $rolecache[$addrole]->id;
+            if (!isset($manualcache[$courseid])) {
+                if ($instances = enrol_get_instances($courseid, false)) {
+                    $manualcache[$courseid] = reset($instances);
                 } else {
-                    $upt->track('enrolments', get_string('unknownrole', 'error', $addrole), 'error');
-                    continue;
+                    $manualcache[$courseid] = false;
                 }
+            }
 
-            } else if (!empty($user->{'type'.$i})) {
-                // if no role, then find "old" enrolment type
-                $addtype = $user->{'type'.$i};
-                if ($addtype < 1 or $addtype > 3) {
-                    $upt->track('enrolments', $strerror.': typeN = 1|2|3', 'error');
-                    continue;
-                } else if ($addtype == 1 and empty($formdata->uulegacy1)) {
-                    if (empty($ccache[$shortname]->defaultrole)) {
-                        $rid = $CFG->defaultcourseroleid;
+            if ($manual and $manualcache[$courseid]) {
+
+                // find role
+                $rid = false;
+                if (!empty($user->{'role'.$i})) {
+                    $addrole = $user->{'role'.$i};
+                    if (array_key_exists($addrole, $rolecache)) {
+                        $rid = $rolecache[$addrole]->id;
                     } else {
-                        $rid = $ccache[$shortname]->defaultrole;
+                        $upt->track('enrolments', get_string('unknownrole', 'error', $addrole), 'error');
+                        continue;
                     }
-                } else {
-                    $rid = $formdata->{'uulegacy'.$addtype};
-                }
 
-            } else {
-                // no role specified, use the default
-                if (empty($ccache[$shortname]->defaultrole)) {
-                    $rid = $CFG->defaultcourseroleid;
+                } else if (!empty($user->{'type'.$i})) {
+                    // if no role, then find "old" enrolment type
+                    $addtype = $user->{'type'.$i};
+                    if ($addtype < 1 or $addtype > 3) {
+                        $upt->track('enrolments', $strerror.': typeN = 1|2|3', 'error');
+                        continue;
+                    } else if (empty($formdata->{'uulegacy'.$addtype})) {
+                        continue;
+                    } else {
+                        $rid = $formdata->{'uulegacy'.$addtype};
+                    }
                 } else {
-                    $rid = $ccache[$shortname]->defaultrole;
+                    // no role specified, use the default from manual enrol plugin
+                    $rid = $manualcache[$courseid]->roleid;
                 }
-            }
 
-            // find duration
-            $timestart = 0;
-            $timeend   = 0;
-            if (!empty($user->{'enrolperiod'.$i})) {
-                $duration = (int)$user->{'enrolperiod'.$i} * 86400; // convert days to seconds
-                if ($duration > 0) { // sanity check
-                    $timestart = time();
-                    $timeend   = $timestart + $duration;
-                }
-            }
+                if ($rid) {
+                    // find duration
+                    $timestart = 0;
+                    $timeend   = 0;
+                    if (!empty($user->{'enrolperiod'.$i})) {
+                        $duration = (int)$user->{'enrolperiod'.$i} * 86400; // convert days to seconds
+                        if ($duration > 0) { // sanity check
+                            $timestart = time();
+                            $timeend   = $timestart + $duration;
+                        }
+                    }
 
-            if ($rid) {
-                $a = new object();
-                $a->course = $shortname;
-                $a->role   = $rolecache[$rid]->name;
-                if (role_assign($rid, $user->id, 0, $coursecontext->id, $timestart, $timeend)) {
-                    $upt->track('enrolments', get_string('enrolledincourserole', '', $a));
-                } else {
-                    $upt->track('enrolments', get_string('enrolledincoursenotrole', '', $a), 'error');
+                    $manual->enrol_user($manualcache[$courseid], $user->id, $rid, $timestart, $timeend, true);
+
+                    $a = new object();
+                    $a->course = $shortname;
+                    $a->role   = $rolecache[$rid]->name;
+                    $upt->track('enrolments', get_string('enrolledincourserole', 'enrol_manual', $a));
                 }
             }
 
@@ -708,6 +690,8 @@
                 try {
                     if (groups_add_member($gid, $user->id)) {
                         $upt->track('enrolments', get_string('addedtogroup', '', $gname));
+                    }  else {
+                        $upt->track('enrolments', get_string('addedtogroupnot', '', $gname), 'error');
                     }
                 } catch (moodle_exception $e) {
                     $upt->track('enrolments', get_string('addedtogroupnot', '', $gname), 'error');
@@ -971,7 +955,7 @@ function init() {
         echo '<th class="header c'.$ci++.'" scope="col">'.get_string('email').'</th>';
         echo '<th class="header c'.$ci++.'" scope="col">'.get_string('password').'</th>';
         echo '<th class="header c'.$ci++.'" scope="col">'.get_string('authentication').'</th>';
-        echo '<th class="header c'.$ci++.'" scope="col">'.get_string('enrolments').'</th>';
+        echo '<th class="header c'.$ci++.'" scope="col">'.get_string('enrolments', 'enrol').'</th>';
         echo '<th class="header c'.$ci++.'" scope="col">'.get_string('delete').'</th>';
         echo '</tr>';
         $this->_row = null;
@@ -1149,7 +1133,7 @@ function uu_allowed_auths() {
     global $CFG;
 
     // only following plugins are guaranteed to work properly
-    // TODO: add support for more plguins in 2.0
+    // TODO: add support for more plugins in 2.0
     $whitelist = array('manual', 'nologin', 'none', 'email');
     $plugins = get_enabled_auth_plugins();
     $choices = array();
@@ -1161,21 +1145,25 @@ function uu_allowed_auths() {
 }
 
 /**
- * Returns list of non administrator roles
+ * Returns list of roles that are assignable in courses
  */
-function uu_allowed_roles($shortname=false) {
-    global $CFG;
+function uu_allowed_roles() {
+    // let's cheat a bit, frontpage is guaranteed to exist and has the same list of roles ;-)
+    $roles = get_assignable_roles(get_context_instance(CONTEXT_COURSE, SITEID), ROLENAME_ORIGINALANDSHORT);
+    return array_reverse($roles, true);
+}
 
-    $roles = get_all_roles();
-    $choices = array();
-    foreach($roles as $role) {
-        if ($shortname) {
-            $choices[$role->id] = $role->shortname;
-        } else {
-            $choices[$role->id] = format_string($role->name);
+function uu_allowed_roles_cache() {
+    $allowedroles = get_assignable_roles(get_context_instance(CONTEXT_COURSE, SITEID), ROLENAME_SHORT);
+    foreach ($allowedroles as $rid=>$rname) {
+        $rolecache[$rid] = new object();
+        $rolecache[$rid]->id   = $rid;
+        $rolecache[$rid]->name = $rname;
+        if (!is_numeric($rname)) { // only non-numeric shortnames are supported!!!
+            $rolecache[$rname] = new object();
+            $rolecache[$rname]->id   = $rid;
+            $rolecache[$rname]->name = $rname;
         }
     }
-
-    return $choices;
+    return $rolecache;
 }
-
diff --git a/admin/uploaduser_form.php b/admin/uploaduser_form.php
index 88a1ced61a65..29d3cb3d9f80 100644
--- a/admin/uploaduser_form.php
+++ b/admin/uploaduser_form.php
@@ -59,7 +59,7 @@ function definition (){
         // I am the template user, why should it be the administrator? we have roles now, other ppl may use this script ;-)
         $templateuser = $USER;
 
-// upload settings and file
+        // upload settings and file
         $mform->addElement('header', 'settingsheader', get_string('settings'));
         $mform->addElement('static', 'uutypelabel', get_string('uuoptype', 'admin') );
 
@@ -105,7 +105,7 @@ function definition (){
         $mform->addElement('select', 'uubulk', get_string('uubulk', 'admin'), $choices);
         $mform->setDefault('uubulk', 0);
 
-// roles selection
+        // roles selection
         $showroles = false;
         foreach ($columns as $column) {
             if (preg_match('/^type\d+$/', $column)) {
@@ -118,31 +118,41 @@ function definition (){
 
             $choices = uu_allowed_roles(true);
 
-            $choices[0] = get_string('uucoursedefaultrole', 'admin');
             $mform->addElement('select', 'uulegacy1', get_string('uulegacy1role', 'admin'), $choices);
-            $mform->setDefault('uulegacy1', 0);
-            unset($choices[0]);
+            if ($studentroles = get_archetype_roles('student')) {
+                foreach ($studentroles as $role) {
+                    if (isset($choices[$role->id])) {
+                        $mform->setDefault('uulegacy1', $role->id);
+                        break;
+                    }
+                }
+                unset($studentroles);
+            }
 
             $mform->addElement('select', 'uulegacy2', get_string('uulegacy2role', 'admin'), $choices);
             if ($editteacherroles = get_archetype_roles('editingteacher')) {
-                $editteacherrole = array_shift($editteacherroles);   /// Take the first one
-                $mform->setDefault('uulegacy2', $editteacherrole->id);
+                foreach ($editteacherroles as $role) {
+                    if (isset($choices[$role->id])) {
+                        $mform->setDefault('uulegacy2', $role->id);
+                        break;
+                    }
+                }
                 unset($editteacherroles);
-            } else {
-                $mform->setDefault('uulegacy2', $CFG->defaultcourseroleid);
             }
 
             $mform->addElement('select', 'uulegacy3', get_string('uulegacy3role', 'admin'), $choices);
             if ($teacherroles = get_archetype_roles('teacher')) {
-                $teacherrole = array_shift($teacherroles);   /// Take the first one
-                $mform->setDefault('uulegacy3', $teacherrole->id);
+                foreach ($teacherroles as $role) {
+                    if (isset($choices[$role->id])) {
+                        $mform->setDefault('uulegacy3', $role->id);
+                        break;
+                    }
+                }
                 unset($teacherroles);
-            } else {
-                $mform->setDefault('uulegacy3', $CFG->defaultcourseroleid);
             }
         }
 
-// default values
+        // default values
         $mform->addElement('header', 'defaultheader', get_string('defaultvalues', 'admin'));
 
         $mform->addElement('text', 'username', get_string('username'), 'size="20"');
@@ -248,10 +258,10 @@ function definition (){
         $mform->setType('address', PARAM_MULTILANG);
         $mform->setAdvanced('address');
 
-        /// Next the profile defaults
+        // Next the profile defaults
         profile_definition($mform);
 
-// hidden fields
+        // hidden fields
         $mform->addElement('hidden', 'iid');
         $mform->setType('iid', PARAM_INT);
 
@@ -363,7 +373,7 @@ function get_data() {
 class admin_uploaduser_form3 extends moodleform {
     function definition (){
         global $CFG, $USER;
-        $mform =& $this->_form;               
+        $mform =& $this->_form;
         $this->add_action_buttons(false, get_string('uploadnewfile'));
     }
 }
diff --git a/admin/user/user_bulk_enrol.php b/admin/user/user_bulk_enrol.php
index 4b99ef87e9a8..4660a3ee4303 100644
--- a/admin/user/user_bulk_enrol.php
+++ b/admin/user/user_bulk_enrol.php
@@ -84,17 +84,13 @@ function sort_compare($a, $b) {
         $ids = explode(',', $info);
         if(!empty($ids[2])) {
             $context = get_context_instance(CONTEXT_COURSE, $ids[1]);
-            if( role_assign(5, $ids[0], 0, $context->id) ) {
-                continue;
-            }
+            role_assign(5, $ids[0], $context->id);
         } else {
             if( empty($ids[1] ) ) {
                 continue;
             }
             $context = get_context_instance(CONTEXT_COURSE, $ids[1]);
-            if( role_unassign(5, $ids[0], 0, $context->id) ) {
-                continue;
-            }
+            role_unassign(5, $ids[0], $context->id);
         }
     }
     redirect($return, get_string('changessaved'));
@@ -104,14 +100,12 @@ function sort_compare($a, $b) {
 echo '<form id="multienrol" name="multienrol" method="post" action="user_bulk_enrol.php">';
 echo '<input type="hidden" name="processed" value="yes" />';
 $count = 0;
-foreach($users as $user)
-{
+foreach($users as $user) {
     $temparray = array (
         '<a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.SITEID.'">'.$user->fullname.'</a>'
     );
-    $mycourses = get_my_courses($user->id);
-    foreach($courses as $acourse)
-    {
+    $mycourses = enrol_get_users_courses($user->id, false);
+    foreach($courses as $acourse) {
         $state = '';
         if (isset($mycourses[$acourse->id])) {
             $state = 'checked="checked"';
diff --git a/admin/webservice/testclient_forms.php b/admin/webservice/testclient_forms.php
index 5f5f3780f1ab..342fc353b619 100644
--- a/admin/webservice/testclient_forms.php
+++ b/admin/webservice/testclient_forms.php
@@ -25,129 +25,6 @@ public function definition() {
 
 // === Test client forms ===
 
-
-class moodle_enrol_role_assign_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->addElement('text', 'wspassword', 'wspassword');
-        } else  if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', PARAM_SAFEDIR);
-
-        /// specific to the create users function
-        $mform->addElement('text', 'userid', 'userid');
-        $mform->addElement('text', 'roleid', 'roleid');
-        $mform->addElement('text', 'contextid', 'contextid');
-        $mform->addElement('text', 'timestart', 'timestart');
-        $mform->addElement('text', 'timeend', 'timeend');
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_SAFEDIR);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_SAFEDIR);
-
-
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['enrolments'] = array();
-        $params['enrolments'][] = (array)$data;
-
-        return $params;
-    }
-}
-
-class moodle_enrol_role_unassign_form extends moodleform {
-    public function definition() {
-        global $CFG;
-
-        $mform = $this->_form;
-
-
-        $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
-
-        //note: these values are intentionally PARAM_RAW - we want users to test any rubbish as parameters
-        $data = $this->_customdata;
-        if ($data['authmethod'] == 'simple') {
-            $mform->addElement('text', 'wsusername', 'wsusername');
-            $mform->addElement('text', 'wspassword', 'wspassword');
-        } else  if ($data['authmethod'] == 'token') {
-            $mform->addElement('text', 'token', 'token');
-        }
-
-        $mform->addElement('hidden', 'authmethod', $data['authmethod']);
-        $mform->setType('authmethod', PARAM_SAFEDIR);
-
-        /// specific to the create users function
-        $mform->addElement('text', 'userid', 'userid');
-        $mform->addElement('text', 'roleid', 'roleid');
-        $mform->addElement('text', 'contextid', 'contextid');
-
-        $mform->addElement('hidden', 'function');
-        $mform->setType('function', PARAM_SAFEDIR);
-
-        $mform->addElement('hidden', 'protocol');
-        $mform->setType('protocol', PARAM_SAFEDIR);
-
-
-
-        $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
-
-        $this->add_action_buttons(true, get_string('execute', 'webservice'));
-    }
-
-    public function get_params() {
-        if (!$data = $this->get_data()) {
-            return null;
-        }
-        // remove unused from form data
-        unset($data->submitbutton);
-        unset($data->protocol);
-        unset($data->function);
-        unset($data->wsusername);
-        unset($data->wspassword);
-        unset($data->token);
-        unset($data->authmethod);
-
-        $params = array();
-        $params['unenrolments'] = array();
-        $params['unenrolments'][] = (array)$data;
-
-        return $params;
-    }
-}
-
 class moodle_user_create_users_form extends moodleform {
     public function definition() {
         global $CFG;
@@ -273,7 +150,7 @@ public function get_params() {
         if (!$data = $this->get_data()) {
             return null;
         }
-        
+
         //set customfields
         if (!empty($data->customfieldtype)) {
             $data->customfields = array(array('type' => $data->customfieldtype, 'value' => $data->customfieldvalue));
@@ -369,7 +246,7 @@ public function get_params() {
             $params['userids'][] = $data->userids[$i];
         }
         /// end of specific code to the create users function
-        
+
         return $params;
     }
 }
@@ -449,7 +326,7 @@ public function definition() {
         global $CFG;
 
         $mform = $this->_form;
-       
+
 
         $mform->addElement('header', 'wstestclienthdr', get_string('testclient', 'webservice'));
 
@@ -476,7 +353,7 @@ public function definition() {
         $mform->addElement('hidden', 'protocol');
         $mform->setType('protocol', PARAM_SAFEDIR);
 
-        
+
 
         $mform->addElement('static', 'warning', '', get_string('executewarnign', 'webservice'));
 
diff --git a/auth/cas/auth.php b/auth/cas/auth.php
index f2a498fedfbb..c2aaa3cb68be 100644
--- a/auth/cas/auth.php
+++ b/auth/cas/auth.php
@@ -824,9 +824,9 @@ function sync_users ($do_updates = true) {
                     // update course creators if needed
                     if ($creatorrole !== false) {
                         if ($this->iscreator($user->username)) {
-                            role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'cas');
+                            role_assign($creatorrole->id, $user->id, $sitecontext->id, 'auth_cas');
                         } else {
-                            role_unassign($creatorrole->id, $user->id, 0, $sitecontext->id, 'cas');
+                            role_unassign($creatorrole->id, $user->id, $sitecontext->id, 'auth_cas');
                         }
                     }
                 }
@@ -883,7 +883,7 @@ function sync_users ($do_updates = true) {
 
                 // add course creators if needed
                 if ($creatorrole !== false and $this->iscreator($user->username)) {
-                    role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'cas');
+                    role_assign($creatorrole->id, $user->id, $sitecontext->id, 'auth_cas');
                 }
             }
             $transaction->allow_commit();
@@ -1127,10 +1127,10 @@ function sync_roles($user) {
             $creatorrole = array_shift($roles);      // We can only use one, let's use the first one
             $systemcontext = get_context_instance(CONTEXT_SYSTEM);
             if ($iscreator) { // Following calls will not create duplicates
-                role_assign($creatorrole->id, $user->id, 0, $systemcontext->id, 0, 0, 0, 'cas');
+                role_assign($creatorrole->id, $user->id, $systemcontext->id, 'auth_cas');
             } else {
                 //unassign only if previously assigned by this plugin!
-                role_unassign($creatorrole->id, $user->id, 0, $systemcontext->id, 'cas');
+                role_unassign($creatorrole->id, $user->id, $systemcontext->id, 'auth_cas');
             }
         }
     }
diff --git a/auth/fc/auth.php b/auth/fc/auth.php
index ce9a5933399f..3717dc4bf7f9 100644
--- a/auth/fc/auth.php
+++ b/auth/fc/auth.php
@@ -183,10 +183,10 @@ function sync_roles($user) {
             $systemcontext = get_context_instance(CONTEXT_SYSTEM);
 
             if ($iscreator) { // Following calls will not create duplicates
-                role_assign($creatorrole->id, $user->id, 0, $systemcontext->id, 0, 0, 0, 'fc');
+                role_assign($creatorrole->id, $user->id, $systemcontext->id, 'auth_fc');
             } else {
                 //unassign only if previously assigned by this plugin!
-                role_unassign($creatorrole->id, $user->id, 0, $systemcontext->id, 'fc');
+                role_unassign($creatorrole->id, $user->id, $systemcontext->id, 'auth_fc');
             }
         }
     }
diff --git a/auth/ldap/auth.php b/auth/ldap/auth.php
index 0bac19c5fb08..f8008cf64b73 100644
--- a/auth/ldap/auth.php
+++ b/auth/ldap/auth.php
@@ -744,9 +744,9 @@ function sync_users($do_updates=true) {
                     // update course creators if needed
                     if ($creatorrole !== false) {
                         if ($this->iscreator($user->username)) {
-                            role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap');
+                            role_assign($creatorrole->id, $user->id, $sitecontext->id, 'auth_ldap');
                         } else {
-                            role_unassign($creatorrole->id, $user->id, 0, $sitecontext->id, 'ldap');
+                            role_unassign($creatorrole->id, $user->id, $sitecontext->id, 'auth_ldap');
                         }
                     }
                 }
@@ -807,7 +807,7 @@ function sync_users($do_updates=true) {
 
                 // add course creators if needed
                 if ($creatorrole !== false and $this->iscreator($user->username)) {
-                    role_assign($creatorrole->id, $user->id, 0, $sitecontext->id, 0, 0, 0, 'ldap');
+                    role_assign($creatorrole->id, $user->id, $sitecontext->id, 'auth_ldap');
                 }
             }
             $transaction->allow_commit();
@@ -1948,10 +1948,10 @@ function sync_roles($user) {
             $systemcontext = get_context_instance(CONTEXT_SYSTEM);
 
             if ($iscreator) { // Following calls will not create duplicates
-                role_assign($creatorrole->id, $user->id, 0, $systemcontext->id, 0, 0, 0, 'ldap');
+                role_assign($creatorrole->id, $user->id, $systemcontext->id, 'auth_ldap');
             } else {
                 //unassign only if previously assigned by this plugin!
-                role_unassign($creatorrole->id, $user->id, 0, $systemcontext->id, 'ldap');
+                role_unassign($creatorrole->id, $user->id, $systemcontext->id, 'auth_ldap');
             }
         }
     }
diff --git a/auth/mnet/auth.php b/auth/mnet/auth.php
index 55af2c550c66..2ee6098d079a 100644
--- a/auth/mnet/auth.php
+++ b/auth/mnet/auth.php
@@ -87,7 +87,7 @@ function user_authorise($token, $useragent) {
         }
 
         $userdata['myhosts'] = array();
-        if($courses = get_my_courses($user->id, 'id', 'id, visible')) {
+        if ($courses = enrol_get_users_courses($user->id, false)) {
             $userdata['myhosts'][] = array('name'=> $SITE->shortname, 'url' => $CFG->wwwroot, 'count' => count($courses));
         }
 
@@ -349,41 +349,33 @@ function confirm_mnet_session($token, $remotepeer) {
             // pass username and an assoc array of "my courses"
             // with info so that the IDP can maintain mnet_enrol_assignments
             $mnetrequest->add_param($remoteuser->username);
-            $fields = 'id, category, sortorder, fullname, shortname, idnumber, summary,
-                       startdate, cost, currency, defaultrole, visible';
-            $courses = get_my_courses($localuser->id, 'visible DESC,sortorder ASC', $fields);
+            $fields = 'id, category, sortorder, fullname, shortname, idnumber, summary, startdate, visible';
+            $courses = enrol_get_users_courses($localuser->id, false, $fields, 'visible DESC,sortorder ASC');
             if (is_array($courses) && !empty($courses)) {
                 // Second request to do the JOINs that we'd have done
-                // inside get_my_courses() if we had been allowed
+                // inside enrol_get_users_courses() if we had been allowed
                 $sql = "SELECT c.id,
-                               cc.name AS cat_name, cc.description AS cat_description,
-                               r.shortname as defaultrolename
+                               cc.name AS cat_name, cc.description AS cat_description
                           FROM {course} c
                           JOIN {course_categories} cc ON c.category = cc.id
-                          LEFT OUTER JOIN {role} r  ON c.defaultrole = r.id
                          WHERE c.id IN (" . join(',',array_keys($courses)) . ')';
                 $extra = $DB->get_records_sql($sql);
 
                 $keys = array_keys($courses);
-                $defaultrolename = $DB->get_field('role', 'shortname', array('id'=>$CFG->defaultcourseroleid));
+                $defaultrole = get_default_course_role($ccache[$shortname]); //TODO: rewrite this completely, there is no default course role any more!!!
                 foreach ($keys AS $id) {
                     if ($courses[$id]->visible == 0) {
                         unset($courses[$id]);
                         continue;
                     }
                     $courses[$id]->cat_id          = $courses[$id]->category;
-                    $courses[$id]->defaultroleid   = $courses[$id]->defaultrole;
+                    $courses[$id]->defaultroleid   = $defaultrole->id;
                     unset($courses[$id]->category);
-                    unset($courses[$id]->defaultrole);
                     unset($courses[$id]->visible);
 
                     $courses[$id]->cat_name        = $extra[$id]->cat_name;
                     $courses[$id]->cat_description = $extra[$id]->cat_description;
-                    if (!empty($extra[$id]->defaultrolename)) {
-                        $courses[$id]->defaultrolename = $extra[$id]->defaultrolename;
-                    } else {
-                        $courses[$id]->defaultrolename = $defaultrolename;
-                    }
+                    $courses[$id]->defaultrolename = $defaultrole->name;
                     // coerce to array
                     $courses[$id] = (array)$courses[$id];
                 }
@@ -486,10 +478,6 @@ function update_enrolments($username, $courses) {
                     c.idnumber,
                     c.summary,
                     c.startdate,
-                    c.cost,
-                    c.currency,
-                    c.defaultroleid,
-                    c.defaultrolename,
                     a.id as assignmentid
                 FROM
                     {mnet_enrol_course} c
diff --git a/auth/shibboleth/index.php b/auth/shibboleth/index.php
index ddb57ffd2a25..ff8a385090f8 100644
--- a/auth/shibboleth/index.php
+++ b/auth/shibboleth/index.php
@@ -76,7 +76,7 @@
                 }
             }
 
-            check_enrolment_plugins($USER);
+            enrol_check_plugins($USER);
             load_all_capabilities();     /// This is what lets the user do anything on the site  :-)
 
             redirect($urltogo);
diff --git a/backup/backuplib.php b/backup/backuplib.php
index f438375f81b0..0a4c3f5c4b9d 100644
--- a/backup/backuplib.php
+++ b/backup/backuplib.php
@@ -162,10 +162,7 @@ function backup_get_needed_users ($courseid, $includemessages=false, $includeblo
     function backup_get_enrolled_users ($courseid) {
         global $CFG;
 
-        // get all users with moodle/course:participate capability, this will include people
-        // assigned at cat level, or site level
-        // but it should be ok if they have no direct assignment at course, mod, block level
-        return get_users_by_capability(get_context_instance(CONTEXT_COURSE, $courseid), 'moodle/course:participate', '', '', '', '', '', '', false);
+        return get_enrolled_users(get_context_instance(CONTEXT_COURSE, $courseid));
     }
 
     //Returns all users ids (every record in users table)
diff --git a/backup/moodle2/backup_course_task.class.php b/backup/moodle2/backup_course_task.class.php
index 09abf4d7f8e2..34eedb71ef8b 100644
--- a/backup/moodle2/backup_course_task.class.php
+++ b/backup/moodle2/backup_course_task.class.php
@@ -69,9 +69,11 @@ public function build() {
         $this->add_step(new create_taskbasepath_directory('create_course_directory'));
 
         // Create the course.xml file with course & category information
-        // annotating some bits, metacourse info, tags and module restrictions
+        // annotating some bits, tags and module restrictions
         $this->add_step(new backup_course_structure_step('course_info', 'course.xml'));
 
+        //TODO: MDL-22793 - add user_enrolments entries
+
         // Annotate the groups used in already annotated groupings
         $this->add_step(new backup_annotate_groups_from_groupings('annotate_groups'));
 
diff --git a/backup/moodle2/backup_final_task.class.php b/backup/moodle2/backup_final_task.class.php
index ef660e57c3b0..d0461e317140 100644
--- a/backup/moodle2/backup_final_task.class.php
+++ b/backup/moodle2/backup_final_task.class.php
@@ -40,6 +40,8 @@ public function build() {
         $coursectxid = get_context_instance(CONTEXT_COURSE, $this->get_courseid())->id;
         $this->add_setting(new backup_activity_generic_setting(backup::VAR_CONTEXTID, base_setting::IS_INTEGER, $coursectxid));
 
+        //TODO: MDL-22793 add enrol instances of enabled enrol plugins in course,
+
         // Generate the groups file with the final annotated groups and groupings
         // including membership based on setting
         $this->add_step(new backup_groups_structure_step('groups', 'groups.xml'));
diff --git a/backup/moodle2/backup_stepslib.php b/backup/moodle2/backup_stepslib.php
index f81388ab270b..0ab76fc28037 100644
--- a/backup/moodle2/backup_stepslib.php
+++ b/backup/moodle2/backup_stepslib.php
@@ -202,7 +202,7 @@ protected function define_structure() {
 
 /**
  * structure step that will generate the course.xml file for the course, including
- * course category reference, tags, metacourse, modules restriction information
+ * course category reference, tags, modules restriction information
  * and some annotations (files & groupings)
  */
 class backup_course_structure_step extends backup_structure_step {
@@ -213,16 +213,15 @@ protected function define_structure() {
         // Define each element separated
 
         $course = new backup_nested_element('course', array('id', 'contextid'), array(
-            'shortname', 'fullname', 'idnumber', 'password',
+            'shortname', 'fullname', 'idnumber',
             'summary', 'summaryformat', 'format', 'showgrades',
-            'newsitems', 'guest', 'startdate', 'enrolperiod',
+            'newsitems', 'startdate',
             'numsections', 'marker', 'maxbytes', 'showreports',
             'visible', 'hiddensections', 'groupmode', 'groupmodeforce',
-            'defaultgroupingid', 'lang', 'theme', 'cost',
-            'currency', 'timecreated', 'timemodified', 'metacourse',
-            'requested', 'restrictmodules', 'expirynotify', 'expirythreshold',
-            'notifystudents', 'enrollable', 'enrolstartdate', 'enrolenddate',
-            'enrol', 'defaultrole', 'enablecompletion'));
+            'defaultgroupingid', 'lang', 'theme',
+            'timecreated', 'timemodified',
+            'requested', 'restrictmodules',
+            'enablecompletion'));
 
         $category = new backup_nested_element('category', array('id'), array(
             'name', 'description'));
@@ -272,7 +271,6 @@ protected function define_structure() {
 
         // Some annotations
 
-        $course->annotate_ids('role', 'defaultrole');
         $course->annotate_ids('grouping', 'defaultgroupingid');
 
         $course->annotate_files(array('course_summary', 'course_content'), null);
@@ -307,8 +305,7 @@ protected function define_structure() {
         $assignments = new backup_nested_element('role_assignments');
 
         $assignment = new backup_nested_element('assignment', array('id'), array(
-            'roleid', 'userid', 'hidden', 'timestart',
-            'timeend', 'timemodified', 'modifierid', 'enrol',
+            'roleid', 'userid', 'timemodified', 'modifierid', 'component', //TODO: MDL-22793 add itemid here
             'sortorder'));
 
         // Build the tree
@@ -714,8 +711,7 @@ protected function define_structure() {
         $assignments = new backup_nested_element('role_assignments');
 
         $assignment = new backup_nested_element('assignment', array('id'), array(
-            'roleid', 'userid', 'hidden', 'timestart',
-            'timeend', 'timemodified', 'modifierid', 'enrol',
+            'roleid', 'userid', 'timemodified', 'modifierid', 'component', //TODO: MDL-22793 add itemid here
             'sortorder'));
 
         // Build the tree
diff --git a/backup/restore_check.html b/backup/restore_check.html
index 42639080c730..7ffff213f5da 100644
--- a/backup/restore_check.html
+++ b/backup/restore_check.html
@@ -92,8 +92,6 @@
         }
         //restoreto
         $restore_restoreto = required_param('restore_restoreto', PARAM_INT);
-        //restore_metacourse
-        $restore_metacourse = required_param('restore_metacourse', PARAM_INT);
         //restore_users
         $restore_users = required_param('restore_users', PARAM_INT);
 
@@ -140,7 +138,6 @@
             }
         }
         $restore->restoreto=$restore_restoreto;
-        $restore->metacourse=$restore_metacourse;
         $restore->users=$restore_users;
         $restore->groups=$restore_groups;
         $restore->logs=$restore_logs;
@@ -220,15 +217,9 @@
     //Check site
     $site = get_site();
 
-    // Non-cached - get accessinfo
-    if (isset($USER->access)) {
-        $accessinfo = $USER->access;
-    } else {
-        $accessinfo = get_user_access_sitewide($USER->id);
-    }
-
     // Get all the courses the user is able to restore to
-    $mycourses = get_user_courses_bycap($USER->id, 'moodle/restore:restorecourse', $accessinfo, true, 'c.sortorder ASC',  array('id', 'fullname', 'shortname', 'visible'));
+    //TODO: use better function which includes all courses for admins
+    $mycourses = get_user_courses_bycap($USER->id, 'moodle/restore:restorecourse');
 
     // Calculate if the user can create courses
     $cancreatecourses = user_can_create_courses();
diff --git a/backup/restore_form.html b/backup/restore_form.html
index 97f42a24d1bb..9d8a5b76a690 100644
--- a/backup/restore_form.html
+++ b/backup/restore_form.html
@@ -77,11 +77,6 @@
         }
     }
 
-    //Check other parameters
-    if (!isset($restore_metacourse)) {
-        $restore_metacourse = 1;
-    }
-
     if (!isset($restore_users)) {
         $restore_users = 1;
     }
@@ -201,14 +196,8 @@
 
     // permission should have been checked already
 
-    // Non-cached - get accessinfo
-    if (isset($USER->access)) {
-        $accessinfo = $USER->access;
-    } else {
-        $accessinfo = get_user_access_sitewide($USER->id);
-    }
-
-    $mycourses = get_user_courses_bycap($USER->id, 'moodle/restore:restorecourse', $accessinfo, true);
+    //TODO: use better function which includes all courses for admins
+    $mycourses = get_user_courses_bycap($USER->id, 'moodle/restore:restorecourse');
 
     // if the user can restore to current course, grant the "current" options
     if (has_capability('moodle/restore:restorecourse', get_context_instance(CONTEXT_COURSE, $id))){
@@ -418,22 +407,6 @@
         //Line
         echo "<tr><td colspan=\"4\">$nonrestmod<hr /></td></tr>";
 
-        //Now print the Metacourse tr
-        echo "<tr>";
-        echo "<td align=\"right\" colspan=\"2\"><b>";
-        echo '<label for="menurestore_metacourse">'.get_string ("metacourse").'</label>';
-        echo "</b></td><td colspan=\"2\">";
-        //If metacourse are in the backup file, show menu, else fixed to no
-        if ($info->backup_metacourse == "true") {
-            $metacourse_options = array();
-            $metacourse_options[0] = get_string("no");
-            $metacourse_options[1] = get_string("yes");
-            echo html_writer::select($metacourse_options, "restore_metacourse", $restore_metacourse, false);
-        } else {
-            echo get_string("no");
-            echo "<input type=\"hidden\" id=\"menurestore_metacourse\" name=\"restore_metacourse\" value=\"0\" />";
-        }
-        echo "</td></tr>";
         //Now print the Users tr
         echo "<tr>";
         echo "<td align=\"right\" colspan=\"2\"><b>";
diff --git a/backup/restorelib.php b/backup/restorelib.php
index 64d75d1406e2..a6ac3ee045e0 100644
--- a/backup/restorelib.php
+++ b/backup/restorelib.php
@@ -1555,9 +1555,7 @@ function restore_create_sections(&$restore, $xml_file) {
                 if(!isset($restore->userswhocanviewcourse)) {
                     // Because this is only used here, there is no point requesting
                     // anything except id
-                    $restore->userswhocanviewcourse=get_users_by_capability(
-                        get_context_instance(CONTEXT_COURSE, $restore->course_id),
-                        'moodle/course:participate','u.id');
+                    $restore->userswhocanviewcourse = get_enrolled_users(get_context_instance(CONTEXT_COURSE, $restore->course_id), '', 0, 'u.id');
                 }
 
                 foreach($info->completiondata as $data) {
@@ -9703,13 +9701,7 @@ function restore_user_can_roll_dates() {
             return true;
         }
 
-        // Non-cached - get accessinfo
-        if (isset($USER->access)) {
-            $accessinfo = $USER->access;
-        } else {
-            $accessinfo = get_user_access_sitewide($USER->id);
-        }
-        $courses = get_user_courses_bycap($USER->id, 'moodle/restore:rolldates', $accessinfo, true);
+        $courses = get_user_courses_bycap($USER->id, 'moodle/restore:rolldates');
         return !empty($courses);
     }
 
diff --git a/blocks/course_list/block_course_list.php b/blocks/course_list/block_course_list.php
index 3a1923a9ae17..0d52ebd7db14 100644
--- a/blocks/course_list/block_course_list.php
+++ b/blocks/course_list/block_course_list.php
@@ -36,11 +36,8 @@ function get_content() {
 
         if (empty($CFG->disablemycourses) and isloggedin() and !isguestuser() and
           !(has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM)) and $adminseesall)) {    // Just print My Courses
-            if ($courses = get_my_courses($USER->id, 'visible DESC, fullname ASC')) {
+            if ($courses = enrol_get_my_courses(NULL, 'visible DESC, fullname ASC')) {
                 foreach ($courses as $course) {
-                    if ($course->id == SITEID) {
-                        continue;
-                    }
                     $linkcss = $course->visible ? "" : " class=\"dimmed\" ";
                     $this->content->items[]="<a $linkcss title=\"" . format_string($course->shortname) . "\" ".
                                "href=\"$CFG->wwwroot/course/view.php?id=$course->id\">" . format_string($course->fullname) . "</a>";
diff --git a/blocks/course_overview/block_course_overview.php b/blocks/course_overview/block_course_overview.php
index 4c494178d1cf..a67a60352b75 100644
--- a/blocks/course_overview/block_course_overview.php
+++ b/blocks/course_overview/block_course_overview.php
@@ -65,7 +65,7 @@ public function get_content() {
             $courses_limit = $courses_limit + 1;
         }
 
-        $courses = get_my_courses($USER->id, 'visible DESC,sortorder ASC', '*', false, $courses_limit);
+        $courses = enrol_get_my_courses(NULL, 'visible DESC,sortorder ASC', '*', $courses_limit);
         $site = get_site();
         $course = $site; //just in case we need the old global $course hack
 
diff --git a/blocks/course_summary/block_course_summary.php b/blocks/course_summary/block_course_summary.php
index 6a86866576a2..94d7431ca773 100644
--- a/blocks/course_summary/block_course_summary.php
+++ b/blocks/course_summary/block_course_summary.php
@@ -27,7 +27,7 @@ function get_content() {
         $options = new object();
         $options->noclean = true;    // Don't clean Javascripts etc
         $context = get_context_instance(CONTEXT_COURSE, $this->page->course->id);
-        $this->page->course->summary = file_rewrite_pluginfile_urls($this->page->course->summary, 'pluginfile.php', $context->id, 'course_summary', $this->page->course->id);
+        $this->page->course->summary = file_rewrite_pluginfile_urls($this->page->course->summary, 'pluginfile.php', $context->id, 'course_summary', NULL);
         $this->content->text = format_text($this->page->course->summary, $this->page->course->summaryformat, $options);
         if ($this->page->user_is_editing()) {
             if($this->page->course->id == SITEID) {
diff --git a/blocks/course_summary/db/upgrade.php b/blocks/course_summary/db/upgrade.php
index 144d2335c197..d408e8400663 100644
--- a/blocks/course_summary/db/upgrade.php
+++ b/blocks/course_summary/db/upgrade.php
@@ -9,7 +9,7 @@
 //
 // The upgrade function in this file will attempt
 // to perform all the necessary actions to upgrade
-// your older installtion to the current version.
+// your older installation to the current version.
 //
 // If there's something it cannot do itself, it
 // will tell you what you need to do.
diff --git a/calendar/export_execute.php b/calendar/export_execute.php
index 757dc34a84b0..1c676be3f690 100644
--- a/calendar/export_execute.php
+++ b/calendar/export_execute.php
@@ -33,7 +33,7 @@
 
 if(!empty($what) && !empty($time)) {
     if(in_array($what, $allowed_what) && in_array($time, $allowed_time)) {
-        $courses = get_my_courses($user->id, NULL, 'id, visible, shortname');
+        $courses = enrol_get_users_courses($user->id, true, 'id, visible, shortname');
 
         if ($what == 'all') {
             $users = $user->id;
diff --git a/calendar/lib.php b/calendar/lib.php
index f8d8e644b933..d23d7384d7d3 100644
--- a/calendar/lib.php
+++ b/calendar/lib.php
@@ -1429,12 +1429,7 @@ function calendar_get_default_courses($ignoreref = false) {
         }
     }
 
-    if (isset($CFG->adminseesall)) {
-        $courses = get_my_courses($USER->id, null, null, $CFG->adminseesall);
-    }
-    else {
-        $courses = get_my_courses($USER->id, null, null, false);
-    }
+    $courses = enrol_get_my_courses();
 
     return $courses;
 }
diff --git a/calendar/view.php b/calendar/view.php
index a0bd998951e0..b455c916baad 100644
--- a/calendar/view.php
+++ b/calendar/view.php
@@ -636,7 +636,7 @@ function calendar_course_filter_selector($getvars = '') {
     if (has_capability('moodle/calendar:manageentries', get_context_instance(CONTEXT_SYSTEM)) && !empty($CFG->calendar_adminseesall)) {
         $courses = get_courses('all', 'c.shortname','c.id,c.shortname');
     } else {
-        $courses = get_my_courses($USER->id, 'shortname');
+        $courses = enrol_get_my_courses();
     }
 
     unset($courses[SITEID]);
diff --git a/cohort/edit.php b/cohort/edit.php
index 94798a1427df..d901575ce609 100644
--- a/cohort/edit.php
+++ b/cohort/edit.php
@@ -26,6 +26,7 @@
  */
 
 require('../config.php');
+require($CFG->dirroot.'/course/lib.php');
 require($CFG->dirroot.'/cohort/lib.php');
 require($CFG->dirroot.'/cohort/edit_form.php');
 
diff --git a/cohort/lib.php b/cohort/lib.php
index caffeb757dba..060c6015f52d 100644
--- a/cohort/lib.php
+++ b/cohort/lib.php
@@ -28,25 +28,56 @@
 
 /**
  * Add new cohort.
- * @param  object $data
- * @return void
+ *
+ * @param  object $cohort
+ * @return int
  */
-function cohort_add_cohort($data) {
+function cohort_add_cohort($cohort) {
     global $DB;
-    $data->timecreated = time();
-    $data->timemodified = $data->timecreated;
-    $DB->insert_record('cohort', $data);
+
+    if (!isset($cohort->name)) {
+        throw new coding_excetion('Missing cohort name in cohort_add_cohort().');
+    }
+    if (!isset($cohort->idnumber)) {
+        $cohort->idnumber = NULL;
+    }
+    if (!isset($cohort->description)) {
+        $cohort->description = $DB->sql_empty();
+    }
+    if (!isset($cohort->descriptionformat)) {
+        $cohort->descriptionformat = FORMAT_HTML;
+    }
+    if (empty($cohort->component)) {
+        $cohort->component = '';
+    }
+    if (!isset($cohort->timecreated)) {
+        $cohort->timecreated = time();
+    }
+    if (!isset($cohort->timemodified)) {
+        $cohort->timemodified = $cohort->timecreated;
+    }
+
+    $cohort->id = $DB->insert_record('cohort', $cohort);
+
+    events_trigger('cohort_added', $cohort);
+
+    return $cohort->id;
 }
 
 /**
  * Update existing cohort.
- * @param  object $data
+ * @param  object $cohort
  * @return void
  */
-function cohort_update_cohort($data) {
+function cohort_update_cohort($cohort) {
     global $DB;
-    $data->timemodified = time();
-    $DB->update_record('cohort', $data);
+    if (isset($cohort->component) and empty($cohort->component)) {
+        $cohort->component = NULL;
+    }
+    $cohort->timemodified = time();
+    $DB->update_record('cohort', $cohort);
+
+    events_trigger('cohort_updated', $cohort);
 }
 
 /**
@@ -63,6 +94,8 @@ function cohort_delete_cohort($cohort) {
 
     $DB->delete_records('cohort_members', array('cohortid'=>$cohort->id));
     $DB->delete_records('cohort', array('id'=>$cohort->id));
+
+    events_trigger('cohort_deleted', $cohort);
 }
 
 /**
@@ -104,6 +137,8 @@ function cohort_add_member($cohortid, $userid) {
     $record->userid    = $userid;
     $record->timeadded = time();
     $DB->insert_record('cohort_members', $record);
+
+    events_trigger('cohort_member_added', (object)array('cohortid'=>$cohortid, 'userid'=>$userid));
 }
 
 /**
@@ -115,6 +150,8 @@ function cohort_add_member($cohortid, $userid) {
 function cohort_remove_member($cohortid, $userid) {
     global $DB;
     $DB->delete_records('cohort_members', array('cohortid'=>$cohortid, 'userid'=>$userid));
+
+    events_trigger('cohort_member_removed', (object)array('cohortid'=>$cohortid, 'userid'=>$userid));
 }
 
 /**
@@ -268,7 +305,7 @@ public function find_users($search) {
 
         return array($groupname => $availableusers);
     }
-    
+
     protected function get_options() {
         $options = parent::get_options();
         $options['cohortid'] = $this->cohortid;
diff --git a/course/category.php b/course/category.php
index 65db242d9cbd..2b9128ab41dd 100644
--- a/course/category.php
+++ b/course/category.php
@@ -112,9 +112,8 @@
             if ($course) {
                 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
                 require_capability('moodle/course:visibility', $coursecontext);
-                if (!$DB->set_field('course', 'visible', $visible, array('id' => $course->id))) {
-                    print_error('errorupdatingcoursevisibility');
-                }
+                $DB->set_field('course', 'visible', $visible, array('id' => $course->id));
+                $DB->set_field('course', 'visibleold', $visible, array('id' => $course->id)); // we set the old flag when user manually changes visibility of course
             }
         }
 
@@ -244,7 +243,7 @@
 
 /// Print out all the courses
     $courses = get_courses_page($category->id, 'c.sortorder ASC',
-            'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible,c.guest,c.password',
+            'c.id,c.sortorder,c.shortname,c.fullname,c.summary,c.visible',
             $totalcount, $page*$perpage, $perpage);
     $numcourses = count($courses);
 
@@ -274,7 +273,6 @@
         $strshow = get_string('show');
         $strsummary = get_string('summary');
         $strsettings = get_string('settings');
-        $strassignteachers = get_string('assignteachers');
         $strallowguests = get_string('allowguests');
         $strrequireskey = get_string('requireskey');
 
@@ -401,16 +399,7 @@
                 echo '</td>';
             } else {
                 echo '<td align="right">';
-                if (!empty($acourse->guest)) {
-                    echo '<a href="view.php?id='.$acourse->id.'"><img title="'.
-                         $strallowguests.'" class="icon" src="'.
-                         $OUTPUT->pix_url('i/guest') . '" alt="'.$strallowguests.'" /></a>';
-                }
-                if (!empty($acourse->password)) {
-                    echo '<a href="view.php?id='.$acourse->id.'"><img title="'.
-                         $strrequireskey.'" class="icon" src="'.
-                         $OUTPUT->pix_url('i/key') . '" alt="'.$strrequireskey.'" /></a>';
-                }
+                //TODO: show some icons for plugins - such as guest, pasword, etc.
                 if (!empty($acourse->summary)) {
                     $link = new moodle_url("/course/info.php?id=$acourse->id");
                     echo $OUTPUT->action_link($link, '<img alt="'.get_string('info').'" class="icon" src="'.$OUTPUT->pix_url('i/info') . '" />',
diff --git a/course/edit.php b/course/edit.php
index 2d8a045702ef..17882a80072d 100644
--- a/course/edit.php
+++ b/course/edit.php
@@ -1,165 +1,159 @@
 <?php
-      // Edit course settings
 
-    require_once('../config.php');
-    require_once($CFG->dirroot.'/enrol/enrol.class.php');
-    require_once('lib.php');
-    require_once('edit_form.php');
-
-    $id         = optional_param('id', 0, PARAM_INT);       // course id
-    $categoryid = optional_param('category', 0, PARAM_INT); // course category - can be changed in edit form
-
-    $PAGE->set_pagelayout('admin');
-
-/// basic access control checks
-    if ($id) { // editing course
-
-        if($id == SITEID){
-            // don't allow editing of  'site course' using this from
-            print_error('cannoteditsiteform');
-        }
-
-        if (!$course = $DB->get_record('course', array('id'=>$id))) {
-            print_error('invalidcourseid');
-        }
-        require_login($course->id);
-        $category = $DB->get_record('course_categories', array('id'=>$course->category));
-        $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
-        require_capability('moodle/course:update', $coursecontext);
-    } else if ($categoryid) { // creating new course in this category
-        $course = null;
-        require_login();
-        if (!$category = $DB->get_record('course_categories', array('id'=>$categoryid))) {
-            print_error('unknowcategory');
-        }
-        require_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $category->id));
-    } else {
-        require_login();
-        print_error('needcoursecategroyid');
-    }
-
-    $PAGE->set_url('/course/edit.php');
-    if ($id !== 0) {
-        $PAGE->url->param('id',$id);
-    } else {
-        $PAGE->url->param('category',$categoryid);
+// 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/>.
+
+/**
+ * Edit course settings
+ *
+ * @package    moodlecore
+ * @copyright  1999 onwards Martin Dougiamas (http://dougiamas.com)
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require_once('../config.php');
+require_once('lib.php');
+require_once('edit_form.php');
+
+$id         = optional_param('id', 0, PARAM_INT);       // course id
+$categoryid = optional_param('category', 0, PARAM_INT); // course category - can be changed in edit form
+
+$PAGE->set_pagelayout('admin');
+$PAGE->set_url('/course/edit.php');
+
+// basic access control checks
+if ($id) { // editing course
+    if ($id == SITEID){
+        // don't allow editing of  'site course' using this from
+        print_error('cannoteditsiteform');
     }
 
-    /// Prepare course and the editor
-    $editoroptions = array('maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
-    if (!empty($course)) {
-        $allowedmods = array();
-        if (!empty($course)) {
-            if ($am = $DB->get_records('course_allowed_modules', array('course'=>$course->id))) {
-                foreach ($am as $m) {
-                    $allowedmods[] = $m->module;
-                }
-            } else {
-                if (empty($course->restrictmodules)) {
-                    $allowedmods = explode(',',$CFG->defaultallowedmodules);
-                } // it'll be greyed out but we want these by default anyway.
-            }
-            $course->allowedmods = $allowedmods;
+    $course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+    require_login($course->id);
+    $category = $DB->get_record('course_categories', array('id'=>$course->category), '*', MUST_EXIST);
+    $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+    require_capability('moodle/course:update', $coursecontext);
+    $PAGE->url->param('id',$id);
+
+} else if ($categoryid) { // creating new course in this category
+    $course = null;
+    require_login();
+    $category = $DB->get_record('course_categories', array('id'=>$categoryid), '*', MUST_EXIST);
+    require_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $category->id));
+    $PAGE->url->param('category',$categoryid);
+
+} else {
+    require_login();
+    print_error('needcoursecategroyid');
+}
+
+// Prepare course and the editor
+$editoroptions = array('maxfiles' => EDITOR_UNLIMITED_FILES, 'maxbytes'=>$CFG->maxbytes, 'trusttext'=>false, 'noclean'=>true);
+if (!empty($course)) {
+    $allowedmods = array();
+    if ($am = $DB->get_records('course_allowed_modules', array('course'=>$course->id))) {
+        foreach ($am as $m) {
+            $allowedmods[] = $m->module;
         }
-        $course = file_prepare_standard_editor($course, 'summary', $editoroptions, $coursecontext, 'course_summary', $course->id);
     } else {
-        $course = file_prepare_standard_editor($course, 'summary', $editoroptions, null, 'course_summary', null);
+        // this happens in case we edit course created before enabling module restrictions or somebody disabled everything :-(
+        if (empty($course->restrictmodules) and !empty($CFG->defaultallowedmodules)) {
+            $allowedmods = explode(',', $CFG->defaultallowedmodules);
+        }
     }
+    $course->allowedmods = $allowedmods;
+    $course = file_prepare_standard_editor($course, 'summary', $editoroptions, $coursecontext, 'course_summary', 0);
 
-/// first create the form
-    $editform = new course_edit_form('edit.php', compact('course', 'category', 'editoroptions'));
-    // now override defaults if course already exists
-    if (!empty($course->id)) {
-        $course->enrolpassword = $course->password; // we need some other name for password field MDL-9929
-        $editform->set_data($course);
-    }
-    if ($editform->is_cancelled()){
-        if (empty($course)) {
-            redirect($CFG->wwwroot);
-        } else {
-            redirect($CFG->wwwroot.'/course/view.php?id='.$course->id);
-        }
+} else {
+    $course = file_prepare_standard_editor($course, 'summary', $editoroptions, null, 'course_summary', null);
+}
 
-    } else if ($data = $editform->get_data()) {
+// first create the form
+$editform = new course_edit_form(NULL, array('course'=>$course, 'category'=>$category, 'editoroptions'=>$editoroptions));
 
-        $data->password = $data->enrolpassword;  // we need some other name for password field MDL-9929
-/// process data if submitted
+if ($editform->is_cancelled()) {
+    if (empty($course)) {
+        redirect($CFG->wwwroot.'/');
+    } else {
+        redirect($CFG->wwwroot.'/course/view.php?id='.$course->id);
+    }
 
-        //preprocess data
-        $data->timemodified = time();
+} else if ($data = $editform->get_data()) {
+    // process data if submitted
 
-        if (empty($course->id)) {
-            // In creating the course
-            if (!$course = create_course($data)) {
-                print_error('coursenotcreated');
-            }
+    if (empty($course->id)) {
+        // In creating the course
+        $course = create_course($data, $editoroptions);
 
-            // Get the context of the newly created course
-            $context = get_context_instance(CONTEXT_COURSE, $course->id);
+        // Get the context of the newly created course
+        $context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
 
-            // Save the files used in the summary editor
-            $editordata = new stdClass;
-            $editordata->id = $course->id;
-            $editordata->summary_editor = $data->summary_editor;
-            $editordata = file_postupdate_standard_editor($editordata, 'summary', $editoroptions, $context, 'course_summary', $course->id);
-            $DB->update_record('course', $editordata);
+        // try to deal with course creators - enrol them internally with default role
+        if (!empty($CFG->creatornewroleid) and !is_viewing($context, NULL, 'moodle/role:assign') and !is_enrolled($context, NULL, 'moodle/role:assign')) {
+            enrol_try_internal_enrol($course->id, $USER->id, $CFG->creatornewroleid);
+        }
 
-            // assign default role to creator if not already having permission to manage course assignments
-            if (!is_viewing($context, NULL, 'moodle/role:assign') and !is_enrolled($context, NULL, 'moodle/role:assign')) {
-                role_assign($CFG->creatornewroleid, $USER->id, 0, $context->id);
+        // Redirect to manual enrolment page if possible
+        $instances = enrol_get_instances($course->id, true);
+        foreach($instances as $instance) {
+            if ($plugin = enrol_get_plugin($instance->enrol)) {
+                if ($link = $plugin->get_manual_enrol_link($instance)) {
+                    redirect($link);
+                }
             }
+        }
 
-            // ensure we can use the course right after creating it
-            // this means trigger a reload of accessinfo...
-            mark_context_dirty($context->path);
+        redirect($CFG->wwwroot."/course/view.php?id=$course->id");
 
-            if ($data->metacourse and has_capability('moodle/course:managemetacourse', $context)) {
-                // Redirect users with metacourse capability to student import
-                redirect($CFG->wwwroot."/course/importstudents.php?id=$course->id");
-            } else {
-                // Redirect to roles assignment
-                redirect($CFG->wwwroot."/$CFG->admin/roles/assign.php?contextid=$context->id");
-            }
-
-        } else {
-            // Save any changes to the files used in the editor
-            $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $coursecontext, 'course_summary', $data->id);
-            if (!update_course($data)) {
-                print_error('coursenotupdated');
-            }
-            redirect($CFG->wwwroot."/course/view.php?id=$course->id");
-        }
+    } else {
+        // Save any changes to the files used in the editor
+        update_course($data, $editoroptions);
+        redirect($CFG->wwwroot."/course/view.php?id=$course->id");
     }
+}
 
 
-/// Print the form
+// Print the form
 
-    $site = get_site();
+$site = get_site();
 
-    $streditcoursesettings = get_string("editcoursesettings");
-    $straddnewcourse = get_string("addnewcourse");
-    $stradministration = get_string("administration");
-    $strcategories = get_string("categories");
+$streditcoursesettings = get_string("editcoursesettings");
+$straddnewcourse = get_string("addnewcourse");
+$stradministration = get_string("administration");
+$strcategories = get_string("categories");
 
-    if (!empty($course->id)) {
-        $PAGE->navbar->add($streditcoursesettings);
-        $title = $streditcoursesettings;
-        $fullname = $course->fullname;
-    } else {
-        $PAGE->navbar->add($stradministration, new moodle_url('/admin/index.php'));
-        $PAGE->navbar->add($strcategories, new moodle_url('/course/index.php'));
-        $PAGE->navbar->add($straddnewcourse);
-        $title = "$site->shortname: $straddnewcourse";
-        $fullname = $site->fullname;
-    }
+if (!empty($course->id)) {
+    $PAGE->navbar->add($streditcoursesettings);
+    $title = $streditcoursesettings;
+    $fullname = $course->fullname;
+} else {
+    $PAGE->navbar->add($stradministration, new moodle_url('/admin/index.php'));
+    $PAGE->navbar->add($strcategories, new moodle_url('/course/index.php'));
+    $PAGE->navbar->add($straddnewcourse);
+    $title = "$site->shortname: $straddnewcourse";
+    $fullname = $site->fullname;
+}
+
+$PAGE->set_title($title);
+$PAGE->set_heading($fullname);
+$PAGE->set_focuscontrol($editform->focus());
 
-    $PAGE->set_title($title);
-    $PAGE->set_heading($fullname);
-    $PAGE->set_focuscontrol($editform->focus());
-    echo $OUTPUT->header();
-    echo $OUTPUT->heading($streditcoursesettings);
+echo $OUTPUT->header();
+echo $OUTPUT->heading($streditcoursesettings);
 
-    $editform->display();
+$editform->display();
 
-    echo $OUTPUT->footer();
+echo $OUTPUT->footer();
 
diff --git a/course/edit_form.php b/course/edit_form.php
index b3ee3e293f61..f69f1b203146 100644
--- a/course/edit_form.php
+++ b/course/edit_form.php
@@ -1,83 +1,73 @@
 <?php
 
-if (!defined('MOODLE_INTERNAL')) {
-    die('Direct access to this script is forbidden.');    ///  It must be included from a Moodle page
-}
+defined('MOODLE_INTERNAL') || die;
 
 require_once($CFG->libdir.'/formslib.php');
 
 class course_edit_form extends moodleform {
+    protected $course;
+    protected $context;
 
     function definition() {
         global $USER, $CFG, $DB;
 
-        $courseconfig = get_config('moodlecourse');
-        $mform    =& $this->_form;
+        $mform    = $this->_form;
 
-        $course   = $this->_customdata['course'];
-        $category = $this->_customdata['category'];
+        $course        = $this->_customdata['course']; // this contains the data of this form
+        $category      = $this->_customdata['category'];
         $editoroptions = $this->_customdata['editoroptions'];
 
-        $systemcontext = get_context_instance(CONTEXT_SYSTEM);
+        $systemcontext   = get_context_instance(CONTEXT_SYSTEM);
         $categorycontext = get_context_instance(CONTEXT_COURSECAT, $category->id);
 
-        $disable_meta = false; // basic meta course state protection; server-side security checks not needed
         if (!empty($course->id)) {
             $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
             $context = $coursecontext;
-
-            if (course_in_meta($course)) {
-                $disable_meta = get_string('metaalreadyinmeta');
-
-            } else if ($course->metacourse) {
-                if ($DB->count_records('course_meta', array('parent_course'=>$course->id)) > 0) {
-                    $disable_meta = get_string('metaalreadyhascourses');
-                }
-
-            } else {
-                // if users already enrolled directly into coures, do not allow switching to meta,
-                // users with metacourse manage permission are exception
-                // please note that we do not need exact results - anything unexpected here prevents metacourse
-                $managers = get_users_by_capability($coursecontext, 'moodle/course:managemetacourse', 'u.id');
-                $enrolroles = get_roles_with_capability('moodle/course:participate', CAP_ALLOW, $coursecontext);
-                if ($users = get_role_users(array_keys($enrolroles), $coursecontext, false, 'u.id', 'u.id ASC')) {
-                    foreach($users as $user) {
-                        if (!isset($managers[$user->id])) {
-                            $disable_meta = get_string('metaalreadyhasenrolments');
-                            break;
-                        }
-                    }
-                }
-                unset($managers);
-                unset($users);
-                unset($enrolroles);
-            }
         } else {
             $coursecontext = null;
             $context = $categorycontext;
         }
 
+        $courseconfig = get_config('moodlecourse');
+
+        $this->course  = $course;
+        $this->context = $context;
+
 /// form definition with new course defaults
 //--------------------------------------------------------------------------------
         $mform->addElement('header','general', get_string('general', 'form'));
 
-        // Must have create course capability in both categories in order to move course
-        if (has_capability('moodle/course:create', $categorycontext)) {
-            $displaylist = array();
-            $parentlist = array();
-            make_categories_list($displaylist, $parentlist, 'moodle/course:create');
-            $mform->addElement('select', 'category', get_string('category'), $displaylist);
+        // verify permissions to change course category or keep current
+        if (empty($course->id)) {
+            if (has_capability('moodle/course:create', $categorycontext)) {
+                $displaylist = array();
+                $parentlist = array();
+                make_categories_list($displaylist, $parentlist, 'moodle/course:create');
+                $mform->addElement('select', 'category', get_string('category'), $displaylist);
+                $mform->addHelpButton('category', 'category');
+                $mform->setDefault('category', $category->id);
+            } else {
+                $mform->addElement('hidden', 'category', null);
+                $mform->setType('category', PARAM_INT);
+                $mform->setConstant('category', $category->id);
+            }
         } else {
-            $mform->addElement('hidden', 'category', null);
-            $mform->setType('category', PARAM_INT);
-        }
-        $mform->addHelpButton('category', 'category');
-        $mform->setDefault('category', $category->id);
-        $mform->setType('category', PARAM_INT);
-
-        if (!empty($course->id) and !has_capability('moodle/course:changecategory', $coursecontext)) {
-            $mform->hardFreeze('category');
-            $mform->setConstant('category', $category->id);
+            if (has_capability('moodle/course:changecategory', $coursecontext)) {
+                $displaylist = array();
+                $parentlist = array();
+                make_categories_list($displaylist, $parentlist, 'moodle/course:create');
+                if (!isset($displaylist[$course->category])) {
+                    //always keep current
+                    $displaylist[$course->category] = format_string($DB->get_field('course_categories', 'name', array('id'=>$course->category)));
+                }
+                $mform->addElement('select', 'category', get_string('category'), $displaylist);
+                $mform->addHelpButton('category', 'category');
+            } else {
+                //keep current
+                $mform->addElement('hidden', 'category', null);
+                $mform->setType('category', PARAM_INT);
+                $mform->setConstant('category', $course->category);
+            }
         }
 
         $mform->addElement('text','fullname', get_string('fullnamecourse'),'maxlength="254" size="50"');
@@ -175,106 +165,8 @@ function definition() {
             $mform->addElement('select', 'theme', get_string('forcetheme'), $themes);
         }
 
-        $meta=array();
-        $meta[0] = get_string('no');
-        $meta[1] = get_string('yes');
-        if ($disable_meta === false) {
-            $mform->addElement('select', 'metacourse', get_string('managemeta'), $meta);
-            $mform->addHelpButton('metacourse', 'managemeta');
-            $mform->setDefault('metacourse', $courseconfig->metacourse);
-        } else {
-            // no metacourse element - we do not want to change it anyway!
-            $mform->addElement('static', 'nometacourse', get_string('managemeta'),
-                ((empty($course->metacourse)) ? $meta[0] : $meta[1]) . " - $disable_meta ");
-            $mform->addHelpButton('nometacourse', 'managemeta');
-        }
-
 //--------------------------------------------------------------------------------
-        $mform->addElement('header','enrolhdr', get_string('enrolments'));
-
-        $choices = array();
-        $modules = explode(',', $CFG->enrol_plugins_enabled);
-        foreach ($modules as $module) {
-            $name = get_string('enrolname', "enrol_$module");
-            $plugin = enrolment_factory::factory($module);
-            if (method_exists($plugin, 'print_entry')) {
-                $choices[$name] = $module;
-            }
-        }
-        asort($choices);
-        $choices = array_flip($choices);
-        $choices = array_merge(array('' => get_string('sitedefault').' ('.get_string('enrolname', "enrol_$CFG->enrol").')'), $choices);
-        $mform->addElement('select', 'enrol', get_string('enrolmentplugins'), $choices);
-        $mform->addHelpButton('enrol', 'enrolmentplugins');
-        $mform->setDefault('enrol', $courseconfig->enrol);
-
-
-        $roles = get_assignable_roles($context);
-        if (!empty($course->id)) {
-            // add current default role, so that it is selectable even when user can not assign it
-            if ($current_role = $DB->get_record('role', array('id'=>$course->defaultrole))) {
-                $roles[$current_role->id] = strip_tags(format_string($current_role->name, true));
-            }
-        }
-        $choices = array();
-        if ($sitedefaultrole = $DB->get_record('role', array('id'=>$CFG->defaultcourseroleid))) {
-            $choices[0] = get_string('sitedefault').' ('.$sitedefaultrole->name.')';
-        } else {
-            $choices[0] = get_string('sitedefault');
-        }
-        $choices = $choices + $roles;
-
-        // fix for MDL-9197
-        foreach ($choices as $choiceid => $choice) {
-            $choices[$choiceid] = format_string($choice);
-        }
-
-        $mform->addElement('select', 'defaultrole', get_string('defaultrole', 'role'), $choices);
-        $mform->setDefault('defaultrole', 0);
-
-
-        $radio = array();
-        $radio[] = &MoodleQuickForm::createElement('radio', 'enrollable', null, get_string('no'), 0);
-        $radio[] = &MoodleQuickForm::createElement('radio', 'enrollable', null, get_string('yes'), 1);
-        $radio[] = &MoodleQuickForm::createElement('radio', 'enrollable', null, get_string('enroldate'), 2);
-        $mform->addGroup($radio, 'enrollable', get_string('enrollable'), ' ', false);
-        $mform->addHelpButton('enrollable', 'enrollable');
-        $mform->setDefault('enrollable', $courseconfig->enrollable);
-
-        $mform->addElement('date_selector', 'enrolstartdate', get_string('enrolstartdate'), array('optional' => true));
-        $mform->setDefault('enrolstartdate', 0);
-        $mform->disabledIf('enrolstartdate', 'enrollable', 'neq', 2);
-
-        $mform->addElement('date_selector', 'enrolenddate', get_string('enrolenddate'), array('optional' => true));
-        $mform->setDefault('enrolenddate', 0);
-        $mform->disabledIf('enrolenddate', 'enrollable', 'neq', 2);
-
-        $mform->addElement('duration', 'enrolperiod', get_string('enrolperiod'), array('optional' => true, 'defaultunit' => 86400));
-        $mform->setDefault('enrolperiod', $courseconfig->enrolperiod);
-
-
-//--------------------------------------------------------------------------------
-        $mform->addElement('header','expirynotifyhdr', get_string('expirynotify'));
-
-        $choices = array();
-        $choices['0'] = get_string('no');
-        $choices['1'] = get_string('yes');
-        $mform->addElement('select', 'expirynotify', get_string('notify'), $choices);
-        $mform->addHelpButton('expirynotify', 'notify');
-        $mform->setDefault('expirynotify', $courseconfig->expirynotify);
-
-        $mform->addElement('select', 'notifystudents', get_string('expirynotifystudents'), $choices);
-        $mform->addHelpButton('notifystudents', 'expirynotifystudents');
-        $mform->setDefault('notifystudents', $courseconfig->notifystudents);
-
-        $thresholdmenu=array();
-        for ($i=1; $i<=30; $i++) {
-            $seconds = $i * 86400;
-            $thresholdmenu[$seconds] = get_string('numdays', '', $i);
-        }
-        $mform->addElement('select', 'expirythreshold', get_string('expirythreshold'), $thresholdmenu);
-        $mform->addHelpButton('expirythreshold', 'expirythreshold');
-        $mform->setDefault('expirythreshold', $courseconfig->expirythreshold);
+        enrol_course_edit_form($mform, $course, $context);
 
 //--------------------------------------------------------------------------------
         $mform->addElement('header','', get_string('groups', 'group'));
@@ -313,48 +205,6 @@ function definition() {
             $mform->setConstant('visible', $course->visible);
         }
 
-        $mform->addElement('passwordunmask', 'enrolpassword', get_string('enrolmentkey'), 'size="25"');
-        $mform->setDefault('enrolpassword', '');
-        $mform->setDefault('enrolpassword', $courseconfig->enrolpassword);
-        $mform->setType('enrolpassword', PARAM_RAW);
-
-        if (empty($course->id) or ($course->password !== '' and $course->id != SITEID)) {
-            // do not require password in existing courses that do not have password yet - backwards compatibility ;-)
-            if (!empty($CFG->enrol_manual_requirekey)) {
-                $mform->addRule('enrolpassword', get_string('required'), 'required', null, 'client');
-            }
-        }
-
-        $choices = array();
-        $choices['0'] = get_string('guestsno');
-        $choices['1'] = get_string('guestsyes');
-        $choices['2'] = get_string('guestskey');
-        $mform->addElement('select', 'guest', get_string('opentoguests'), $choices);
-        $mform->setDefault('guest', $courseconfig->guest);
-
-        // If we are creating a course, its enrol method isn't yet chosen, BUT the site has a default enrol method which we can use here
-        $enrol_object = $CFG;
-        if (!empty($course->id)) {
-            $enrol_object = $course;
-        }
-        // If the print_entry method exists and the course enrol method isn't manual (both set or inherited from site), show cost
-        if (method_exists(enrolment_factory::factory($enrol_object->enrol), 'print_entry') && !($enrol_object->enrol == 'manual' || (empty($enrol_object->enrol) && $CFG->enrol == 'manual'))) {
-            $costgroup=array();
-            $currencies = get_string_manager()->get_list_of_currencies();
-            $costgroup[]= &MoodleQuickForm::createElement('text','cost', '', 'maxlength="6" size="6"');
-            $costgroup[]= &MoodleQuickForm::createElement('select', 'currency', '', $currencies);
-            $mform->addGroup($costgroup, 'costgrp', get_string('cost'), '&nbsp;', false);
-            //defining a rule for a form element within a group :
-            $costgrprules=array();
-            //set the message to null to tell Moodle to use a default message
-            //available for most rules, fetched from language pack (err_{rulename}).
-            $costgrprules['cost'][]=array(null, 'numeric', null, 'client');
-            $mform->addGroupRule('costgrp',$costgrprules);
-            $mform->setDefault('cost', '');
-            $mform->setDefault('currency', empty($CFG->enrol_currency) ? 'USD' : $CFG->enrol_currency);
-
-        }
-
 //--------------------------------------------------------------------------------
         $mform->addElement('header','', get_string('language'));
 
@@ -386,31 +236,38 @@ function definition() {
         }
 
 //--------------------------------------------------------------------------------
-        if (has_capability('moodle/site:config', $systemcontext) && ((!empty($course->requested) && $CFG->restrictmodulesfor == 'requested') || $CFG->restrictmodulesfor == 'all')) {
-            $mform->addElement('header', '', get_string('restrictmodules'));
-
-            $options = array();
-            $options['0'] = get_string('no');
-            $options['1'] = get_string('yes');
-            $mform->addElement('select', 'restrictmodules', get_string('restrictmodules'), $options);
-            $mods = array(0=>get_string('allownone'));
-            $mods += $DB->get_records_menu('modules', array(), 'name', 'id, name');
-
+        if (has_capability('moodle/site:config', $systemcontext)) {
+            if (((!empty($course->requested) && $CFG->restrictmodulesfor == 'requested') || $CFG->restrictmodulesfor == 'all')) {
+                $mform->addElement('header', '', get_string('restrictmodules'));
+
+                $options = array();
+                $options['0'] = get_string('no');
+                $options['1'] = get_string('yes');
+                $mform->addElement('select', 'restrictmodules', get_string('restrictmodules'), $options);
+                if (!empty($CFG->restrictbydefault)) {
+                    $mform->setDefault('restrictmodules', 1);
+                }
 
-            $mform->addElement('select', 'allowedmods', get_string('to'), $mods,
-                            array('multiple'=>'multiple', 'size'=>'10'));
-            $mform->disabledIf('allowedmods', 'restrictmodules', 'eq', 0);
+                $mods = array(0=>get_string('allownone'));
+                $mods += $DB->get_records_menu('modules', array('visible'=>1), 'name', 'id, name');
+                $mform->addElement('select', 'allowedmods', get_string('to'), $mods, array('multiple'=>'multiple', 'size'=>'10'));
+                $mform->disabledIf('allowedmods', 'restrictmodules', 'eq', 0);
+                // defaults are already in $course
+            } else {
+                // remove any mod restriction
+                $mform->addElement('hidden', 'restrictmodules', 0);
+                $mform->setType('restrictmodules', PARAM_INT);
+            }
         } else {
-            $mform->addElement('hidden', 'restrictmodules', null);
+            $mform->addElement('hidden', 'restrictmodules');
             $mform->setType('restrictmodules', PARAM_INT);
-        }
-        if ($CFG->restrictmodulesfor == 'all') {
-            $mform->setDefault('allowedmods', explode(',',$CFG->defaultallowedmodules));
-            if (!empty($CFG->restrictbydefault)) {
-                $mform->setDefault('restrictmodules', 1);
+            if (empty($course->id)) {
+                $mform->setConstant('restrictmodules', (int)($CFG->restrictmodulesfor == 'all'));
+            } else {
+                // keep previous
+                $mform->setConstant('restrictmodules', $course->restrictmodules);
             }
         }
-        $mform->setType('restrictmodules', PARAM_INT);
 
 /// customizable role names in this course
 //--------------------------------------------------------------------------------
@@ -439,14 +296,18 @@ function definition() {
 //--------------------------------------------------------------------------------
         $mform->addElement('hidden', 'id', null);
         $mform->setType('id', PARAM_INT);
+
+/// finally set the current form data
+//--------------------------------------------------------------------------------
+        $this->set_data($course);
     }
 
     function definition_after_data() {
         global $DB;
 
-        $mform =& $this->_form;
+        $mform = $this->_form;
 
-        // add availabe groupings
+        // add available groupings
         if ($courseid = $mform->getElementValue('id') and $mform->elementExists('defaultgroupingid')) {
             $options = array();
             if ($groupings = $DB->get_records('groupings', array('courseid'=>$courseid))) {
@@ -478,21 +339,7 @@ function validation($data, $files) {
             }
         }
 
-        if (!empty($data['enrolstartdate']) && !empty($data['enrolenddate']) &&
-                $data['enrolenddate'] <= $data['enrolstartdate']){
-            $errors['enrolenddate'] = get_string('enrolenddaterror');
-        }
-
-        if (!empty($CFG->enrol_manual_usepasswordpolicy) and isset($data['enrolpassword']) and $data['enrolpassword'] != '') {
-            $course = $this->_customdata['course'];
-            if ($course->password !== $data['enrolpassword']) {
-                // enforce password policy only if changing password - backwards compatibility
-                $errmsg = '';
-                if (!check_password_policy($data['enrolpassword'], $errmsg)) {
-                    $errors['enrolpassword'] = $errmsg;
-                }
-            }
-        }
+        $errors = array_merge($errors, enrol_course_edit_validation($data, $this->context));
 
         return $errors;
     }
diff --git a/course/enrol.php b/course/enrol.php
index f7808982a8d6..aceb88b9555f 100644
--- a/course/enrol.php
+++ b/course/enrol.php
@@ -16,118 +16,15 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * Depending on the current enrolment method, this page
- * presents the user with whatever they need to know when
- * they try to enrol in a course.
+ * Redirection of old enrol entry point.
  *
  * @copyright 1999 Martin Dougiamas  http://dougiamas.com
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  * @package course
  */
 
-require_once("../config.php");
-require_once("lib.php");
-require_once("$CFG->dirroot/enrol/enrol.class.php");
+require('../config.php');
 
-$id           = required_param('id', PARAM_INT);
-$loginasguest = optional_param('loginasguest', 0, PARAM_BOOL); // hmm, is this still needed?
-
-$url = new moodle_url('/course/enrol.php', array('id'=>$id));
-if ($loginasguest !== 0) {
-    $url->param('loginasguest', $loginasguest);
-}
-$PAGE->set_url($url);
-
-if (!isloggedin()) {
-    // do not use require_login here because we are usually comming from it
-    redirect(get_login_url());
-}
-
-if (!$course = $DB->get_record('course', array('id'=>$id))) {
-    print_error("That's an invalid course id");
-}
-
-if (! $context = get_context_instance(CONTEXT_COURSE, $course->id) ) {
-    print_error("That's an invalid course id");
-}
-
-/// do not use when in course login as
-if (session_is_loggedinas() and $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
-    print_error('loginasnoenrol', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid);
-}
-
-$enrol = enrolment_factory::factory($course->enrol); // do not use if (!$enrol... here, it can not work in PHP4 - see MDL-7529
-
-/// Refreshing all current role assignments for the current user
-
-load_all_capabilities();
-
-/// Double check just in case they are actually enrolled already and
-/// thus got to this script by mistake.  This might occur if enrolments
-/// changed during this session or something
-
-if (has_capability('moodle/course:participate', $context)) {
-    if (!empty($SESSION->wantsurl)) {
-        $destination = $SESSION->wantsurl;
-        unset($SESSION->wantsurl);
-    } else {
-        $destination = "$CFG->wwwroot/course/view.php?id=$course->id";
-    }
-    redirect($destination);   // Bye!
-}
-
-/// Check if the course is a meta course  (bug 5734)
-if ($course->metacourse) {
-    echo $OUTPUT->header();
-    notice(get_string('coursenotaccessible'), "$CFG->wwwroot/index.php");
-}
-
-/// Users can't enroll to site course
-if ($course->id == SITEID) {
-    echo $OUTPUT->header();
-    notice(get_string('enrollfirst'), "$CFG->wwwroot/index.php");
-}
-
-/// Double check just in case they are enrolled to start in the future
-
-if ($course->enrolperiod) {   // Only active if the course has an enrolment period in effect
-    if ($roles = get_user_roles($context, $USER->id)) {
-        foreach ($roles as $role) {
-            if ($role->timestart and ($role->timestart >= time())) {
-                $message = get_string('enrolmentnotyet', '', userdate($student->timestart));
-                echo $OUTPUT->header();
-                notice($message, "$CFG->wwwroot/index.php");
-            }
-        }
-    }
-}
-
-/// Check if the course is enrollable
-if (!method_exists($enrol, 'print_entry')) {
-    echo $OUTPUT->header();
-    notice(get_string('enrolmentnointernal'), "$CFG->wwwroot/index.php");
-}
-
-if (!$course->enrollable ||
-        ($course->enrollable == 2 && $course->enrolstartdate > 0 && $course->enrolstartdate > time()) ||
-        ($course->enrollable == 2 && $course->enrolenddate > 0 && $course->enrolenddate <= time())
-        ) {
-    $PAGE->set_title($course->shortname);
-    $PAGE->set_heading($course->fullname);
-    $PAGE->navbar->add($course->shortname);
-    echo $OUTPUT->header();
-    notice(get_string('notenrollable'), "$CFG->wwwroot/index.php");
-}
-
-/// Check the submitted enrolment information if there is any (eg could be enrolment key)
-
-if ($form = data_submitted()) {
-    $enrol->check_entry($form, $course);   // Should terminate/redirect in here if it's all OK
-}
-
-/// Otherwise, we print the entry form.
-
-$enrol->print_entry($course);
-
-/// Easy!
+$id = required_param('id', PARAM_INT);
 
+redirect(new moodle_url('/enrol/index.php', array('id'=>$id)));
diff --git a/course/external.php b/course/external.php
deleted file mode 100644
index 660f6f81d7f3..000000000000
--- a/course/external.php
+++ /dev/null
@@ -1,749 +0,0 @@
-<?php
-/**
- * Moodle - Modular Object-Oriented Dynamic Learning Environment
- *         http://moodle.com
- *
- * LICENSE
- *
- * This program 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 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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:
- *
- *         http://www.gnu.org/copyleft/gpl.html
- *
- * @category  Moodle
- * @package   user
- * @copyright Copyright (c) 1999 onwards Martin Dougiamas     http://dougiamas.com
- * @license   http://www.gnu.org/copyleft/gpl.html     GNU GPL License
- */
-require_once(dirname(dirname(__FILE__)) . '/lib/moodleexternal.php');
-require_once(dirname(dirname(__FILE__)) . '/course/lib.php');
-
-/**
- * course webservice api
- *
- * @author Jerome Mouneyrac
- */
-final class course_external extends moodle_external {
-
-    /**
-     * Retrieve courses
-     * @param array|struct $params - need to be define as struct for XMLRPC
-     * @subparam integer   $params:search->id - the id to search
-     * @subparam string    $params:search->idnumber - the idnumber to search
-     * @subparam string    $params:search->shortname - the shortname to search
-     * @return object     $return
-     * @subreturn integer $return:course->id
-     * @subreturn string  $return:course->idnumber
-     * @subreturn string  $return:course->shortname
-     * @subreturn integer $return:course->category
-     * @subreturn string  $return:course->fullname
-     * @subreturn string  $return:course->summary
-     * @subreturn string  $return:course->format
-     * @subreturn integer $return:course->startdate
-     * @subreturn integer $return:course->sortorder
-     * @subreturn integer $return:course->showgrades
-     * @subreturn string  $return:course->modinfo
-     * @subreturn string  $return:course->newsitems
-     * @subreturn string  $return:course->guest
-     * @subreturn integer $return:course->metacourse
-     * @subreturn string  $return:course->password
-     * @subreturn integer $return:course->enrolperiod
-     * @subreturn integer $return:course->defaultrole
-     * @subreturn integer $return:course->enrollable
-     * @subreturn integer $return:course->numsections
-     * @subreturn integer $return:course->expirynotify
-     * @subreturn integer $return:course->notifystudents
-     * @subreturn integer $return:course->expirythreshold
-     * @subreturn integer $return:course->marker
-     * @subreturn integer $return:course->maxbytes
-     * @subreturn integer $return:course->showreports
-     * @subreturn integer $return:course->visible
-     * @subreturn integer $return:course->hiddensections
-     * @subreturn integer $return:course->groupmode
-     * @subreturn integer $return:course->groupmodeforce
-     * @subreturn integer $return:course->defaultgroupingid
-     * @subreturn string  $return:course->lang
-     * @subreturn string  $return:course->theme
-     * @subreturn string  $return:course->cost
-     * @subreturn string  $return:course->currency
-     * @subreturn integer $return:course->timecreated
-     * @subreturn integer $return:course->timemodified
-     * @subreturn integer $return:course->requested
-     * @subreturn integer $return:course->restrictmodules
-     * @subreturn integer $return:course->enrolstartdate
-     * @subreturn integer $return:course->enrolenddate
-     * @subreturn string  $return:course->enrol
-     * @subreturn integer $return:course->enablecompletion
-     */
-    static function get_courses($params) {
-        global $USER, $DB;
-        if (has_capability('moodle/course:participate', get_context_instance(CONTEXT_SYSTEM))) {
-            $courses = array();
-            foreach ($params as $param) {
-                $course = new stdClass();
-                if (key_exists('id', $param)) {
-                    $param['id'] = clean_param($param['id'], PARAM_INT);
-                    $course = $DB->get_record('course', array('id'=>$param['id']));
-
-                } else if (key_exists('idnumber', $param)) {
-                    $param['idnumber'] = clean_param($param['idnumber'], PARAM_ALPHANUM);
-                    $course = $DB->get_record('course', array('idnumber'=>$param['idnumber']));
-                } else if (key_exists('shortname', $param)) {
-                    $param['shortname'] = clean_param($param['shortname'], PARAM_ALPHANUM);
-                    $course = $DB->get_record('course', array('shortname'=>$param['shortname']));
-                }
-                if (!empty($course)) {
-                    $returnedcourse = new stdClass();
-                    $returnedcourse->id =  $course->id;
-                    $returnedcourse->idnumber =  $course->idnumber;
-                    $returnedcourse->shortname =  $course->shortname;
-                    $returnedcourse->summary =  $course->summary;
-                    $returnedcourse->format =  $course->format;
-                    $returnedcourse->fullname =  $course->fullname;
-                    $returnedcourse->numsections =  $course->numsections;
-                    $returnedcourse->startdate =  $course->startdate;
-                    $returnedcourse->category =  $course->category;
-                    $returnedcourse->sortorder =  $course->sortorder;
-                    $returnedcourse->password =  $course->password  ;
-                    $returnedcourse->showgrades =  $course->showgrades;
-                    $returnedcourse->modinfo =  $course->modinfo;
-                    $returnedcourse->newsitems =  $course->newsitems;
-                    $returnedcourse->guest =  $course->guest;
-                    $returnedcourse->enrolperiod =  $course->enrolperiod;
-                    $returnedcourse->marker =  $course->marker;
-                    $returnedcourse->maxbytes =  $course->maxbytes;
-                    $returnedcourse->showreports =  $course->showreports;
-                    $returnedcourse->visible =  $course->visible;
-                    $returnedcourse->hiddensections =  $course->hiddensections;
-                    $returnedcourse->groupmode =  $course->groupmode;
-                    $returnedcourse->groupmodeforce =  $course->groupmodeforce;
-                    $returnedcourse->defaultgroupingid =  $course->defaultgroupingid;
-                    $returnedcourse->lang =  $course->lang;
-                    $returnedcourse->theme =  $course->theme;
-                    $returnedcourse->cost =  $course->cost;
-                    $returnedcourse->currency =  $course->currency;
-                    $returnedcourse->timecreated =  $course->timecreated;
-                    $returnedcourse->timemodified =  $course->timemodified;
-                    $returnedcourse->metacourse =  $course->metacourse;
-                    $returnedcourse->requested =  $course->requested;
-                    $returnedcourse->restrictmodules =  $course->restrictmodules;
-                    $returnedcourse->expirynotify =  $course->expirynotify;
-                    $returnedcourse->expirythreshold =  $course->expirythreshold;
-                    $returnedcourse->notifystudents =  $course->notifystudents;
-                    $returnedcourse->enrollable =  $course->enrollable;
-                    $returnedcourse->enrolstartdate =  $course->enrolstartdate;
-                    $returnedcourse->enrolenddate =  $course->enrolenddate;
-                    $returnedcourse->enrol =  $course->enrol;
-                    $returnedcourse->defaultrole =  $course->defaultrole;
-                    $returnedcourse->enablecompletion =  $course->enablecompletion;
-
-                    $courses[] = $returnedcourse;
-                }
-            }
-            return $courses;
-        }
-        else {
-            throw new moodle_exception('wscouldnotviewcoursenopermission');
-        }
-    }
-
-    /**
-     * Create multiple courses
-     * @param array|struct $params - need to be define as struct for XMLRPC
-     * @subparam string    $params:course->idnumber
-     * @subparam string    $params:course->shortname
-     * @subparam integer   $params:course->category
-     * @subparam string    $params:course->fullname
-     * @subparam string    $params:course->summary
-     * @subparam string    $params:course->format
-     * @subparam integer   $params:course->startdate
-     * @subparam integer   $params:course->sortorder
-     * @subparam integer   $params:course->showgrades
-     * @subparam string    $params:course->modinfo
-     * @subparam string    $params:course->newsitems
-     * @subparam string    $params:course->guest
-     * @subparam integer   $params:course->metacourse
-     * @subparam string    $params:course->password
-     * @subparam integer   $params:course->enrolperiod
-     * @subparam integer   $params:course->defaultrole
-     * @subparam integer   $params:course->enrollable
-     * @subparam integer   $params:course->numsections
-     * @subparam integer   $params:course->expirynotify
-     * @subparam integer   $params:course->notifystudents
-     * @subparam integer   $params:course->expirythreshold
-     * @subparam integer   $params:course->marker
-     * @subparam integer   $params:course->maxbytes
-     * @subparam integer   $params:course->showreports
-     * @subparam integer   $params:course->visible
-     * @subparam integer   $params:course->hiddensections
-     * @subparam integer   $params:course->groupmode
-     * @subparam integer   $params:course->groupmodeforce
-     * @subparam integer   $params:course->defaultgroupingid
-     * @subparam string    $params:course->lang
-     * @subparam string    $params:course->theme
-     * @subparam string    $params:course->cost
-     * @subparam string    $params:course->currency
-     * @subparam integer   $params:course->timecreated
-     * @subparam integer   $params:course->timemodified
-     * @subparam integer   $params:course->requested
-     * @subparam integer   $params:course->restrictmodules
-     * @subparam integer   $params:course->enrolstartdate
-     * @subparam integer   $params:course->enrolenddate
-     * @subparam string    $params:course->enrol
-     * @subparam integer   $params:course->enablecompletion
-     * @return array $return ids of new courses
-     * @subreturn integer $return:id course id
-     */
-    static function create_courses($params) {
-        global $USER;
-        if (has_capability('moodle/course:create', get_context_instance(CONTEXT_SYSTEM))) {
-            $courses = array();
-            foreach ($params as $courseparams) {
-
-                $course = new stdClass();
-                if (array_key_exists('idnumber', $courseparams)) {
-                    $course->idnumber =  clean_param($courseparams['idnumber'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('shortname', $courseparams)) {
-                    $course->shortname =  clean_param($courseparams['shortname'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('category', $courseparams)) {
-                    $course->category =  clean_param($courseparams['category'], PARAM_INT);
-                }
-
-                if (array_key_exists('fullname', $courseparams)) {
-                    $course->fullname =  clean_param($courseparams['fullname'], PARAM_TEXT);
-                }
-
-                if (array_key_exists('summary', $courseparams)) {
-                    $course->summary =  clean_param($courseparams['summary'], PARAM_TEXT);
-                }
-
-                if (array_key_exists('format', $courseparams)) {
-                    $course->format =  clean_param($courseparams['format'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('startdate', $courseparams)) {
-                    $course->startdate =  clean_param($courseparams['startdate'], PARAM_INT);
-                }
-
-
-                if (array_key_exists('sortorder', $courseparams)) {
-                    $course->sortorder =  clean_param($courseparams['sortorder'], PARAM_INT);
-                }
-
-                if (array_key_exists('showgrades', $courseparams)) {
-                    $course->showgrades =  clean_param($courseparams['showgrades'], PARAM_INT);
-                }
-
-                if (array_key_exists('modinfo', $courseparams)) {
-                    $course->modinfo =  clean_param($courseparams['modinfo'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('newsitems', $courseparams)) {
-                    $course->newsitems =  clean_param($courseparams['newsitems'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('guest', $courseparams)) {
-                    $course->guest =  clean_param($courseparams['guest'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('metacourse', $courseparams)) {
-                    $course->metacourse =  clean_param($courseparams['metacourse'], PARAM_INT);
-                }
-
-                if (array_key_exists('password', $courseparams)) {
-                    $course->password =  clean_param($courseparams['password'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('enrolperiod', $courseparams)) {
-                    $course->enrolperiod =  clean_param($courseparams['enrolperiod'], PARAM_INT);
-                }
-
-                if (array_key_exists('defaultrole', $courseparams)) {
-                    $course->defaultrole =  clean_param($courseparams['defaultrole'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrollable', $courseparams)) {
-                    $course->enrollable =  clean_param($courseparams['enrollable'], PARAM_INT);
-                }
-
-                if (array_key_exists('numsections', $courseparams)) {
-                    $course->numsections =  clean_param($courseparams['numsections'], PARAM_INT);
-                }
-
-                if (array_key_exists('expirynotify', $courseparams)) {
-                    $course->expirynotify =  clean_param($courseparams['expirynotify'], PARAM_INT);
-                }
-
-                if (array_key_exists('notifystudents', $courseparams)) {
-                    $course->notifystudents =  clean_param($courseparams['notifystudents'], PARAM_INT);
-                }
-
-                if (array_key_exists('expirythreshold', $courseparams)) {
-                    $course->expirythreshold =  clean_param($courseparams['expirythreshold'], PARAM_INT);
-                }
-
-                if (array_key_exists('marker', $courseparams)) {
-                    $course->marker =  clean_param($courseparams['marker'], PARAM_INT);
-                }
-
-                if (array_key_exists('maxbytes', $courseparams)) {
-                    $course->maxbytes =  clean_param($courseparams['maxbytes'], PARAM_INT);
-                }
-
-                if (array_key_exists('showreports', $courseparams)) {
-                    $course->showreports =  clean_param($courseparams['showreports'], PARAM_INT);
-                }
-
-                if (array_key_exists('visible', $courseparams)) {
-                    $course->visible =  clean_param($courseparams['visible'], PARAM_INT);
-                }
-
-                if (array_key_exists('hiddensections', $courseparams)) {
-                    $course->hiddensections =  clean_param($courseparams['hiddensections'], PARAM_INT);
-                }
-
-
-                if (array_key_exists('groupmode', $courseparams)) {
-                    $course->groupmode =  clean_param($courseparams['groupmode'], PARAM_INT);
-                }
-
-                if (array_key_exists('groupmodeforce', $courseparams)) {
-                    $course->groupmodeforce =  clean_param($courseparams['groupmodeforce'], PARAM_INT);
-                }
-
-                if (array_key_exists('defaultgroupingid', $courseparams)) {
-                    $course->defaultgroupingid =  clean_param($courseparams['defaultgroupingid'], PARAM_INT);
-                }
-
-                if (array_key_exists('lang', $courseparams)) {
-                    $course->lang =  clean_param($courseparams['lang'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('theme', $courseparams)) {
-                    $course->theme =  clean_param($courseparams['theme'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('cost', $courseparams)) {
-                    $course->cost =  clean_param($courseparams['cost'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('currency', $courseparams)) {
-                    $course->currency =  clean_param($courseparams['currency'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('timecreated', $courseparams)) {
-                    $course->timecreated =  clean_param($courseparams['timecreated'], PARAM_INT);
-                }
-
-                if (array_key_exists('timemodified', $courseparams)) {
-                    $course->timemodified =  clean_param($courseparams['timemodified'], PARAM_INT);
-                }
-
-                if (array_key_exists('requested', $courseparams)) {
-                    $course->requested =  clean_param($courseparams['requested'], PARAM_INT);
-                }
-
-                if (array_key_exists('restrictmodules', $courseparams)) {
-                    $course->restrictmodules =  clean_param($courseparams['restrictmodules'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrolstartdate', $courseparams)) {
-                    $course->enrolstartdate =  clean_param($courseparams['enrolstartdate'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrolenddate', $courseparams)) {
-                    $course->enrolenddate =  clean_param($courseparams['enrolenddate'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrol', $courseparams)) {
-                    $course->enrol =  clean_param($courseparams['enrol'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('enablecompletion', $courseparams)) {
-                    $course->enablecompletion =  clean_param($courseparams['enablecompletion'], PARAM_INT);
-                }
-
-                $courses[] = create_course($course);
-
-            }
-            return $courses;
-        }
-        else {
-            throw new moodle_exception('wscouldnotcreateecoursenopermission');
-        }
-    }
-
-     /**
-     * Delete multiple courses
-     * @global object $DB
-     * @param array|struct $params - need to be define as struct for XMLRPC
-     * @subparam string $params:course->shortname
-     * @subparam integer $params:course->id
-     * @subparam string $params:course->shortname
-     * @subparam string $params:course->idnumber
-     * @return boolean result true if success
-     */
-    static function delete_courses($params) {
-        global $DB,$USER;
-        if (has_capability('moodle/course:delete', get_context_instance(CONTEXT_SYSTEM))) {
-            $courses = array();
-            $result = true;
-            foreach ($params as $param) {
-                $course = new stdClass();
-                if (key_exists('id', $param)) {
-                    $param['id'] = clean_param($param['id'], PARAM_INT);
-                    if (!delete_course($param['id'], false)) {
-                        $result = false;
-                    }
-                } else if (key_exists('idnumber', $param)) {
-                    $param['idnumber'] = clean_param($param['idnumber'], PARAM_ALPHANUM);
-                    //it doesn't cost that much to retrieve the course here
-                    //as it would be done into delete_course()
-                    $course = $DB->get_record('course', array('idnumber'=>$param['idnumber']));
-                    if (!delete_course($course, false)) {
-                        $result = false;
-                    }
-                } else if (key_exists('shortname', $param)) {
-                    $param['shortname'] = clean_param($param['shortname'], PARAM_ALPHANUM);
-                    $course = $DB->get_record('course', array('shortname'=>$param['shortname']));
-                    if (!delete_course($course, false)) {
-                        $result = false;
-                    }
-                }
-            }
-            return $result;
-        }
-        else {
-            throw new moodle_exception('wscouldnotdeletecoursenopermission');
-        }
-    }
-
-    /**
-     * Update some courses information
-     * @global object $DB
-     * @param array|struct $params - need to be define as struct for XMLRPC
-     * @subparam integer   $params:course->id
-     * @subparam string    $params:course->idnumber
-     * @subparam string    $params:course->shortname
-     * @subparam integer   $params:course->category
-     * @subparam string    $params:course->fullname
-     * @subparam string    $params:course->summary
-     * @subparam string    $params:course->format
-     * @subparam integer   $params:course->startdate
-     * @subparam integer   $params:course->sortorder
-     * @subparam integer   $params:course->showgrades
-     * @subparam string    $params:course->modinfo
-     * @subparam string    $params:course->newsitems
-     * @subparam string    $params:course->guest
-     * @subparam integer   $params:course->metacourse
-     * @subparam string    $params:course->password
-     * @subparam integer   $params:course->enrolperiod
-     * @subparam integer   $params:course->defaultrole
-     * @subparam integer   $params:course->enrollable
-     * @subparam integer   $params:course->numsections
-     * @subparam integer   $params:course->expirynotify
-     * @subparam integer   $params:course->notifystudents
-     * @subparam integer   $params:course->expirythreshold
-     * @subparam integer   $params:course->marker
-     * @subparam integer   $params:course->maxbytes
-     * @subparam integer   $params:course->showreports
-     * @subparam integer   $params:course->visible
-     * @subparam integer   $params:course->hiddensections
-     * @subparam integer   $params:course->groupmode
-     * @subparam integer   $params:course->groupmodeforce
-     * @subparam integer   $params:course->defaultgroupingid
-     * @subparam string    $params:course->lang
-     * @subparam string    $params:course->theme
-     * @subparam string    $params:course->cost
-     * @subparam string    $params:course->currency
-     * @subparam integer   $params:course->timecreated
-     * @subparam integer   $params:course->timemodified
-     * @subparam integer   $params:course->requested
-     * @subparam integer   $params:course->restrictmodules
-     * @subparam integer   $params:course->enrolstartdate
-     * @subparam integer   $params:course->enrolenddate
-     * @subparam string    $params:course->enrol
-     * @subparam integer   $params:course->enablecompletion
-     * @return boolean result true if success
-     */
-    static function update_courses($params) {
-        global $DB,$USER;
-
-        if (has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM))) {
-            $courses = array();
-            $result = true;
-            foreach ($params as $courseparams) {
-
-                $course = new stdClass();
-
-                if (array_key_exists('idnumber', $courseparams)) {
-                    $course->idnumber =  clean_param($courseparams['idnumber'], PARAM_ALPHANUM);
-                }
-
-                if (array_key_exists('shortname', $courseparams)) {
-                    $course->shortname =  clean_param($courseparams['shortname'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('category', $courseparams)) {
-                    $course->category =  clean_param($courseparams['category'], PARAM_INT);
-                }
-
-                if (array_key_exists('fullname', $courseparams)) {
-                    $course->fullname =  clean_param($courseparams['fullname'], PARAM_TEXT);
-                }
-
-                if (array_key_exists('summary', $courseparams)) {
-                    $course->summary =  clean_param($courseparams['summary'], PARAM_TEXT);
-                }
-
-                if (array_key_exists('format', $courseparams)) {
-                    $course->format =  clean_param($courseparams['format'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('startdate', $courseparams)) {
-                    $course->startdate =  clean_param($courseparams['startdate'], PARAM_INT);
-                }
-
-
-                if (array_key_exists('sortorder', $courseparams)) {
-                    $course->sortorder =  clean_param($courseparams['sortorder'], PARAM_INT);
-                }
-
-                if (array_key_exists('showgrades', $courseparams)) {
-                    $course->showgrades =  clean_param($courseparams['showgrades'], PARAM_INT);
-                }
-
-                if (array_key_exists('modinfo', $courseparams)) {
-                    $course->modinfo =  clean_param($courseparams['modinfo'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('newsitems', $courseparams)) {
-                    $course->newsitems =  clean_param($courseparams['newsitems'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('guest', $courseparams)) {
-                    $course->guest =  clean_param($courseparams['guest'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('metacourse', $courseparams)) {
-                    $course->metacourse =  clean_param($courseparams['metacourse'], PARAM_INT);
-                }
-
-                if (array_key_exists('password', $courseparams)) {
-                    $course->password =  clean_param($courseparams['password'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('enrolperiod', $courseparams)) {
-                    $course->enrolperiod =  clean_param($courseparams['enrolperiod'], PARAM_INT);
-                }
-
-                if (array_key_exists('defaultrole', $courseparams)) {
-                    $course->defaultrole =  clean_param($courseparams['defaultrole'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrollable', $courseparams)) {
-                    $course->enrollable =  clean_param($courseparams['enrollable'], PARAM_INT);
-                }
-
-                if (array_key_exists('numsections', $courseparams)) {
-                    $course->numsections =  clean_param($courseparams['numsections'], PARAM_INT);
-                }
-
-                if (array_key_exists('expirynotify', $courseparams)) {
-                    $course->expirynotify =  clean_param($courseparams['expirynotify'], PARAM_INT);
-                }
-
-                if (array_key_exists('notifystudents', $courseparams)) {
-                    $course->notifystudents =  clean_param($courseparams['notifystudents'], PARAM_INT);
-                }
-
-                if (array_key_exists('expirythreshold', $courseparams)) {
-                    $course->expirythreshold =  clean_param($courseparams['expirythreshold'], PARAM_INT);
-                }
-
-                if (array_key_exists('marker', $courseparams)) {
-                    $course->marker =  clean_param($courseparams['marker'], PARAM_INT);
-                }
-
-                if (array_key_exists('maxbytes', $courseparams)) {
-                    $course->maxbytes =  clean_param($courseparams['maxbytes'], PARAM_INT);
-                }
-
-                if (array_key_exists('showreports', $courseparams)) {
-                    $course->showreports =  clean_param($courseparams['showreports'], PARAM_INT);
-                }
-
-                if (array_key_exists('visible', $courseparams)) {
-                    $course->visible =  clean_param($courseparams['visible'], PARAM_INT);
-                }
-
-                if (array_key_exists('hiddensections', $courseparams)) {
-                    $course->hiddensections =  clean_param($courseparams['hiddensections'], PARAM_INT);
-                }
-
-                if (array_key_exists('groupmode', $courseparams)) {
-                    $course->groupmode =  clean_param($courseparams['groupmode'], PARAM_INT);
-                }
-
-                if (array_key_exists('groupmodeforce', $courseparams)) {
-                    $course->groupmodeforce =  clean_param($courseparams['groupmodeforce'], PARAM_INT);
-                }
-
-                if (array_key_exists('defaultgroupingid', $courseparams)) {
-                    $course->defaultgroupingid =  clean_param($courseparams['defaultgroupingid'], PARAM_INT);
-                }
-
-                if (array_key_exists('lang', $courseparams)) {
-                    $course->lang =  clean_param($courseparams['lang'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('theme', $courseparams)) {
-                    $course->theme =  clean_param($courseparams['theme'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('cost', $courseparams)) {
-                    $course->cost =  clean_param($courseparams['cost'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('currency', $courseparams)) {
-                    $course->currency =  clean_param($courseparams['currency'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('timecreated', $courseparams)) {
-                    $course->timecreated =  clean_param($courseparams['timecreated'], PARAM_INT);
-                }
-
-                if (array_key_exists('timemodified', $courseparams)) {
-                    $course->timemodified =  clean_param($courseparams['timemodified'], PARAM_INT);
-                }
-
-                if (array_key_exists('requested', $courseparams)) {
-                    $course->requested =  clean_param($courseparams['requested'], PARAM_INT);
-                }
-
-                if (array_key_exists('restrictmodules', $courseparams)) {
-                    $course->restrictmodules =  clean_param($courseparams['restrictmodules'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrolstartdate', $courseparams)) {
-                    $course->enrolstartdate =  clean_param($courseparams['enrolstartdate'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrolenddate', $courseparams)) {
-                    $course->enrolenddate =  clean_param($courseparams['enrolenddate'], PARAM_INT);
-                }
-
-                if (array_key_exists('enrol', $courseparams)) {
-                    $course->enrol =  clean_param($courseparams['enrol'], PARAM_ALPHANUMEXT);
-                }
-
-                if (array_key_exists('enablecompletion', $courseparams)) {
-                    $course->enablecompletion =  clean_param($courseparams['enablecompletion'], PARAM_INT);
-                }
-
-                if (array_key_exists('id', $courseparams)) {
-                    $course->id =  clean_param($courseparams['id'], PARAM_INT);
-                }
-
-                if (!update_course($course)) {
-                    $result = false;
-                }
-
-            }
-            return $result;
-        }
-        else {
-            throw new moodle_exception('wscouldnotupdatecoursenopermission');
-        }
-
-    }
-
-    /**
-     * Get course modules
-     * @global object $DB
-     * @param array|struct $params - need to be define as struct for XMLRPC
-     * @subparam string $params:course->id
-     * @return array $return course modules
-     * @subreturn string $return:id module id
-     * @subreturn string $return:name module name
-     * @subreturn string $return:type module type
-     */
-    static function get_course_modules($params, $type=null) {
-        global $DB;
-        if (has_capability('moodle/course:participate', get_context_instance(CONTEXT_SYSTEM))) {
-            $modules = array();
-            foreach ($params as $courseparams) {
-                if (array_key_exists('id', $courseparams)) {
-                    $id =  clean_param($courseparams['id'], PARAM_INT);
-                }
-
-                $activities = get_array_of_activities($id);
-
-                foreach ($activities as $activity) {
-                    if (empty($type)) {
-                        $module = array('id' => $activity->id, 'courseid' => $id, 'name' => $activity->name, 'type' => $activity->mod);
-                        $modules[] = $module;
-                    }
-                    else if ($type=="activities") {
-                        if ($activity->mod != "resource" && $activity->mod != "label") {
-                            $module = array('id' => $activity->id, 'courseid' => $id, 'name' => $activity->name, 'type' => $activity->mod);
-                            $modules[] = $module;
-                        }
-                    }
-                    else if ($type=="resources") {
-                        if ($activity->mod == "resource" || $activity->mod == "label") {
-                            $module = array('id' => $activity->id, 'courseid' => $id, 'resource' => $activity->name, 'type' => $activity->mod);
-                            $modules[] = $module;
-                        }
-                    }
-                }
-            }
-            return $modules;
-        }
-        else {
-            throw new moodle_exception('wscouldnotgetcoursemodulesnopermission');
-        }
-    }
-
-     /**
-     * Get course activities
-     * @global object $DB
-     * @param array|struct $params - need to be define as struct for XMLRPC
-     * @subparam string $params:course->id
-     * @return array $return course activities
-     * @subreturn string $return:id activity id
-     * @subreturn string $return:name activity name
-     * @subreturn string $return:type activity type
-     */
-    static function get_course_activities($params) {
-        course_external::get_course_modules($params, "activities");
-    }
-
-    /**
-     * Get course resources
-     * @global object $DB
-     * @param array|struct $params - need to be define as struct for XMLRPC
-     * @subparam string $params:course->id
-     * @return array $return course resources
-     * @subreturn integer $return:id resource id
-     * @subreturn string $return:name resource name
-     * @subreturn string $return:type resource type
-     */
-    static function get_course_resources($params) {
-        course_external::get_course_modules($params, "resources");
-    }
-
-}
-
-
diff --git a/course/externallib.php b/course/externallib.php
index 6b8c70803b30..ec8e627811cb 100644
--- a/course/externallib.php
+++ b/course/externallib.php
@@ -18,12 +18,14 @@
 /**
  * External user API
  *
- * @package    moodlecore
- * @subpackage webservice
+ * @package    core
+ * @subpackage course
  * @copyright  2009 Moodle Pty Ltd (http://moodle.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die;
+
 require_once("$CFG->libdir/externallib.php");
 
 class moodle_course_external extends external_api {
diff --git a/course/importstudents.html b/course/importstudents.html
deleted file mode 100644
index 5b8d74230982..000000000000
--- a/course/importstudents.html
+++ /dev/null
@@ -1,80 +0,0 @@
-
-<form id="studentform" method="post" action="importstudents.php">
-<input type="hidden" name="previoussearch" value="<?php echo $previoussearch ?>" />
-<input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
-<input type="hidden" name="id" value="<?php echo $id?>" />
-  <table summary="" align="center" border="0" cellpadding="5" cellspacing="0">
-    <tr>
-      <td valign="top">
-          <label for="removeselect"><?php echo count($alreadycourses) . " ". $stralreadycourses ?></label>
-          <br />
-          <select name="removeselect[]" size="20" id="removeselect" multiple="multiple"
-                  onFocus="getElementById('studentform').add.disabled=true;
-                           getElementById('studentform').remove.disabled=false;
-                           getElementById('studentform').addselect.selectedIndex=-1;">
-          <?php
-            foreach ($alreadycourses as $course) {
-                echo "<option value=\"$course->id\">".course_format_name($course,60)."</option>\n";
-            }
-          ?>
-          </select></td>
-      <td valign="top">
-        <p class="arrow_button">
-            <input name="add" id="add" type="submit" value="<?php echo '&nbsp;'.$OUTPUT->larrow().' &nbsp; &nbsp; '.get_string('add'); ?>" title="<?php print_string('add'); ?>" />
-            <br />
-            <input name="remove" id="remove" type="submit" value="<?php echo '&nbsp; '.$OUTPUT->rarrow().' &nbsp; &nbsp; '.get_string('remove'); ?>" title="<?php print_string('remove'); ?>" />
-        </p>
-      </td>
-      <td valign="top">
-          <label for="addselect"><?php echo $numcourses . " " . $strpotentialcourses ?></label>
-          <br />
-          <select name="addselect[]" size="20" id="addselect" multiple="multiple"
-                  onFocus="getElementById('studentform').add.disabled=false;
-                           getElementById('studentform').remove.disabled=true;
-                           getElementById('studentform').removeselect.selectedIndex=-1;">
-          <?php
-
-              if (!empty($searchcourses)) {
-                  echo "<optgroup label=\"$strsearchresults (" . count($searchcourses) . ")\">\n";
-                  foreach ($searchcourses as $course) {
-                      echo "<option value=\"$course->id\">".course_format_name($course,60)."</option>\n";
-                  }
-                  echo "</optgroup>\n";
-              }
-              if (!empty($courses)) {
-                  if ($numcourses > MAX_COURSES_PER_PAGE) {
-                      echo '<optgroup label="'.get_string('toomanytoshow').'"><option></option></optgroup>'."\n"
-                          .'<optgroup label="'.get_string('trysearching').'"><option></option></optgroup>'."\n";
-                  }
-                  else {
-                      foreach ($courses as $course) {
-                      echo "<option value=\"$course->id\">".course_format_name($course,60)."</option>\n";
-                      }
-                  }
-              }
-          ?>
-         </select>
-         <br />
-         <label for="searchtext" class="accesshide"><?php p($strsearch) ?></label>
-         <input type="text" name="searchtext" id="searchtext" size="30" value="<?php p($searchtext) ?>"
-                  onFocus ="getElementById('studentform').add.disabled=true;
-                            getElementById('studentform').remove.disabled=true;
-                            getElementById('studentform').removeselect.selectedIndex=-1;
-                            getElementById('studentform').addselect.selectedIndex=-1;"
-                  onkeydown = "var keyCode = event.which ? event.which : event.keyCode;
-                               if (keyCode == 13) {
-                                    getElementById('studentform').previoussearch.value=1;
-                                    getElementById('studentform').submit();
-                               } " />
-         <input name="search" id="search" type="submit" value="<?php p($strsearch) ?>" />
-         <?php
-              if (!empty($searchcourses)) {
-                  echo '<input name="showall" id="showall" type="submit" value="'.$strshowall.'" />'."\n";
-              }
-         ?>
-       </td>
-    </tr>
-  </table>
-</form>
-
-
diff --git a/course/importstudents.php b/course/importstudents.php
deleted file mode 100644
index 434abb70f87f..000000000000
--- a/course/importstudents.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Script to assign students to a meta course by selecting which courses the meta
- * course comprises. This is basically a hack of student.php that uses courses instead.
- *
- * @copyright 1999 Martin Dougiamas  http://dougiamas.com
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @package course
- */
-
-    require_once("../config.php");
-    require_once("lib.php");
-
-    define("MAX_COURSES_PER_PAGE", 1000);
-
-    $id             = required_param('id',PARAM_INT); // course id
-    $add            = optional_param('add', 0, PARAM_BOOL);
-    $remove         = optional_param('remove', 0, PARAM_BOOL);
-    $showall        = optional_param('showall', 0, PARAM_BOOL);
-    $searchtext     = optional_param('searchtext', '', PARAM_RAW); // search string
-    $previoussearch = optional_param('previoussearch', 0, PARAM_BOOL);
-    $previoussearch = ($searchtext != '') or ($previoussearch) ? 1:0;
-
-    $url = new moodle_url('/course/importstudents.php', array('id'=>$id));
-    if ($add !== 0) {
-        $url->param('add', $add);
-    }
-    if ($remove !== 0) {
-        $url->param('remove', $remove);
-    }
-    if ($showall !== 0) {
-        $url->param('showall', $showall);
-    }
-    if ($searchtext !== '') {
-        $url->param('searchtext', $searchtext);
-    }
-    if ($previoussearch !== 0) {
-        $url->param('previoussearch', $previoussearch);
-    }
-    $PAGE->set_url($url);
-
-    $site = get_site();
-
-    if (!$course = $DB->get_record('course', array('id'=>$id))) {
-        print_error("invalidcourseid");
-    }
-
-    require_login($course->id);
-    $context = get_context_instance(CONTEXT_COURSE, $course->id);
-    require_capability('moodle/course:managemetacourse', $context);
-
-    if (!$course->metacourse) {
-        redirect("$CFG->wwwroot/course/view.php?id=$course->id");
-    }
-
-    $strassigncourses = get_string('metaassigncourses');
-    $stralreadycourses = get_string('metaalreadycourses');
-    $strnoalreadycourses = get_string('metanoalreadycourses');
-    $strpotentialcourses = get_string('metapotentialcourses');
-    $strnopotentialcourses = get_string('metanopotentialcourses');
-    $straddcourses = get_string('metaaddcourse');
-    $strremovecourse = get_string('metaremovecourse');
-    $strsearch        = get_string("search");
-    $strsearchresults  = get_string("searchresults");
-    $strcourses   = get_string("courses");
-    $strshowall = get_string("showall");
-
-    $PAGE->navbar->add($strassigncourses);
-    $PAGE->set_title("$course->shortname: $strassigncourses");
-    $PAGE->set_heading($site->fullname);
-    $PAGE->set_focuscontrol("searchtext");
-    echo $OUTPUT->header();
-
-/// Print a help notice about the need to use this page
-
-    echo $OUTPUT->heading(get_string('childcourses'));
-
-    if (!$frm = data_submitted()) {
-        $note = get_string("importmetacoursenote");
-        echo $OUTPUT->box($note);
-
-/// A form was submitted so process the input
-
-    } else {
-        if ($add and !empty($frm->addselect) and confirm_sesskey()) {
-            $timestart = $timeend = 0;
-            foreach ($frm->addselect as $addcourse) {
-                $addcourse = clean_param($addcourse, PARAM_INT);
-                set_time_limit(180);
-                if (!add_to_metacourse($course->id,$addcourse)) {
-                    print_error("cannotmetacourse");
-                }
-            }
-        } else if ($remove and !empty($frm->removeselect) and confirm_sesskey()) {
-            foreach ($frm->removeselect as $removecourse) {
-                set_time_limit(180);
-                $removecourse = clean_param($removecourse, PARAM_INT);
-                if (! remove_from_metacourse($course->id,$removecourse)) {
-                    print_error("cannotremovefrommeta");
-                }
-            }
-        } else if ($showall and confirm_sesskey()) {
-            $searchtext = '';
-            $previoussearch = 0;
-        }
-    }
-
-
-/// Get all existing students and teachers for this course.
-    if(! $alreadycourses = get_courses_in_metacourse($course->id)) {
-        $alreadycourses = array();
-    }
-
-    $numcourses = 0;
-
-
-/// Get search results excluding any users already in this course
-    if (($searchtext != '') and $previoussearch and confirm_sesskey()) {
-        if ($searchcourses = get_courses_search(explode(" ",$searchtext),'fullname ASC',0,99999,$numcourses)) {
-            foreach ($searchcourses as $tmp) {
-                if (array_key_exists($tmp->id,$alreadycourses)) {
-                    unset($searchcourses[$tmp->id]);
-                }
-                if (!empty($tmp->metacourse)) {
-                    unset($searchcourses[$tmp->id]);
-                }
-            }
-            if (array_key_exists($course->id,$searchcourses)) {
-                unset($searchcourses[$course->id]);
-            }
-            $numcourses = count($searchcourses);
-        }
-    }
-
-/// If no search results then get potential students for this course excluding users already in course
-    if (empty($searchcourses)) {
-        $numcourses = count_courses_notin_metacourse($course->id);
-
-        if ($numcourses > 0 and $numcourses <= MAX_COURSES_PER_PAGE) {
-            $courses = get_courses_notin_metacourse($course->id);
-        } else {
-            $courses = array();
-        }
-    }
-
-    echo $OUTPUT->box_start();
-
-    include('importstudents.html');
-
-    echo $OUTPUT->box_end();
-
-    echo $OUTPUT->footer();
-
-
diff --git a/course/index.php b/course/index.php
index 52b70de37289..7725b299e56d 100644
--- a/course/index.php
+++ b/course/index.php
@@ -192,18 +192,19 @@
 }
 
 /// Hide or show a category
-if ((!empty($hide) or !empty($show)) and confirm_sesskey()) {
-    if (!empty($hide)) {
-        $tempcat = $DB->get_record('course_categories', array('id'=>$hide));
-        $visible = 0;
-    } else {
-        $tempcat = $DB->get_record('course_categories', array('id'=>$show));
-        $visible = 1;
+if ($hide and confirm_sesskey()) {
+    if ($tempcat = $DB->get_record('course_categories', array('id'=>$hide))) {
+        require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent));
+        if ($tempcat->visible == 1) {
+            course_category_hide($tempcat);
+        }
     }
-    require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent));
-    if ($tempcat) {
-        $DB->set_field('course_categories', 'visible', $visible, array('id'=>$tempcat->id));
-        $DB->set_field('course', 'visible', $visible, array('category' => $tempcat->id));
+} else if ($show and confirm_sesskey()) {
+    if ($tempcat = $DB->get_record('course_categories', array('id'=>$show))) {
+        require_capability('moodle/category:manage', get_category_or_system_context($tempcat->parent));
+        if ($tempcat->visible == 0) {
+            course_category_show($tempcat);
+        }
     }
 }
 
diff --git a/course/info.php b/course/info.php
index fc3fc4e0bca2..9c0d32a685f6 100644
--- a/course/info.php
+++ b/course/info.php
@@ -29,7 +29,7 @@
     }
 
     $context = get_context_instance(CONTEXT_COURSE, $course->id);
-    if ((!course_parent_visible($course) || (! $course->visible)) && !has_capability('moodle/course:viewhiddencourses', $context)) {
+    if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $context)) {
         print_error('coursehidden', '', $CFG->wwwroot .'/');
     }
 
@@ -44,6 +44,8 @@
     echo $OUTPUT->header();
     echo $OUTPUT->heading('<a href="view.php?id='.$course->id.'">'.format_string($course->fullname) . '</a><br />(' . format_string($course->shortname) . ')');
 
+    //TODO: add enrol info
+    /*
     if ($course->guest || $course->password) {
         echo $OUTPUT->box_start('generalbox icons');
         if ($course->guest) {
@@ -55,17 +57,17 @@
             echo "<div><img alt=\"\" class=\"icon key\" src=\"" . $OUTPUT->pix_url('i/key') . "\" />&nbsp;$strrequireskey</div>";
         }
         echo $OUTPUT->box_end();
-    }
+    }*/
 
 
     echo $OUTPUT->box_start('generalbox info');
 
-    $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course_summary', $course->id);
+    $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course_summary', NULL);
     echo format_text($course->summary, $course->summaryformat, NULL, $course->id);
 
-    if (!empty($CFG->coursemanager)) {
-        $coursemanagerroles = explode(',', $CFG->coursemanager);
-        foreach ($coursemanagerroles as $roleid) {
+    if (!empty($CFG->coursecontact)) {
+        $coursecontactroles = explode(',', $CFG->coursecontact);
+        foreach ($coursecontactroles as $roleid) {
             $role = $DB->get_record('role', array('id'=>$roleid));
             $roleid = (int) $roleid;
             if ($users = get_role_users($roleid, $context, true)) {
@@ -84,9 +86,7 @@
         }
     }
 
-    require_once("$CFG->dirroot/enrol/enrol.class.php");
-    $enrol = enrolment_factory::factory($course->enrol);
-    echo $enrol->get_access_icons($course);
+// TODO: print some enrol icons
 
     echo $OUTPUT->box_end();
 
diff --git a/course/lib.php b/course/lib.php
index 6aa9819da1bf..546ac6ed1b8b 100644
--- a/course/lib.php
+++ b/course/lib.php
@@ -20,9 +20,12 @@
  *
  * @copyright 1999 Martin Dougiamas  http://dougiamas.com
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @package course
+ * @package core
+ * @subpackage course
  */
 
+defined('MOODLE_INTERNAL') || die;
+
 require_once($CFG->libdir.'/completionlib.php');
 require_once($CFG->libdir.'/filelib.php');
 
@@ -1915,7 +1918,7 @@ function get_course_category_tree($id = 0, $depth = 0) {
     list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
     list($catsql, $catparams) = $DB->get_in_or_equal(array_keys($categoryids));
     $sql = "SELECT
-            c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.password,c.summary,c.guest,c.cost,c.currency,c.category
+            c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary,c.category
             $ccselect
             FROM {course} c
             $ccjoin
@@ -2004,13 +2007,8 @@ function make_categories_options() {
  */
 function print_category_info($category, $depth, $showcourses = false) {
     global $CFG, $DB, $OUTPUT;
-    static $strallowguests, $strrequireskey, $strsummary;
 
-    if (empty($strsummary)) {
-        $strallowguests = get_string('allowguests');
-        $strrequireskey = get_string('requireskey');
-        $strsummary = get_string('summary');
-    }
+    $strsummary = get_string('summary');
 
     $catlinkcss = $category->visible ? '' : ' class="dimmed" ';
 
@@ -2028,7 +2026,7 @@ function print_category_info($category, $depth, $showcourses = false) {
 
     echo "\n\n".'<table class="categorylist">';
 
-    $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.password,c.summary,c.guest,c.cost,c.currency');
+    $courses = get_courses($category->id, 'c.sortorder ASC', 'c.id,c.sortorder,c.visible,c.fullname,c.shortname,c.summary');
     if ($showcourses and $coursecount) {
 
         echo '<tr>';
@@ -2061,18 +2059,7 @@ function print_category_info($category, $depth, $showcourses = false) {
                 echo '</td><td valign="top" class="course name">';
                 echo '<a '.$linkcss.' href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">'. format_string($course->fullname).'</a>';
                 echo '</td><td align="right" valign="top" class="course info">';
-                if ($course->guest ) {
-                    echo '<a title="'.$strallowguests.'" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">';
-                    echo '<img alt="'.$strallowguests.'" src="'.$OUTPUT->pix_url('i/guest') . '" /></a>';
-                } else {
-                    echo '<img alt="" style="width:18px;height:16px;" src="'.$OUTPUT->pix_url('spacer') . '" />';
-                }
-                if ($course->password) {
-                    echo '<a title="'.$strrequireskey.'" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">';
-                    echo '<img alt="'.$strrequireskey.'" src="'.$OUTPUT->pix_url('i/key') . '" /></a>';
-                } else {
-                    echo '<img alt="" style="width:18px;height:16px;" src="'.$OUTPUT->pix_url('spacer') . '" />';
-                }
+                //TODO: add some guest, pay icons
                 if ($course->summary) {
                     $link = new moodle_url('/course/info.php?id='.$course->id);
                     echo $OUTPUT->action_link($link, '<img alt="'.$strsummary.'" src="'.$OUTPUT->pix_url('i/info') . '" />',
@@ -2186,17 +2173,17 @@ function print_courses($category) {
             $category   = array_shift($categories);
             $courses    = get_courses_wmanagers($category->id,
                                                 'c.sortorder ASC',
-                                                array('password','summary','summaryformat','currency'));
+                                                array('summary','summaryformat'));
         } else {
             $courses    = get_courses_wmanagers('all',
                                                 'c.sortorder ASC',
-                                                array('password','summary','summaryformat','currency'));
+                                                array('summary','summaryformat'));
         }
         unset($categories);
     } else {
         $courses    = get_courses_wmanagers($category->id,
                                             'c.sortorder ASC',
-                                            array('password','summary','summaryformat','currency'));
+                                            array('summary','summaryformat'));
     }
 
     if ($courses) {
@@ -2239,7 +2226,7 @@ function print_course($course, $highlightterms = '') {
     $context = get_context_instance(CONTEXT_COURSE, $course->id);
 
     // Rewrite file URLs so that they are correct
-    $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course_summary', $course->id);
+    $course->summary = file_rewrite_pluginfile_urls($course->summary, 'pluginfile.php', $context->id, 'course_summary', NULL);
 
     $linkcss = $course->visible ? '' : ' class="dimmed" ';
 
@@ -2251,8 +2238,8 @@ function print_course($course, $highlightterms = '') {
 
     /// first find all roles that are supposed to be displayed
 
-    if (!empty($CFG->coursemanager)) {
-        $managerroles = split(',', $CFG->coursemanager);
+    if (!empty($CFG->coursecontact)) {
+        $managerroles = split(',', $CFG->coursecontact);
         $namesarray = array();
         if (isset($course->managers)) {
             if (count($course->managers)) {
@@ -2318,9 +2305,7 @@ function print_course($course, $highlightterms = '') {
         }
     }
 
-    require_once("$CFG->dirroot/enrol/enrol.class.php");
-    $enrol = enrolment_factory::factory($course->enrol);
-    echo $enrol->get_access_icons($course);
+    // TODO: print some enrol icons
 
     echo '</div><div class="summary">';
     $options = NULL;
@@ -2345,7 +2330,7 @@ function print_my_moodle() {
         print_error('nopermissions', '', '', 'See My Moodle');
     }
 
-    $courses  = get_my_courses($USER->id, 'visible DESC,sortorder ASC', array('summary'));
+    $courses  = enrol_get_my_courses('summary', 'visible DESC,sortorder ASC');
     $rhosts   = array();
     $rcourses = array();
     if (!empty($CFG->mnet_dispatcher_mode) && $CFG->mnet_dispatcher_mode==='strict') {
@@ -3036,15 +3021,6 @@ function course_format_name ($course,$max=100) {
     }
 }
 
-/**
- * This function will return true if the given course is a child course at all
- */
-function course_in_meta ($course) {
-    global $DB;
-    return $DB->record_exists("course_meta", array("child_course"=>$course->id));
-}
-
-
 function update_restricted_mods($course, $mods) {
     global $DB;
 
@@ -3082,8 +3058,8 @@ function course_allowed_module($course,$mod) {
     }
 
     // Admins and admin-like people who can edit everything can also add anything.
-    // This is a bit wierd, really.  I debated taking it out but it's enshrined in help for the setting.
-    if (has_capability('moodle/course:update', get_context_instance(CONTEXT_SYSTEM))) {
+    // Originally there was a coruse:update test only, but it did not match the test in course edit form
+    if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
         return true;
     }
 
@@ -3201,33 +3177,92 @@ function category_delete_move($category, $newparentid, $showfeedback=true) {
  * sortorder in order.
  *
  * @param $courseids is an array of course ids
+ * @return bool success
  */
 function move_courses($courseids, $categoryid) {
     global $CFG, $DB, $OUTPUT;
 
-    if (!empty($courseids) and $category = $DB->get_record('course_categories', array('id'=>$categoryid))) {
-        $courseids = array_reverse($courseids);
-        $i = 1;
+    if (empty($courseids)) {
+        // nothing to do
+        return;
+    }
 
-        foreach ($courseids as $courseid) {
-            if (!$course  = $DB->get_record("course", array("id"=>$courseid))) {
-                echo $OUTPUT->notification("Error finding course $courseid");
-            } else {
-                $course->category  = $categoryid;
-                $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - $i++;
+    if (!$category = $DB->get_record('course_categories', array('id'=>$categoryid))) {
+        return false;
+    }
 
-                $DB->update_record('course', $course);
+    $courseids = array_reverse($courseids);
+    $i = 1;
 
-                $context   = get_context_instance(CONTEXT_COURSE, $course->id);
-                $newparent = get_context_instance(CONTEXT_COURSECAT, $course->category);
-                context_moved($context, $newparent);
+    foreach ($courseids as $courseid) {
+        if ($course = $DB->get_record('course', array('id'=>$courseid), 'id, category')) {
+            $course->category  = $category->id;
+            $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - $i++;
+            if ($category->visible == 0) {
+                // hide the course when moving into hidden category,
+                // do not update the visibleold flag - we want to get to previous state if somebody unhides the category
+                $course->visible = 0;
             }
+
+            $DB->update_record('course', $course);
+
+            $context   = get_context_instance(CONTEXT_COURSE, $course->id);
+            $newparent = get_context_instance(CONTEXT_COURSECAT, $course->category);
+            context_moved($context, $newparent);
         }
-        fix_course_sortorder();
     }
+    fix_course_sortorder();
+
     return true;
 }
 
+/**
+ * Hide course category and child course and subcategories
+ * @param $category
+ * @return void
+ */
+function course_category_hide($category) {
+    global $DB;
+
+    $category->visible = 0;
+    $DB->set_field('course_categories', 'visible', 0, array('id'=>$category->id));
+    $DB->set_field('course_categories', 'visibleold', 0, array('id'=>$category->id));
+    $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($category->id)); // store visible flag so that we can return to it if we immediately unhide
+    $DB->set_field('course', 'visible', 0, array('category' => $category->id));
+    // get all child categories and hide too
+    if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
+        foreach ($subcats as $cat) {
+            $DB->set_field('course_categories', 'visibleold', $cat->visible, array('id'=>$cat->id));
+            $DB->set_field('course_categories', 'visible', 0, array('id'=>$cat->id));
+            $DB->execute("UPDATE {course} SET visibleold = visible WHERE category = ?", array($cat->id));
+            $DB->set_field('course', 'visible', 0, array('category' => $cat->id));
+        }
+    }
+}
+
+/**
+ * Show course category and child course and subcategories
+ * @param $category
+ * @return void
+ */
+function course_category_show($category) {
+    global $DB;
+
+    $category->visible = 1;
+    $DB->set_field('course_categories', 'visible', 1, array('id'=>$category->id));
+    $DB->set_field('course_categories', 'visibleold', 1, array('id'=>$category->id));
+    $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($category->id));
+    // get all child categories and unhide too
+    if ($subcats = $DB->get_records_select('course_categories', "path LIKE ?", array("$category->path/%"))) {
+        foreach ($subcats as $cat) {
+            if ($cat->visibleold) {
+                $DB->set_field('course_categories', 'visible', 1, array('id'=>$cat->id));
+            }
+            $DB->execute("UPDATE {course} SET visible = visibleold WHERE category = ?", array($cat->id));
+        }
+    }
+}
+
 /**
  * Efficiently moves a category - NOTE that this can have
  * a huge impact access-control-wise...
@@ -3237,6 +3272,7 @@ function move_category($category, $newparentcat) {
 
     $context = get_context_instance(CONTEXT_COURSECAT, $category->id);
 
+    $hidecat = false;
     if (empty($newparentcat->id)) {
         $DB->set_field('course_categories', 'parent', 0, array('id'=>$category->id));
 
@@ -3245,6 +3281,11 @@ function move_category($category, $newparentcat) {
     } else {
         $DB->set_field('course_categories', 'parent', $newparentcat->id, array('id'=>$category->id));
         $newparent = get_context_instance(CONTEXT_COURSECAT, $newparentcat->id);
+
+        if (!$newparentcat->visible and $category->visible) {
+            // better hide category when moving into hidden category, teachers may unhide afterwards and the hidden children will be restored properly
+            $hidecat = true;
+        }
     }
 
     context_moved($context, $newparent);
@@ -3254,6 +3295,10 @@ function move_category($category, $newparentcat) {
 
     // and fix the sortorders
     fix_course_sortorder();
+
+    if ($hidecat) {
+        course_category_hide($category);
+    }
 }
 
 /**
@@ -3361,7 +3406,7 @@ function save_local_role_names($courseid, $data) {
     $context = get_context_instance(CONTEXT_COURSE, $courseid);
 
     foreach ($data as $fieldname => $value) {
-        if (!strstr($fieldname, 'role_')) {
+        if (strpos($fieldname, 'role_') !== 0) {
             continue;
         }
         list($ignored, $roleid) = explode('_', $fieldname);
@@ -3385,158 +3430,181 @@ function save_local_role_names($courseid, $data) {
 }
 
 /**
- * Create a course and either return a $course object or false
+ * Create a course and either return a $course object
+ *
+ * Please note this functions does not verify any access control,
+ * the calling code is responsible for all validation (usually it is the form definition).
  *
+ * @param array $editoroptions course description editor options
  * @param object $data  - all the data needed for an entry in the 'course' table
+ * @return object new course instance
  */
-function create_course($data) {
-    global $CFG, $USER, $DB;
+function create_course($data, $editoroptions = NULL) {
+    global $CFG, $DB;
 
-    //check the categoryid
-    if (!empty($data->category) && !$data->category==0) {
-        $category = $DB->get_record('course_categories', array('id'=>$data->category));
-        if (empty($category)) {
-            throw new moodle_exception('noexistingcategory');
-        }
-    }
+    //check the categoryid - must be given for all new courses
+    $category = $DB->get_record('course_categories', array('id'=>$data->category), '*', MUST_EXIST);
 
     //check if the shortname already exist
-    if(!empty($data->shortname)) {
-        $course = $DB->get_record('course', array('shortname' => $data->shortname));
-        if (!empty($course)) {
+    if (!empty($data->shortname)) {
+        if ($DB->record_exists('course', array('shortname' => $data->shortname))) {
             throw new moodle_exception('shortnametaken');
         }
     }
 
     //check if the id number already exist
-    if(!empty($data->idnumber)) {
-        $course = $DB->get_record('course', array('idnumber' => $data->idnumber));
-        if (!empty($course)) {
+    if (!empty($data->idnumber)) {
+        if ($DB->record_exists('course', array('idnumber' => $data->idnumber))) {
             throw new moodle_exception('idnumbertaken');
         }
     }
 
+    $data->timecreated  = time();
+    $data->timemodified = $data->timecreated;
 
-    // preprocess allowed mods
-    $allowedmods = empty($data->allowedmods) ? array() : $data->allowedmods;
-    unset($data->allowedmods);
-    if ($CFG->restrictmodulesfor == 'all') {
-        $data->restrictmodules = 1;
+    // place at beginning of any category
+    $data->sortorder = 0;
 
-        // if the user is not an admin, get the default allowed modules because
-        // there are no modules passed by the form
-        if(!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
-            if(!$allowedmods && $CFG->defaultallowedmodules) {
-                $allowedmods = explode(',', $CFG->defaultallowedmodules);
-            }
-        }
+    if ($editoroptions) {
+        // summary text is updated later, we need context to store the files first
+        $data->summary = '';
+        $data->summary_format = FORMAT_HTML;
+    }
+
+    // init visible flags
+    if (isset($data->visible)) {
+        $data->visibleold = $data->visible;
     } else {
-        $data->restrictmodules = 0;
+        $data->visibleold = $data->visible = 1;
     }
 
-    $data->timecreated = time();
+    $newcourseid = $DB->insert_record('course', $data);
+    $context = get_context_instance(CONTEXT_COURSE, $newcourseid, MUST_EXIST);
 
-    // place at beginning of any category
-    $data->sortorder = 0;
+    if ($editoroptions) {
+        // Save the files used in the summary editor and store
+        $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course_summary', 0);
+        $DB->set_field('course', 'summary', $data->summary, array('id'=>$newcourseid));
+        $DB->set_field('course', 'summaryformat', $data->summary_format, array('id'=>$newcourseid));
+    }
 
-    if ($newcourseid = $DB->insert_record('course', $data)) {  // Set up new course
+    $course = $DB->get_record('course', array('id'=>$newcourseid));
 
-        $course = $DB->get_record('course', array('id'=>$newcourseid));
+    // Setup the blocks
+    blocks_add_default_course_blocks($course);
 
-        // Setup the blocks
-        blocks_add_default_course_blocks($course);
+    $section = new object();
+    $section->course        = $course->id;   // Create a default section.
+    $section->section       = 0;
+    $section->summaryformat = FORMAT_HTML;
+    $DB->insert_record('course_sections', $section);
 
-        update_restricted_mods($course, $allowedmods);
+    fix_course_sortorder();
 
-        $section = new object();
-        $section->course  = $course->id;   // Create a default section.
-        $section->section = 0;
-        $section->summaryformat = FORMAT_HTML;
-        $section->id = $DB->insert_record('course_sections', $section);
+    // update module restrictions
+    if ($course->restrictmodules) {
+        if (isset($data->allowedmods)) {
+            update_restricted_mods($course, $allowedmods);
+        } else {
+            if (!empty($CFG->defaultallowedmodules)) {
+                update_restricted_mods($course, explode(',', $CFG->defaultallowedmodules));
+            }
+        }
+    }
 
-        fix_course_sortorder();
+    // new context created - better mark it as dirty
+    mark_context_dirty($context->path);
 
-        add_to_log(SITEID, 'course', 'new', 'view.php?id='.$course->id, $data->fullname.' (ID '.$course->id.')');
+    // Save any custom role names.
+    save_local_role_names($course->id, $data);
 
-        // Save any custom role names.
-        save_local_role_names($course->id, $data);
+    // set up enrolments
+    enrol_course_updated(true, $course, $data);
 
-        // Trigger events
-        events_trigger('course_created', $course);
+    add_to_log(SITEID, 'course', 'new', 'view.php?id='.$course->id, $data->fullname.' (ID '.$course->id.')');
 
-        return $course;
-    }
+    // Trigger events
+    events_trigger('course_created', $course);
 
-    return false;   // error
+    return $course;
 }
 
 /**
- * Update a course and return true or false
+ * Update a course.
+ *
+ * Please note this functions does not verify any access control,
+ * the calling code is responsible for all validation (usually it is the form definition).
  *
  * @param object $data  - all the data needed for an entry in the 'course' table
+ * @param array $editoroptions course description editor options
+ * @return void
  */
-function update_course($data) {
-    global $USER, $CFG, $DB;
+function update_course($data, $editoroptions = NULL) {
+    global $CFG, $DB;
 
-    // Preprocess allowed mods
-    $allowedmods = empty($data->allowedmods) ? array() : $data->allowedmods;
-    unset($data->allowedmods);
+    $data->timemodified = time();
 
-    // Normal teachers can't change setting
-    if (!has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
-        unset($data->restrictmodules);
-    }
+    $oldcourse = $DB->get_record('course', array('id'=>$data->id), '*', MUST_EXIST);
+    $context   = get_context_instance(CONTEXT_COURSE, $oldcourse->id);
 
-    $movecat = false;
-    $oldcourse = $DB->get_record('course', array('id'=>$data->id)); // should not fail, already tested above
-    // check that course id exist
-    if (empty($oldcourse)) {
-       throw new moodle_exception('courseidnotfound');
+    if ($editoroptions) {
+        $data = file_postupdate_standard_editor($data, 'summary', $editoroptions, $context, 'course_summary', 0);
     }
 
-    if (!has_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $oldcourse->category))
-      or !has_capability('moodle/course:create', get_context_instance(CONTEXT_COURSECAT, $data->category))) {
-        // can not move to new category, keep the old one
+    if (!isset($data->category) or empty($data->category)) {
+        // prevent nulls and 0 in category field
         unset($data->category);
-
-    } elseif ($oldcourse->category != $data->category) {
-        $movecat = true;
     }
+    $movecat = (isset($data->category) and $oldcourse->category != $data->category);
 
-    // Update with the new data
-    if ($DB->update_record('course', $data)) {
-
-        $course = $DB->get_record('course', array('id'=>$data->id));
-
-        add_to_log($course->id, "course", "update", "edit.php?id=$course->id", $course->id);
-
-        // "Admins" can change allowed mods for a course
-        if (has_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM))) {
-            update_restricted_mods($course, $allowedmods);
-        }
+    // init visible flags
+    if (isset($data->visible)) {
+        $data->visible = $oldcourse->visible;
+    }
 
+    if ($data->visible != $oldcourse->visible) {
+        // reset the visibleold flag when manually hiding/unhiding course
+        $data->visibleold = $data->visible;
+    } else {
         if ($movecat) {
-            $context   = get_context_instance(CONTEXT_COURSE, $course->id);
-            $newparent = get_context_instance(CONTEXT_COURSECAT, $course->category);
-            context_moved($context, $newparent);
+            $newcategory = $DB->get_record('course_categories', array('id'=>$data->category));
+            if (empty($newcategory->visible)) {
+                // make sure when moving into hidden category the course is hidden automatically
+                $data->visible = 0;
+            }
         }
+    }
 
-        fix_course_sortorder();
+    // Update with the new data
+    $DB->update_record('course', $data);
 
-        // Test for and remove blocks which aren't appropriate anymore
-        blocks_remove_inappropriate($course);
+    $course = $DB->get_record('course', array('id'=>$data->id));
 
-        // Save any custom role names.
-        save_local_role_names($course->id, $data);
+    if ($movecat) {
+        $newparent = get_context_instance(CONTEXT_COURSECAT, $course->category);
+        context_moved($context, $newparent);
+    }
 
-        // Trigger events
-        events_trigger('course_updated', $course);
+    fix_course_sortorder();
 
-        return true;
+    // Test for and remove blocks which aren't appropriate anymore
+    blocks_remove_inappropriate($course);
 
+    // update module restrictions
+    if (isset($data->allowedmods)) {
+        update_restricted_mods($course, $allowedmods);
     }
 
-    return false;
+    // Save any custom role names.
+    save_local_role_names($course->id, $data);
+
+    // update enrol settings
+    enrol_course_updated(false, $course, $data);
+
+    add_to_log($course->id, "course", "update", "edit.php?id=$course->id", $course->id);
+
+    // Trigger events
+    events_trigger('course_updated', $course);
 }
 
 /**
@@ -3575,7 +3643,6 @@ function average_number_of_courses_modules() {
  * @property-read int $summarytrust
  * @property-read string $reason
  * @property-read int $requester
- * @property-read string $password
  */
 class course_request {
 
@@ -3845,26 +3912,22 @@ public function approve() {
         $course->showgrades         = $courseconfig->showgrades;
         $course->showreports        = $courseconfig->showreports;
         $course->maxbytes           = $courseconfig->maxbytes;
-        $course->enrol              = $courseconfig->enrol;
-        $course->enrollable         = $courseconfig->enrollable;
-        $course->enrolperiod        = $courseconfig->enrolperiod;
-        $course->expirynotify       = $courseconfig->expirynotify;
-        $course->notifystudents     = $courseconfig->notifystudents;
-        $course->expirythreshold    = $courseconfig->expirythreshold;
         $course->groupmode          = $courseconfig->groupmode;
         $course->groupmodeforce     = $courseconfig->groupmodeforce;
         $course->visible            = $courseconfig->visible;
-        $course->enrolpassword      = $courseconfig->enrolpassword;
-        $course->guest              = $courseconfig->guest;
+        $course->visibleold         = $course->visible;
         $course->lang               = $courseconfig->lang;
 
+        //TODO: use standard course creation function !!
+
         // Insert the record
         $course->id = $DB->insert_record('course', $course);
         if ($course->id) {
             $course = $DB->get_record('course', array('id' => $course->id));
             blocks_add_default_course_blocks($course);
             $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
-            role_assign($CFG->creatornewroleid, $this->properties->requester, 0, $coursecontext->id); // assing teacher role
+            // TODO: do some real enrolment here
+            role_assign($CFG->creatornewroleid, $this->properties->requester, $coursecontext->id); // assing teacher role
             if (!empty($CFG->restrictmodulesfor) && $CFG->restrictmodulesfor != 'none' && !empty($CFG->restrictbydefault)) {
                 // if we're all or requested we're ok.
                 $allowedmods = explode(',',$CFG->defaultallowedmodules);
@@ -3929,7 +3992,7 @@ protected function copy_summary_files_to_course($course) {
             foreach ($files as $file) {
                 $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
                 if (!$file->is_directory()) {
-                    $filerecord = array('contextid'=>$coursecontext->id, 'filearea'=>'course_summary', 'itemid'=>$course->id, 'filepath'=>$file->get_filepath(), 'filename'=>$file->get_filename());
+                    $filerecord = array('contextid'=>$coursecontext->id, 'filearea'=>'course_summary', 'itemid'=>0, 'filepath'=>$file->get_filepath(), 'filename'=>$file->get_filename());
                     $fs->create_file_from_storedfile($filerecord, $file);
                 }
             }
diff --git a/course/pending.php b/course/pending.php
index 54d502e87145..c2b92c4b0eab 100644
--- a/course/pending.php
+++ b/course/pending.php
@@ -115,12 +115,7 @@
         $course->check_shortname_collision();
 
         $row = array();
-        // Show an enrolment key icon in the first column if applicable.
-        if (!empty($course->password)) {
-            $row[] = $keyicon;
-        } else {
-            $row[] = '';
-        }
+        // TODO: Show an enrolment key icon in the first column if applicable.
         // Info in the other columns.
         $row[] = format_string($course->shortname);
         $row[] = format_string($course->fullname);
diff --git a/course/renderer.php b/course/renderer.php
index e23a9a65a282..eba9bc2f25e1 100644
--- a/course/renderer.php
+++ b/course/renderer.php
@@ -80,7 +80,7 @@ public function course_category_tree(array $structure) {
         $content .= html_writer::tag('div', get_string('expandall'), array('class'=>'removefromall collapseall'));
         $content .= html_writer::end_tag('div');
         $content .= html_writer::end_tag('div');
-        
+
         // Return the course category tree HTML
         return $content;
     }
@@ -128,18 +128,10 @@ protected function course_category_tree_category(stdClass $category, $depth=1) {
                 $content .= html_writer::link(new moodle_url('/course/view.php', array('id'=>$course->id)), $course->fullname, array('class'=>'course_link'));
                 $content .= html_writer::start_tag('div', array('class'=>'course_info clearfix'));
 
-                if ($course->guest ) {
-                    $image = html_writer::empty_tag('img', array('src'=>$this->output->pix_url('i/guest'), 'alt'=>$this->strings->allowguests, 'title'=>$this->strings->allowguests));
-                    $content .= html_writer::tag('div', $image, array('class'=>'course_info_spacer'));
-                } else {
-                    $content .= html_writer::tag('div', '', array('class'=>'course_info_spacer'));
-                }
-                if ($course->password) {
-                    $image = html_writer::empty_tag('img', array('src'=>$this->output->pix_url('i/key'), 'alt'=>$this->strings->requireskey, 'title'=>$this->strings->requireskey));
-                    $content .= html_writer::tag('div', $image, array('class'=>'course_info_spacer'));
-                } else {
-                    $content .= html_writer::tag('div', '', array('class'=>'course_info_spacer'));
-                }
+                //TODO: add enrol info
+                $content .= html_writer::tag('div', '', array('class'=>'course_info_spacer'));
+                $content .= html_writer::tag('div', '', array('class'=>'course_info_spacer'));
+
                 if ($course->summary) {
                     $image = html_writer::empty_tag('img', array('src'=>$this->output->pix_url('i/info'), 'alt'=>$this->strings->summary));
                     $content .= html_writer::link(new moodle_url('/course/info.php', array('id'=>$course->id)), $image, array('title'=>$this->strings->summary));
diff --git a/course/report/completion/lib.php b/course/report/completion/lib.php
index 3ca42ba701a6..9b2d12271e58 100644
--- a/course/report/completion/lib.php
+++ b/course/report/completion/lib.php
@@ -35,6 +35,8 @@ function completion_report_extend_navigation($navigation, $course, $context) {
     global $CFG, $OUTPUT;
 
     if (has_capability('coursereport/completion:view', $context)) {
+        require_once($CFG->libdir.'/completionlib.php');
+
         $url = new moodle_url('/course/report/completion/index.php', array('course'=>$course->id));
         $navigation->add(get_string('pluginname','coursereport_completion'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/report', ''));
     }
diff --git a/course/report/log/lib.php b/course/report/log/lib.php
index 7d1c92040e2f..3a7b6bc7a0fe 100644
--- a/course/report/log/lib.php
+++ b/course/report/log/lib.php
@@ -88,7 +88,7 @@ function print_mnet_log_selector_form($hostid, $course, $selecteduser=0, $select
 
     // If looking at a different host, we're interested in all our site users
     if ($hostid == $CFG->mnet_localhost_id && $course->id != SITEID) {
-        $courseusers = get_users_by_capability($context, 'moodle/course:participate', 'u.id, u.firstname, u.lastname, u.idnumber', 'lastname ASC, firstname ASC', $limitfrom, $limitnum, $selectedgroup,'', false);
+        $courseusers = get_enrolled_users($context, '', $selectedgroup, 'u.id, u.firstname, u.lastname, u.idnumber', 'lastname ASC, firstname ASC', $limitfrom, $limitnum);
     } else {
         // this may be a lot of users :-(
         $courseusers = $DB->get_records('user', array('deleted'=>0), 'lastaccess DESC', 'id, firstname, lastname, idnumber', $limitfrom, $limitnum);
@@ -356,12 +356,7 @@ function print_log_selector_form($course, $selecteduser=0, $selecteddate='today'
     // Get all the possible users
     $users = array();
 
-    if ($course->id != SITEID) {
-        $courseusers = get_users_by_capability($context, 'moodle/course:participate', 'u.id, u.firstname, u.lastname, u.idnumber', 'lastname ASC, firstname ASC', '','',$selectedgroup,null, false);
-    } else {
-        // this may be a lot of users :-(
-        $courseusers = $DB->get_records('user', array('deleted'=>0), 'lastaccess DESC', 'id, firstname, lastname, idnumber');
-    }
+    $courseusers = get_enrolled_users($context, '', $selectedgroup, 'u.id, u.firstname, u.lastname, u.idnumber', 'lastname ASC, firstname ASC');
 
     if (count($courseusers) < COURSE_MAX_USERS_PER_DROPDOWN && !$showusers) {
         $showusers = 1;
diff --git a/course/request_form.php b/course/request_form.php
index ed91bd02e20a..d7657b7f0181 100644
--- a/course/request_form.php
+++ b/course/request_form.php
@@ -72,11 +72,6 @@ function definition() {
         $mform->addHelpButton('summary_editor', 'coursesummary');
         $mform->setType('summary_editor', PARAM_RAW);
 
-        $mform->addElement('passwordunmask', 'password', get_string('enrolmentkey'), 'size="25"');
-        $mform->setHelpButton('password', array('enrolmentkey', get_string('enrolmentkey')), true);
-        $mform->setDefault('password', '');
-        $mform->setType('password', PARAM_RAW);
-
         $mform->addElement('header','requestreason', get_string('courserequestreason'));
 
         $mform->addElement('textarea', 'reason', get_string('courserequestsupport'), array('rows'=>'15', 'cols'=>'50'));
diff --git a/course/reset_form.php b/course/reset_form.php
index 20e5f546b880..773bbf1cb851 100644
--- a/course/reset_form.php
+++ b/course/reset_form.php
@@ -25,7 +25,7 @@ function definition (){
         $mform->addElement('header', 'rolesheader', get_string('roles'));
 
         $roles = get_assignable_roles(get_context_instance(CONTEXT_COURSE, $COURSE->id));
-        $mform->addElement('select', 'reset_roles', get_string('unenrolroleusers'), $roles, array('multiple' => 'multiple'));
+        $mform->addElement('select', 'unenrol_users', get_string('unenrolroleusers', 'enrol'), $roles, array('multiple' => 'multiple'));
         $mform->addElement('checkbox', 'reset_roles_overrides', get_string('deletecourseoverrides', 'role'));
         $mform->setAdvanced('reset_roles_overrides');
         $mform->addElement('checkbox', 'reset_roles_local', get_string('deletelocalroles', 'role'));
@@ -102,12 +102,6 @@ function load_defaults() {
 
         $defaults = array ('reset_events'=>1, 'reset_logs'=>1, 'reset_roles_local'=>1, 'reset_gradebook_grades'=>1, 'reset_notes'=>1);
 
-        if (!empty($COURSE->defaultrole)) {
-            $defaults['reset_roles'] = array($COURSE->defaultrole);
-        } else {
-            $defaults['reset_roles'] = array($CFG->defaultcourseroleid);
-        }
-
         if ($allmods = $DB->get_records('modules') ) {
             foreach ($allmods as $mod) {
                 $modname = $mod->name;
diff --git a/course/unenrol.php b/course/unenrol.php
deleted file mode 100644
index 97cde2d569f3..000000000000
--- a/course/unenrol.php
+++ /dev/null
@@ -1,123 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Remove oneself or someone else from a course, unassigning all roles one might have
- *
- * This will not delete any of their data from the course, but will remove them
- * from the participant list and prevent any course email being sent to them.
- *
- * @copyright 1999 Martin Dougiamas  http://dougiamas.com
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @package course
- */
-
-require_once("../config.php");
-require_once("lib.php");
-
-$id      = required_param('id', PARAM_INT);               //course
-$userid  = optional_param('user', 0, PARAM_INT);          //course
-$confirm = optional_param('confirm', 0, PARAM_BOOL);
-
-$PAGE->set_url('/course/unenrol.php', array('id'=>$id));
-
-if($userid == $USER->id){
-    // the rest of this code assumes $userid=0 means
-    // you are unassigning yourself, so set this for the
-    // correct capabiliy checks & language later
-    $userid = 0;
-}
-
-if (!$course = $DB->get_record('course', array('id'=>$id))) {
-    print_error('invalidcourseid');
-}
-
-if (! $context = get_context_instance(CONTEXT_COURSE, $course->id)) {
-    print_error('invalidcontext');
-}
-
-require_login($course->id);
-
-if ($course->metacourse) {
-    print_error('cantunenrollfrommetacourse', '', $CFG->wwwroot.'/course/view.php?id='.$course->id);
-}
-
-if ($userid) {   // Unenrolling someone else
-    require_capability('moodle/role:assign', $context, NULL, false);
-
-    $roles = get_user_roles($context, $userid, false);
-
-    // verify user may unassign all roles at course context
-    foreach($roles as $role) {
-        if (!user_can_assign($context, $role->roleid)) {
-            print_error('cannotunassignrolefrom', '', '',
-                    $role->roleid);
-        }
-    }
-
-} else {         // Unenrol yourself
-    require_capability('moodle/role:unassignself', $context, NULL, false);
-}
-
-if (!empty($USER->access['rsw'][$context->path])) {
-    print_error('cantunenrollinthisrole', '',
-                $CFG->wwwroot.'/course/view.php?id='.$course->id);
-}
-
-if ($confirm and confirm_sesskey()) {
-    if ($userid) {
-        if (! role_unassign(0, $userid, 0, $context->id)) {
-            print_error("unenrolerror");
-        }
-
-        add_to_log($course->id, 'course', 'unenrol',
-                "view.php?id=$course->id", $course->id);
-        redirect($CFG->wwwroot.'/user/index.php?id='.$course->id);
-
-    } else {
-        if (! role_unassign(0, $USER->id, 0, $context->id)) {
-            print_error("unenrolerror");
-        }
-
-        // force a refresh of mycourses
-        unset($USER->mycourses);
-        add_to_log($course->id, 'course', 'unenrol',
-                "view.php?id=$course->id", $course->id);
-
-        redirect($CFG->wwwroot);
-    }
-}
-
-
-$strunenrol = get_string('unenrol');
-$PAGE->navbar->add($strunenrol);
-$PAGE->set_title("$course->shortname: $strunenrol");
-$PAGE->set_heading($course->fullname);
-echo $OUTPUT->header();
-if ($userid) {
-    if (!$user = $DB->get_record('user', array('id'=>$userid))) {
-        print_error('nousers');
-    }
-    $strunenrolsure  = get_string('unenrolsure', '', fullname($user, true));
-    echo $OUTPUT->confirm($strunenrolsure, "unenrol.php?id=$id&user=$user->id&confirm=yes", $PAGE->url);
-} else {
-    $strunenrolsure  = get_string('unenrolsure', '', get_string("yourself"));
-    echo $OUTPUT->confirm($strunenrolsure, "unenrol.php?id=$id&confirm=yes", $PAGE->url);
-}
-
-echo $OUTPUT->footer();
-
diff --git a/course/user.php b/course/user.php
index c29f089237af..23a0ce71e792 100644
--- a/course/user.php
+++ b/course/user.php
@@ -53,8 +53,9 @@
 $coursecontext   = get_context_instance(CONTEXT_COURSE, $course->id);
 $personalcontext = get_context_instance(CONTEXT_USER, $user->id);
 
+require_login();
 $PAGE->set_pagelayout('admin');
-if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) and !has_capability('moodle/course:participate', $coursecontext)) {
+if (has_capability('moodle/user:viewuseractivitiesreport', $personalcontext) and !is_enrolled($coursecontext)) {
     // do not require parents to be enrolled in courses ;-)
     $PAGE->set_course($course);
 } else {
diff --git a/enrol/authorize/db/upgrade.php b/enrol/authorize/db/upgrade.php
index 424a71bc6baf..d63a5d747ab8 100644
--- a/enrol/authorize/db/upgrade.php
+++ b/enrol/authorize/db/upgrade.php
@@ -28,7 +28,7 @@ function xmldb_enrol_authorize_upgrade($oldversion) {
 
     //===== 1.9.0 upgrade line ======//
 
-    if ($result && $oldversion < 2008020500 && is_enabled_enrol('authorize')) {
+    if ($result && $oldversion < 2008020500 && enrol_is_enabled('authorize')) {
         require_once($CFG->dirroot.'/enrol/authorize/localfuncs.php');
         if (!check_curl_available()) {
             echo $OUTPUT->notification("You are using the authorize.net enrolment plugin for payment handling but cUrl is not available.
diff --git a/enrol/authorize/enrol.php b/enrol/authorize/enrol.php
index bc7816111bc9..c342baba27ae 100755
--- a/enrol/authorize/enrol.php
+++ b/enrol/authorize/enrol.php
@@ -1,6 +1,5 @@
 <?php
 
-require_once($CFG->dirroot.'/enrol/enrol.class.php');
 require_once($CFG->dirroot.'/enrol/authorize/const.php');
 require_once($CFG->dirroot.'/enrol/authorize/localfuncs.php');
 require_once($CFG->dirroot.'/enrol/authorize/authorizenet.class.php');
@@ -49,7 +48,7 @@ public function print_entry($course)
                 print_error('httpsrequired', 'enrol_authorize');
             } else {
                 $wwwsroot = str_replace('http:','https:', $CFG->wwwroot);
-                redirect("$wwwsroot/course/enrol.php?id=$course->id");
+                redirect("$wwwsroot/enrol/index.php?id=$course->id");
                 exit;
             }
         }
@@ -685,7 +684,8 @@ public function cron()
                     $timeend = $order->settletime + $course->enrolperiod;
                 }
                 $user = $DB->get_record('user', array('id'=>$order->userid));
-                if (role_assign($role->id, $user->id, 0, $context->id, $timestart, $timeend, 0, 'authorize')) {
+                // TODO: do some real enrolment here
+                if (role_assign($role->id, $user->id, $context->id, 'enrol_authorize')) {
                     $this->log .= "User($user->id) has been enrolled to course($course->id).\n";
                     if (!empty($CFG->enrol_mailstudents)) {
                         $sendem[] = $order->id;
diff --git a/enrol/authorize/locallib.php b/enrol/authorize/locallib.php
index a9620e08c6d8..9e010ce19d44 100644
--- a/enrol/authorize/locallib.php
+++ b/enrol/authorize/locallib.php
@@ -358,7 +358,7 @@ function authorize_print_order($orderid)
                     }
                     else {
                         if (!empty($unenrol)) {
-                            role_unassign(0, $order->userid, 0, $coursecontext->id);
+                            role_unassign_all(array('userid'=>$order->userid, 'contextid'=>$coursecontext->id, 'component'=>'enrol_authorize'), true, true);
                         }
                         redirect("$CFG->wwwroot/enrol/authorize/index.php?order=$orderid");
                     }
@@ -383,7 +383,7 @@ function authorize_print_order($orderid)
     elseif (ORDER_DELETE == $do && in_array(ORDER_DELETE, $statusandactions->actions)) {
         if ($confirm && confirm_sesskey()) {
             if (!empty($unenrol)) {
-                role_unassign(0, $order->userid, 0, $coursecontext->id);
+                role_unassign_all(array('userid'=>$order->userid, 'contextid'=>$coursecontext->id, 'component'=>'enrol_authorize'), true, true);
             }
             $DB->delete_records('enrol_authorize', array('id'=>$orderid));
             redirect("$CFG->wwwroot/enrol/authorize/index.php");
@@ -436,7 +436,7 @@ function authorize_print_order($orderid)
                     if (AN_APPROVED == AuthorizeNet::process($suborder, $message, $extra, AN_ACTION_VOID)) {
                         if (empty($CFG->an_test)) {
                             if (!empty($unenrol)) {
-                                role_unassign(0, $order->userid, 0, $coursecontext->id);
+                                role_unassign_all(array('userid'=>$order->userid, 'contextid'=>$coursecontext->id, 'component'=>'enrol_authorize'), true, true);
                             }
                             redirect("$CFG->wwwroot/enrol/authorize/index.php?order=$orderid");
                         }
diff --git a/enrol/authorize/uploadcsv.php b/enrol/authorize/uploadcsv.php
index e4c90c959f4a..d61df38f067f 100644
--- a/enrol/authorize/uploadcsv.php
+++ b/enrol/authorize/uploadcsv.php
@@ -215,7 +215,8 @@ function authorize_process_csv($filename) {
                     $timestart = time();
                     $timeend = $timestart + $course->enrolperiod;
                 }
-                if (role_assign($role->id, $user->id, 0, $coursecontext->id, $timestart, $timeend, 0, 'authorize')) {
+                //TODO: do some real enrolment here
+                if (role_assign($role->id, $user->id, $coursecontext->id, 'enrol_authorize')) {
                     $imported++;
                     if (!empty($CFG->enrol_mailstudents)) {
                         $sendem[] = $order->id;
diff --git a/enrol/category/cli/sync.php b/enrol/category/cli/sync.php
new file mode 100644
index 000000000000..37ce194b2160
--- /dev/null
+++ b/enrol/category/cli/sync.php
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * CLI sync for full category enrol synchronisation.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+if (isset($_SERVER['REMOTE_ADDR'])) {
+    error_log("enrol/category/cli/sync.php can not be called from web server!");
+    exit;
+}
+
+require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
+require_once("$CFG->dirroot/enrol/category/locallib.php");
+
+if (!enrol_is_enabled('category')) {
+     die('enrol_category plugin is disabled, sync is disabled');
+}
+
+enrol_category_sync_full();
diff --git a/enrol/category/db/access.php b/enrol/category/db/access.php
new file mode 100644
index 000000000000..afe295065217
--- /dev/null
+++ b/enrol/category/db/access.php
@@ -0,0 +1,38 @@
+<?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/>.
+
+/**
+ * Capabilities for category access plugin.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$capabilities = array(
+    // marks roles that have category role assignments synchronised to course enrolments
+    // overrides bellow system context are ignored (for performance reasons).
+    // by default his is not allowed in new installs, admins have to explicitly allow category enrolments
+    'enrol/category:synchronised' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_SYSTEM,
+        'legacy' => array(
+        )
+    ),
+);
+
+
diff --git a/enrol/category/db/events.php b/enrol/category/db/events.php
new file mode 100644
index 000000000000..9037d79fd78f
--- /dev/null
+++ b/enrol/category/db/events.php
@@ -0,0 +1,40 @@
+<?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/>.
+
+/**
+ * category enrolment plugin event handler definition.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/* List of handlers */
+$handlers = array (
+    'role_assigned' => array (
+        'handlerfile'      => '/enrol/category/locallib.php',
+        'handlerfunction'  => array('enrol_category_handler', 'role_assigned'),
+        'schedule'         => 'instant'
+    ),
+
+    'role_unassigned' => array (
+        'handlerfile'      => '/enrol/category/locallib.php',
+        'handlerfunction'  => array('enrol_category_handler', 'role_unassigned'),
+        'schedule'         => 'instant'
+    ),
+
+);
diff --git a/enrol/category/db/install.php b/enrol/category/db/install.php
new file mode 100644
index 000000000000..7299a3396de6
--- /dev/null
+++ b/enrol/category/db/install.php
@@ -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/>.
+
+/**
+ * category enrolment plugin installation.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+function xmldb_enrol_category_install() {
+    global $CFG, $DB;
+
+    if (!$DB->record_exists_select('role_assignments', "contextid IN (SELECT id FROM {context} WHERE contextlevel = ?)", array(CONTEXT_COURSECAT))) {
+        // fresh install or nobody used category enrol
+        return;
+    }
+
+    // existing sites need a way to keep category role enrols, but we do not want to encourage that on new sites
+
+    // extremely ugly hack, the sync depends on the capabilities so we unfortunately force update of caps here
+    // note: this is not officially supported and should not be copied elsewhere! :-(
+    update_capabilities('enrol_category');
+    $syscontext = get_context_instance(CONTEXT_SYSTEM);
+    $archetypes = array('student', 'teacher', 'editingteacher');
+    $enableplugin = false;
+    foreach ($archetypes as $archetype) {
+        $roles = get_archetype_roles($archetype);
+        foreach ($roles as $role) {
+            if (!$DB->record_exists_select('role_assignments', "roleid = ? AND contextid IN (SELECT id FROM {context} WHERE contextlevel = ?)", array($role->id, CONTEXT_COURSECAT))) {
+                continue;
+            }
+            assign_capability('enrol/category:synchronised', CAP_ALLOW, $role->id, $syscontext->id, true);
+            $levels = get_role_contextlevels($role->id);
+            $levels[] = CONTEXT_COURSECAT;
+            set_role_contextlevels($role->id, $levels);
+            $enableplugin = true;
+        }
+    }
+
+    if (!$enableplugin) {
+        return;
+    }
+
+    // enable this plugin
+    $enabledplugins = explode(',', $CFG->enrol_plugins_enabled);
+    $enabledplugins[] = 'category';
+    $enabledplugins = array_unique($enabledplugins);
+    set_config('enrol_plugins_enabled', implode(',', $enabledplugins));
+
+    // brute force course resync, this may take a while
+    require_once("$CFG->dirroot/enrol/category/locallib.php");
+    enrol_category_sync_full();
+}
diff --git a/enrol/category/lang/en/enrol_category.php b/enrol/category/lang/en/enrol_category.php
new file mode 100644
index 000000000000..7a1344940ac7
--- /dev/null
+++ b/enrol/category/lang/en/enrol_category.php
@@ -0,0 +1,28 @@
+<?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/>.
+
+/**
+ * Strings for component 'enrol_category', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['category:synchronised'] = 'Role assignments synchronised to course enrolment';
+$string['pluginname'] = 'Category enrolments';
+$string['pluginname_desc'] = 'Category enrolment plugin is a legacy solution for enrolments at the course category level via role assignments. It is recommended to use cohort synchronisation instead.';
diff --git a/enrol/category/lib.php b/enrol/category/lib.php
new file mode 100644
index 000000000000..01f3466b9192
--- /dev/null
+++ b/enrol/category/lib.php
@@ -0,0 +1,97 @@
+<?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/>.
+
+/**
+ * Category enrolment plugin.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * category enrolment plugin implementation.
+ * @author Petr Skoda
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class enrol_category_plugin extends enrol_plugin {
+
+   /**
+     * Is it possible to delete enrol instance via standard UI?
+     *
+     * @param object $instance
+     * @return bool
+     */
+    public function instance_deleteable($instance) {
+        global $DB;
+
+        if (!enrol_is_enabled('category')) {
+            return true;
+        }
+        // allow delete only when no synced users here
+        return !$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id));
+    }
+
+    /**
+     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * @param int $courseid
+     * @return moodle_url page url
+     */
+    public function get_candidate_link($courseid) {
+        // instances are added automatically as necessary
+        return NULL;
+    }
+
+    /**
+     * Called for all enabled enrol plugins that returned true from is_cron_required().
+     * @return void
+     */
+    public function cron() {
+        global $CFG;
+
+        if (!enrol_is_enabled('category')) {
+            return;
+        }
+
+        require_once("$CFG->dirroot/enrol/category/locallib.php");
+        enrol_category_sync_full();
+    }
+
+    /**
+     * Called after updating/inserting course.
+     *
+     * @param bool $inserted true if course just inserted
+     * @param object $course
+     * @param object $data form data
+     * @return void
+     */
+    public function course_updated($inserted, $course, $data) {
+        global $CFG;
+
+        if (!enrol_is_enabled('category')) {
+            return;
+        }
+
+        // sync category enrols
+        require_once("$CFG->dirroot/enrol/category/locallib.php");
+        enrol_category_sync_course($course);
+    }
+}
+
+
diff --git a/enrol/category/locallib.php b/enrol/category/locallib.php
new file mode 100644
index 000000000000..815d0fa278e4
--- /dev/null
+++ b/enrol/category/locallib.php
@@ -0,0 +1,323 @@
+<?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/>.
+
+/**
+ * Local stuff for category enrolment plugin.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Event handler for category enrolment plugin.
+ *
+ * We try to keep everything in sync via listening to events,
+ * it may fail sometimes, so we always do a full sync in cron too.
+ */
+class enrol_category_handler {
+    public function role_assigned($ra) {
+        global $DB;
+
+        if (!enrol_is_enabled('category')) {
+            return true;
+        }
+
+        //only category level roles are interesting
+        $parentcontext = get_context_instance_by_id($ra->contextid);
+        if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
+            return true;
+        }
+
+        // make sure the role is to be actually synchronised
+        // please note we are ignoring overrides of the synchronised capability (for performance reasons in full sync)
+        $syscontext = get_context_instance(CONTEXT_SYSTEM);
+        if (!$DB->record_exists('role_capabilities', array('contextid'=>$syscontext->id, 'roleid'=>$ra->roleid, 'capability'=>'enrol/category:synchronised', 'permission'=>CAP_ALLOW))) {
+            return true;
+        }
+
+        // add necessary enrol instances
+        $plugin = enrol_get_plugin('category');
+        $sql = "SELECT c.*
+                  FROM {course} c
+                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel AND ctx.path LIKE :match)
+             LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
+                 WHERE e.id IS NULL";
+        $params = array('courselevel'=>CONTEXT_COURSE, 'match'=>$parentcontext->path.'/%');
+        $rs = $DB->get_recordset_sql($sql, $params);
+        foreach ($rs as $course) {
+            $plugin->add_instance($course);
+        }
+        $rs->close();
+
+        // now look for missing enrols
+        $sql = "SELECT e.*
+                  FROM {course} c
+                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel AND ctx.path LIKE :match)
+                  JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
+             LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
+                 WHERE ue.id IS NULL";
+        $params = array('courselevel'=>CONTEXT_COURSE, 'match'=>$parentcontext->path.'/%', 'userid'=>$ra->userid);
+        $rs = $DB->get_recordset_sql($sql, $params);
+        foreach ($rs as $instance) {
+            $plugin->enrol_user($instance, $ra->userid);
+        }
+        $rs->close();
+
+        return true;
+    }
+
+    public function role_unassigned($ra) {
+        global $DB;
+
+        if (!enrol_is_enabled('category')) {
+            return true;
+        }
+
+        // only category level roles are interesting
+        $parentcontext = get_context_instance_by_id($ra->contextid);
+        if ($parentcontext->contextlevel != CONTEXT_COURSECAT) {
+            return true;
+        }
+
+        // now this is going to be a bit slow, take all enrolments in child courses and verify each separately
+        $syscontext = get_context_instance(CONTEXT_SYSTEM);
+        $roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
+
+        $plugin = enrol_get_plugin('category');
+
+        $sql = "SELECT e.*
+                  FROM {course} c
+                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel AND ctx.path LIKE :match)
+                  JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
+                  JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)";
+        $params = array('courselevel'=>CONTEXT_COURSE, 'match'=>$parentcontext->path.'/%', 'userid'=>$ra->userid);
+        $rs = $DB->get_recordset_sql($sql, $params);
+
+        list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r000');
+        $params['userid'] = $ra->userid;
+
+        foreach ($rs as $instance) {
+            $coursecontext = get_context_instance(CONTEXT_COURSE, $instance->courseid);
+            $contextids = get_parent_contexts($coursecontext);
+            array_pop($contextids); // remove system context, we are interested in categories only
+
+            list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c000');
+            $params = array_merge($params, $contextparams);
+
+            $sql = "SELECT ra.id
+                      FROM {role_assignments} ra
+                     WHERE ra.userid = :userid AND ra.contextid $contextids AND ra.roleid $roleids";
+            if (!$DB->record_exists_sql($sql, $params)) {
+                // user does not have any interesting role in any parent context, let's unenrol
+                $plugin->unenrol_user($instance, $ra->userid);
+            }
+        }
+        $rs->close();
+
+        return true;
+    }
+}
+
+/**
+ * Sync all category enrolments in one course
+ * @param int $courseid course id
+ * @return void
+ */
+function enrol_category_sync_course($course) {
+    global $DB;
+
+    if (!enrol_is_enabled('category')) {
+        return;
+    }
+
+    $plugin = enrol_get_plugin('category');
+
+    $syscontext = get_context_instance(CONTEXT_SYSTEM);
+    $roles = get_roles_with_capability('enrol/category:synchronised', CAP_ALLOW, $syscontext);
+
+    // first find out if any parent category context contains interesting role assignments
+    $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+    $contextids = get_parent_contexts($coursecontext);
+    array_pop($contextids); // remove system context, we are interested in categories only
+
+    list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r000');
+    list($contextids, $contextparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'c000');
+    $params = array_merge($params, $contextparams);
+    $params['courseid'] = $course->id;
+
+    $sql = "SELECT 'x'
+              FROM {role_assignments}
+             WHERE roleid $roleids AND contextid $contextids";
+    if (!$DB->record_exists_sql($sql, $params)) {
+        if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
+            // should be max one instance, but anyway
+            foreach ($instances as $instance) {
+                $plugin->delete_instance($instance);
+            }
+        }
+        return;
+    }
+
+    // make sure the enrol instance exists - there should be always only one instance
+    $delinstances = array();
+    if ($instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'category'))) {
+        $instance = array_shift($instances);
+        $delinstances = $instances;
+    } else {
+        $i = $plugin->add_instance($course);
+        $instance = $DB->get_record('enrol', array('id'=>$i));
+    }
+
+    // add new enrolments
+    $sql = "SELECT ra.userid
+              FROM (SELECT DISTINCT xra.userid
+                      FROM {role_assignments} xra
+                     WHERE xra.roleid $roleids AND xra.contextid $contextids
+                   ) ra
+         LEFT JOIN {user_enrolments} ue ON (ue.enrolid = :instanceid AND ue.userid = ra.userid)
+             WHERE ue.id IS NULL";
+    $params['instanceid'] = $instance->id;
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach ($rs as $ra) {
+        $plugin->enrol_user($instance, $ra->userid);
+    }
+    $rs->close();
+
+    // remove unwanted enrolments
+    $sql = "SELECT DISTINCT ue.userid
+              FROM {user_enrolments} ue
+         LEFT JOIN {role_assignments} ra ON (ra.roleid $roleids AND ra.contextid $contextids AND ra.userid = ue.userid)
+             WHERE ue.enrolid = :instanceid AND ra.id IS NULL";
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach ($rs as $ra) {
+        $plugin->unenrol_user($instance, $ra->userid);
+    }
+    $rs->close();
+
+    if ($delinstances) {
+        // we have to do this as the last step in order to prevent temporary unenrolment
+        foreach ($delinstances as $delinstance) {
+            $plugin->delete_instance($delinstance);
+        }
+    }
+}
+
+function enrol_category_sync_full() {
+    global $DB;
+
+
+    if (!enrol_is_enabled('category')) {
+        return;
+    }
+
+    // we may need a lot of time here
+    @set_time_limit(0);
+
+    $plugin = enrol_get_plugin('category');
+
+    $syscontext = get_context_instance(CONTEXT_SYSTEM);
+
+    // 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 ($instances = $DB->get_records('enrol', array('enrol'=>'category'))) {
+            $plugin->delete_instance($instance);
+        }
+    }
+
+    list($roleids, $params) = $DB->get_in_or_equal(array_keys($roles), SQL_PARAMS_NAMED, 'r000');
+    $params['courselevel'] = CONTEXT_COURSE;
+    $params['catlevel'] = CONTEXT_COURSECAT;
+
+    // first of all add necessay enrol instances to all courses
+    $parentcat = $DB->sql_concat("cat.path", "'/%'");
+    $sql = "SELECT DISTINCT c.*
+              FROM {course} c
+              JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = :courselevel)
+              JOIN (SELECT DISTINCT cctx.path
+                      FROM {course_categories} cc
+                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
+                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
+                   ) cat ON (ctx.path LIKE $parentcat)
+         LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'category')
+             WHERE e.id IS NULL";
+
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach($rs as $course) {
+        $plugin->add_instance($course);
+    }
+    $rs->close();
+
+    // now look for courses that do not have any interesting roles in parent contexts,
+    // but still have the instance and delete them
+    $sql = "SELECT e.*
+              FROM {enrol} e
+              JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
+         LEFT JOIN (SELECT DISTINCT cctx.path
+                      FROM {course_categories} cc
+                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
+                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
+                   ) cat ON (ctx.path LIKE $parentcat)
+             WHERE e.enrol = 'category' AND cat.path IS NULL";
+
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach($rs as $instance) {
+        $plugin->delete_instance($instance);
+    }
+    $rs->close();
+
+    // add missing enrolments
+    $sql = "SELECT e.*, cat.userid
+              FROM {enrol} e
+              JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
+              JOIN (SELECT DISTINCT cctx.path, ra.userid
+                      FROM {course_categories} cc
+                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
+                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
+                   ) cat ON (ctx.path LIKE $parentcat)
+         LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = cat.userid)
+             WHERE e.enrol = 'category' AND ue.id IS NULL";
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach($rs as $instance) {
+        $userid = $instance->userid;
+        unset($instance->userid);
+        $plugin->enrol_user($instance, $userid);
+    }
+    $rs->close();
+
+    // remove stale enrolments
+    $sql = "SELECT e.*, ue.userid
+              FROM {enrol} e
+              JOIN {context} ctx ON (ctx.instanceid = e.courseid AND ctx.contextlevel = :courselevel)
+              JOIN {user_enrolments} ue ON (ue.enrolid = e.id)
+         LEFT JOIN (SELECT DISTINCT cctx.path, ra.userid
+                      FROM {course_categories} cc
+                      JOIN {context} cctx ON (cctx.instanceid = cc.id AND cctx.contextlevel = :catlevel)
+                      JOIN {role_assignments} ra ON (ra.contextid = cctx.id AND ra.roleid $roleids)
+                   ) cat ON (ctx.path LIKE $parentcat AND cat.userid = ue.userid)
+             WHERE e.enrol = 'category' AND cat.userid IS NULL";
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach($rs as $instance) {
+        $userid = $instance->userid;
+        unset($instance->userid);
+        $plugin->unenrol_user($instance, $userid);
+    }
+    $rs->close();
+}
diff --git a/enrol/category/settings.php b/enrol/category/settings.php
new file mode 100644
index 000000000000..653fe1f1fb7b
--- /dev/null
+++ b/enrol/category/settings.php
@@ -0,0 +1,37 @@
+<?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/>.
+
+/**
+ * category enrolment plugin settings and presets.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_category_settings', '', get_string('pluginname_desc', 'enrol_category')));
+
+
+    //--- enrol instance defaults ----------------------------------------------------------------------------
+
+}
+
diff --git a/enrol/category/version.php b/enrol/category/version.php
new file mode 100644
index 000000000000..e14f1bfae4f7
--- /dev/null
+++ b/enrol/category/version.php
@@ -0,0 +1,27 @@
+<?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/>.
+
+/**
+ * Category enrolment plugin version specification.
+ *
+ * @package   enrol_category
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$plugin->version = 2010061500;
+$plugin->cron    = 60;
\ No newline at end of file
diff --git a/enrol/cohort/addinstance.php b/enrol/cohort/addinstance.php
new file mode 100644
index 000000000000..77fcc45a7421
--- /dev/null
+++ b/enrol/cohort/addinstance.php
@@ -0,0 +1,60 @@
+<?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/>.
+
+/**
+ * Adds new instance of enrol_cohort to specified course.
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+require_once("$CFG->dirroot/enrol/cohort/addinstance_form.php");
+require_once("$CFG->dirroot/enrol/cohort/locallib.php");
+
+$id = required_param('id', PARAM_INT); // course id
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('moodle/course:enrolconfig', $context);
+
+$enrol = enrol_get_plugin('cohort');
+if (!$enrol->get_candidate_link($course->id)) {
+    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
+}
+
+$mform = new enrol_cohort_addinstance_form(NULL, $course);
+
+if ($mform->is_cancelled()) {
+    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
+
+} else if ($data = $mform->get_data()) {
+    $enrol->add_instance($course, array('customint1'=>$data->cohortid, 'roleid'=>$data->roleid));
+    enrol_cohort_sync($course->id);
+    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
+}
+
+$PAGE->set_url('/enrol/cohort/addinstance.php', array('id'=>$course->id));
+
+echo $OUTPUT->header();
+
+$mform->display();
+
+echo $OUTPUT->footer();
diff --git a/enrol/cohort/addinstance_form.php b/enrol/cohort/addinstance_form.php
new file mode 100644
index 000000000000..b7b2a933cc9d
--- /dev/null
+++ b/enrol/cohort/addinstance_form.php
@@ -0,0 +1,72 @@
+<?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/>.
+
+/**
+ * Adds instance form
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/formslib.php");
+
+class enrol_cohort_addinstance_form extends moodleform {
+    function definition() {
+        global $CFG, $DB;
+
+        $mform  = $this->_form;
+        $course = $this->_customdata;
+        $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+
+        $enrol = enrol_get_plugin('cohort');
+
+        //TODO: add only cohorts from parent contexts of this course
+        $cohorts = array('' => get_string('choosedots'));
+        $rs = $DB->get_recordset('cohort', array(), 'name ASC', 'id, name, contextid');
+        foreach ($rs as $c) {
+            $context = get_context_instance_by_id($c->contextid);
+            if (!has_capability('moodle/cohort:view', $context)) {
+                continue;
+            }
+            $cohorts[$c->id] = format_string($c->name);
+        }
+        $rs->close();
+
+        $roles = get_assignable_roles($coursecontext);
+
+        $mform->addElement('header','general', get_string('pluginname', 'enrol_cohort'));
+
+        $mform->addElement('select', 'cohortid', get_string('cohort', 'cohort'), $cohorts);
+        $mform->addRule('cohortid', get_string('required'), 'required', null, 'client');
+
+        $mform->addElement('select', 'roleid', get_string('role'), $roles);
+        $mform->addRule('roleid', get_string('required'), 'required', null, 'client');
+        $mform->setDefault('roleid', $enrol->get_config('roleid'));
+
+        $mform->addElement('hidden', 'id', null);
+        $mform->setType('id', PARAM_INT);
+
+        $this->add_action_buttons();
+
+        $this->set_data(array('id'=>$course->id));
+    }
+
+    //TODO: validate duplicate role-cohort does not exist
+}
diff --git a/enrol/cohort/db/access.php b/enrol/cohort/db/access.php
new file mode 100644
index 000000000000..c43116ba9089
--- /dev/null
+++ b/enrol/cohort/db/access.php
@@ -0,0 +1,37 @@
+<?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/>.
+
+/**
+ * Capabilities for cohort access plugin.
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$capabilities = array(
+    // configure the cohort plugin settings in each course
+    'enrol/cohort:config' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+        )
+    ),
+);
+
+
diff --git a/enrol/cohort/db/events.php b/enrol/cohort/db/events.php
new file mode 100644
index 000000000000..c4768281a52e
--- /dev/null
+++ b/enrol/cohort/db/events.php
@@ -0,0 +1,45 @@
+<?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/>.
+
+/**
+ * Cohort enrolment plugin event handler definition.
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/* List of handlers */
+$handlers = array (
+    'cohort_member_added' => array (
+        'handlerfile'      => '/enrol/cohort/locallib.php',
+        'handlerfunction'  => array('enrol_cohort_handler', 'member_added'),
+        'schedule'         => 'instant'
+    ),
+
+    'cohort_member_removed' => array (
+        'handlerfile'      => '/enrol/cohort/locallib.php',
+        'handlerfunction'  => array('enrol_cohort_handler', 'member_removed'),
+        'schedule'         => 'instant'
+    ),
+
+    'cohort_deleted' => array (
+        'handlerfile'      => '/enrol/cohort/locallib.php',
+        'handlerfunction'  => array('enrol_cohort_handler', 'deleted'),
+        'schedule'         => 'instant'
+    ),
+);
diff --git a/enrol/cohort/lang/en/enrol_cohort.php b/enrol/cohort/lang/en/enrol_cohort.php
new file mode 100644
index 000000000000..d5fa00647df0
--- /dev/null
+++ b/enrol/cohort/lang/en/enrol_cohort.php
@@ -0,0 +1,28 @@
+<?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/>.
+
+/**
+ * Strings for component 'enrol_cohort', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['cohort:config'] = 'Configure cohort instances';
+$string['pluginname'] = 'Cohort sync';
+$string['pluginname_desc'] = 'Cohort enrolment plugin synchronises cohort members with course participants.';
diff --git a/enrol/cohort/lib.php b/enrol/cohort/lib.php
new file mode 100644
index 000000000000..cb4836c85b12
--- /dev/null
+++ b/enrol/cohort/lib.php
@@ -0,0 +1,118 @@
+<?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/>.
+
+/**
+ * Cohort enrolment plugin.
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Cohort enrolment plugin implementation.
+ * @author Petr Skoda
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class enrol_cohort_plugin extends enrol_plugin {
+    /**
+     * Returns localised name of enrol instance
+     *
+     * @param object $instance (null is accepted too)
+     * @return string
+     */
+    public function get_instance_name($instance) {
+        global $DB;
+
+        if (empty($instance)) {
+            $enrol = $this->get_name();
+            return get_string('pluginname', 'enrol_'.$enrol);
+        } else if (empty($instance->name)) {
+            $enrol = $this->get_name();
+            if ($role = $DB->get_record('role', array('id'=>$instance->roleid))) {
+                $role = role_get_name($role, get_context_instance(CONTEXT_COURSE, $instance->courseid));
+            } else {
+                $role = get_string('error');
+            }
+
+            return get_string('pluginname', 'enrol_'.$enrol) . ' (' . format_string($DB->get_field('cohort', 'name', array('id'=>$instance->customint1))) . ' - ' . $role .')';
+        } else {
+            return format_string($instance->name);
+        }
+    }
+
+    /**
+     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * @param int $courseid
+     * @return moodle_url page url
+     */
+    public function get_candidate_link($courseid) {
+        global $DB;
+
+        if (!has_capability('moodle/course:enrolconfig', get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST))) {
+            return NULL;
+        }
+        if (!$DB->record_exists('cohort', array())) {
+            //TODO: consider only parent contexts
+            return NULL;
+        }
+        // multiple instances supported - multiple parent courses linked
+        return new moodle_url('/enrol/cohort/addinstance.php', array('id'=>$courseid));
+    }
+
+    /**
+     * Called for all enabled enrol plugins that returned true from is_cron_required().
+     * @return void
+     */
+    public function cron() {
+        global $CFG;
+
+        // purge all roles if cohort sync disabled, those can be recreated later here in cron
+        if (!enrol_is_enabled('cohort')) {
+            role_unassign_all(array('component'=>'cohort_enrol'));
+            return;
+        }
+
+        require_once("$CFG->dirroot/enrol/cohort/locallib.php");
+        enrol_cohort_sync();
+    }
+
+    /**
+     * Called after updating/inserting course.
+     *
+     * @param bool $inserted true if course just inserted
+     * @param object $course
+     * @param object $data form data
+     * @return void
+     */
+    public function course_updated($inserted, $course, $data) {
+        global $CFG;
+
+        if (!$inserted) {
+            // sync cohort enrols
+            require_once("$CFG->dirroot/enrol/cohort/locallib.php");
+            enrol_cohort_sync($course->id);
+        } else {
+            // cohorts are never inserted automatically
+        }
+
+    }
+}
+
+
diff --git a/enrol/cohort/locallib.php b/enrol/cohort/locallib.php
new file mode 100644
index 000000000000..b71872d24b2f
--- /dev/null
+++ b/enrol/cohort/locallib.php
@@ -0,0 +1,189 @@
+<?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/>.
+
+/**
+ * Local stuff for cohort enrolment plugin.
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Event handler for cohort enrolment plugin.
+ *
+ * We try to keep everything in sync via listening to events,
+ * it may fail sometimes, so we always do a full sync in cron too.
+ */
+class enrol_cohort_handler {
+    public function member_added($ca) {
+        global $DB;
+
+        if (!enrol_is_enabled('cohort')) {
+            return true;
+        }
+
+        // does anything want to sync with this parent?
+        //TODO: add join to role table to make sure that roleid actually exists
+        if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ca->cohortid, 'enrol'=>'cohort'), 'id ASC')) {
+            return true;
+        }
+
+        $plugin = enrol_get_plugin('cohort');
+        foreach ($enrols as $enrol) {
+            // no problem if already enrolled
+            $plugin->enrol_user($enrol, $ca->userid, $enrol->roleid);
+        }
+
+        return true;
+    }
+
+    public function member_removed($ca) {
+        global $DB;
+
+        // does anything want to sync with this parent?
+        if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ca->cohortid, 'enrol'=>'cohort'), 'id ASC')) {
+            return true;
+        }
+
+        $plugin = enrol_get_plugin('cohort');
+        foreach ($enrols as $enrol) {
+            // no problem if already enrolled
+            $plugin->unenrol_user($enrol, $ca->userid);
+        }
+
+        return true;
+    }
+
+    public function deleted($cohort) {
+        global $DB;
+
+        // does anything want to sync with this parent?
+        if (!$enrols = $DB->get_records('enrol', array('customint1'=>$cohort->id, 'enrol'=>'cohort'), 'id ASC')) {
+            return true;
+        }
+
+        $plugin = enrol_get_plugin('cohort');
+        foreach ($enrols as $enrol) {
+            $plugin->delete_instance($enrol);
+        }
+
+        return true;
+    }
+}
+
+/**
+ * Sync all cohort course links.
+ * @param int $courseid one course, empty mean all
+ * @return void
+ */
+function enrol_cohort_sync($courseid = NULL) {
+    global $CFG, $DB;
+
+    // unfortunately this may take a long time
+    @set_time_limit(0); //if this fails during upgrade we can continue from cron, no big deal
+
+    $cohort = enrol_get_plugin('cohort');
+
+    $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+
+    // iterate through all not enrolled yet users
+    if (enrol_is_enabled('cohort')) {
+        $params = array();
+        $onecourse = "";
+        if ($courseid) {
+            $params['courseid'] = $courseid;
+            $onecourse = "AND e.courseid = :courseid";
+        }
+        $sql = "SELECT cm.userid, e.id AS enrolid
+                  FROM {cohort_members} cm
+                  JOIN {enrol} e ON (e.customint1 = cm.cohortid AND e.status = :statusenabled AND e.enrol = 'cohort' $onecourse)
+             LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = cm.userid)
+                 WHERE ue.id IS NULL";
+        $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
+        $params['courseid'] = $courseid;
+        $rs = $DB->get_recordset_sql($sql, $params);
+        $instances = array(); //cache
+        foreach($rs as $ue) {
+            if (!isset($instances[$ue->enrolid])) {
+                $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+            }
+            $cohort->enrol_user($instances[$ue->enrolid], $ue->userid);
+        }
+        $rs->close();
+        unset($instances);
+    }
+
+    // unenrol as necessary - ignore enabled flag, we want to get rid of all
+    $sql = "SELECT ue.userid, e.id AS enrolid
+              FROM {user_enrolments} ue
+              JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'cohort' $onecourse)
+         LEFT JOIN {cohort_members} cm ON (cm.cohortid  = e.customint1 AND cm.userid = ue.userid)
+             WHERE cm.id IS NULL";
+    //TODO: this may use a bit of SQL optimisation
+    $rs = $DB->get_recordset_sql($sql, array('courseid'=>$courseid));
+    $instances = array(); //cache
+    foreach($rs as $ue) {
+        if (!isset($instances[$ue->enrolid])) {
+            $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+        }
+        $cohort->unenrol_user($instances[$ue->enrolid], $ue->userid);
+    }
+    $rs->close();
+    unset($instances);
+
+    // now assign all necessary roles
+    if (enrol_is_enabled('cohort')) {
+        $sql = "SELECT e.roleid, ue.userid, c.id AS contextid, e.id AS itemid
+                  FROM {user_enrolments} ue
+                  JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'cohort' AND e.status = :statusenabled $onecourse)
+                  JOIN {context} c ON (c.instanceid = e.courseid AND c.contextlevel = :coursecontext)
+             LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.userid = ue.userid AND ra.itemid = e.id AND ra.component = 'enrol_cohort' AND e.roleid = ra.roleid)
+                 WHERE ra.id IS NULL";
+        $params = array();
+        $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
+        $params['coursecontext'] = CONTEXT_COURSE;
+        $params['courseid'] = $courseid;
+
+        $rs = $DB->get_recordset_sql($sql, $params);
+        foreach($rs as $ra) {
+            role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_cohort', $ra->itemid);
+        }
+        $rs->close();
+    }
+
+    // remove unwanted roles - include ignored roles and disabled plugins too
+    $onecourse = $courseid ? "AND c.instanceid = :courseid" : "";
+    $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid
+              FROM {role_assignments} ra
+              JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = :coursecontext $onecourse)
+         LEFT JOIN (SELECT e.id AS enrolid, e.roleid, ue.userid
+                      FROM {user_enrolments} ue
+                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'cohort')
+                   ) x ON (x.enrolid = ra.itemid AND ra.component = 'enrol_cohort' AND x.roleid = ra.roleid AND x.userid = ra.userid)
+             WHERE x.userid IS NULL AND ra.component = 'enrol_cohort'";
+    $params = array('coursecontext' => CONTEXT_COURSE, 'courseid' => $courseid);
+
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach($rs as $ra) {
+        role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_cohort', $ra->itemid);
+    }
+    $rs->close();
+
+}
diff --git a/enrol/cohort/settings.php b/enrol/cohort/settings.php
new file mode 100644
index 000000000000..07b1c97f631f
--- /dev/null
+++ b/enrol/cohort/settings.php
@@ -0,0 +1,44 @@
+<?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/>.
+
+/**
+ * Cohort enrolment plugin settings and presets.
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_cohort_settings', '', get_string('pluginname_desc', 'enrol_cohort')));
+
+
+    //--- enrol instance defaults ----------------------------------------------------------------------------
+    if (!during_initial_install()) {
+        $options = get_default_enrol_roles(get_context_instance(CONTEXT_SYSTEM));
+        $student = get_archetype_roles('student');
+        $student = reset($student);
+        $settings->add(new admin_setting_configselect_with_advanced('enrol_cohort/roleid',
+            get_string('defaultrole', 'role'), '',
+            array('value'=>$student->id, 'adv'=>true), $options));
+    }
+}
+
diff --git a/enrol/cohort/version.php b/enrol/cohort/version.php
new file mode 100644
index 000000000000..f992e51c2e04
--- /dev/null
+++ b/enrol/cohort/version.php
@@ -0,0 +1,27 @@
+<?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/>.
+
+/**
+ * Cohort enrolment plugin version specification.
+ *
+ * @package   enrol_cohort
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$plugin->version = 2010061500;
+$plugin->cron    = 60;
\ No newline at end of file
diff --git a/enrol/database/cli/sync.php b/enrol/database/cli/sync.php
new file mode 100644
index 000000000000..e6daa145e87b
--- /dev/null
+++ b/enrol/database/cli/sync.php
@@ -0,0 +1,40 @@
+<?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/>.
+
+/**
+ * CLI sync for full external database synchronisation.
+ *
+ * @package   enrol_database
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+if (isset($_SERVER['REMOTE_ADDR'])) {
+    error_log("enrol/database/cli/sync.php can not be called from web server!");
+    exit;
+}
+
+require(dirname(dirname(dirname(dirname(__FILE__)))).'/config.php');
+
+if (!enrol_is_enabled('database')) {
+     die('enrol_database plugin is disabled, sync is disabled');
+}
+
+
+$enrol = enrol_get_plugin('database');
+$enrol->sync_courses();
+$enrol->sync_enrolments();
diff --git a/enrol/database/config.html b/enrol/database/config.html
deleted file mode 100644
index e7b28e7b4834..000000000000
--- a/enrol/database/config.html
+++ /dev/null
@@ -1,251 +0,0 @@
-<?php
-    $yesno = array('0'=>get_string('no'), '1'=>get_string('yes'));
-?>
-
-<table cellspacing="0" cellpadding="5" border="0" class="boxaligncenter">
-<tr>
-<th colspan="2" scope="col">
-    <?php print_string("server_settings", "enrol_database") ?>
-</th>
-</tr>
-<tr>
-    <td align="right">enrol_dbtype:</td>
-    <td>
-    <?php
-       $dbtypes = array("access","ado_access", "ado", "ado_mssql", "borland_ibase", "csv", "db2", "fbsql", "firebird", "ibase", "informix72", "informix", "mssql", "mssql_n", "mysql", "mysqlt", "oci805", "oci8", "oci8po", "odbc", "odbc_mssql", "odbc_oracle", "oracle", "postgres64", "postgres7", "postgres", "proxy", "sqlanywhere", "sybase", "vfp");
-       foreach ($dbtypes as $dbtype) {
-           $dboptions[$dbtype] = $dbtype;
-       }
-       if (!isset($frm->enrol_dbtype)) {
-           $frm->enrol_dbtype = 'mysql';
-       }
-       echo html_writer::select($dboptions, "enrol_dbtype", $frm->enrol_dbtype, false);
-    ?>
-
-    </td>
-    <td>
-    <?php  print_string("dbtype","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_dbhost:</td>
-    <td>
-    <?php
-        if (!isset($frm->enrol_dbhost)) {
-            $frm->enrol_dbhost = '';
-        }
-    ?>
-        <input size="15" type="text" name="enrol_dbhost" value="<?php p($frm->enrol_dbhost) ?>" />
-    </td>
-    <td>
-    <?php  print_string("dbhost","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_dbuser:</td>
-    <td>
-        <input size="15" type="text" name="enrol_dbuser" value="<?php echo $frm->enrol_dbuser ?>" />
-    </td>
-    <td>
-    <?php  print_string("dbuser","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_dbpass:</td>
-    <td>
-        <input size="15" type="password" name="enrol_dbpass" value="<?php echo $frm->enrol_dbpass ?>" />
-    </td>
-    <td>
-    <?php  print_string("dbpass","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_dbname:</td>
-    <td>
-        <input size="15" type="text" name="enrol_dbname" value="<?php echo $frm->enrol_dbname ?>" />
-    </td>
-    <td>
-    <?php  print_string("dbname","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_dbtable:</td>
-    <td>
-        <input size="15" type="text" name="enrol_dbtable" value="<?php echo $frm->enrol_dbtable ?>" />
-    </td>
-    <td>
-    <?php  print_string("dbtable","enrol_database") ?>
-    </td>
-</tr>
-<tr>
-    <th colspan="2" scope="col">
-        <?php print_string("remote_fields_mapping", "enrol_database") ?>
-    </th>
-</tr>
-<tr>
-    <td align="right">enrol_localcoursefield:</td>
-    <td>
-        <input size="15" type="text" name="enrol_localcoursefield" value="<?php echo $frm->enrol_localcoursefield ?>" />
-    </td>
-    <td>
-    <?php  print_string("localcoursefield","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_localuserfield:</td>
-    <td>
-        <input size="15" type="text" name="enrol_localuserfield" value="<?php echo $frm->enrol_localuserfield ?>" />
-    </td>
-    <td>
-    <?php  print_string("localuserfield","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_db_localrolefield:</td>
-    <td>
-        <input size="15" type="text" name="enrol_db_localrolefield" value="<?php echo $frm->enrol_db_localrolefield ?>" />
-    </td>
-    <td>
-    <?php  print_string("localrolefield","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_remotecoursefield:</td>
-    <td>
-        <input size="15" type="text" name="enrol_remotecoursefield" value="<?php echo $frm->enrol_remotecoursefield ?>" />
-    </td>
-    <td>
-    <?php  print_string("remotecoursefield","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_remoteuserfield:</td>
-    <td>
-        <input size="15" type="text" name="enrol_remoteuserfield" value="<?php echo $frm->enrol_remoteuserfield ?>" />
-    </td>
-    <td>
-    <?php  print_string("remoteuserfield","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_db_remoterolefield:</td>
-    <td>
-        <input size="15" type="text" name="enrol_db_remoterolefield" value="<?php echo $frm->enrol_db_remoterolefield ?>" />
-    </td>
-    <td>
-    <?php  print_string("remoterolefield","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-   <th colspan="2" scope="col">
-        <?php print_string('roles', 'role') ?>
-   </th>
-</tr>
-<tr>
-    <td align="right">enrol_db_defaultcourseroleid:</td>
-    <td>
-    <?php
-        $sitecontext = get_context_instance(CONTEXT_SYSTEM);
-        $assignableroles = get_assignable_roles($sitecontext);
-        $assignableroles = array('' => get_string('default')) + $assignableroles;
-
-        echo html_writer::select($assignableroles, 'enrol_db_defaultcourseroleid', $frm->enrol_db_defaultcourseroleid, false);
-        if (isset($err['enrol_db_defaultcourseroleid'])) echo $OUTPUT->error_text($err['enrol_db_defaultcourseroleid']);
-    ?>
-    </td>
-    <td>
-    <?php  print_string("defaultcourseroleid","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-   <th colspan="2" scope="col">
-        <?php print_string("enrol_database_autocreation_settings", "enrol_database") ?>
-   </th>
-</tr>
-<tr>
-    <td align="right">enrol_db_autocreate:</td>
-    <td>
-    <?php
-       echo html_writer::select($yesno, "enrol_db_autocreate", $frm->enrol_db_autocreate, false);
-           if (isset($err["enrol_db_autocreate"])) echo $OUTPUT->error_text($err["enrol_db_autocreate"]);
-    ?>
-    </td>
-    <td>
-    <?php  print_string("autocreate","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-        <td align="right">enrol_db_category:</td>
-        <td>
-    <?php
-           $displaylist = array();
-           $parentlist = array();
-           make_categories_list($displaylist, $parentlist);
-           echo html_writer::select($displaylist, "enrol_db_category", $frm->enrol_db_category, false);
-
-    ?>
-    <?php  if (isset($err["enrol_db_category"])) echo $OUTPUT->error_text($err["enrol_db_category"]); ?>
-    </td><td>
-    <?php  print_string("category","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_db_template:</td>
-        <td>
-    <input name="enrol_db_template" type="text" size="30" value="<?php echo $frm->enrol_db_template?>" />
-    <?php  if (isset($err["enrol_db_template"])) echo $OUTPUT->error_text($err["enrol_db_template"]); ?>
-    </td><td>
-    <?php  print_string("template","enrol_database") ?>
-    </td>
-</tr>
-
-<tr>
-   <th colspan="2" scope="col">
-        <?php print_string("general_options", "enrol_database") ?>
-   </th>
-</tr>
-
-<tr>
-    <td align="right">enrol_db_ignorehiddencourse:</td>
-    <td>
-    <?php
-        echo html_writer::select($yesno, "enrol_db_ignorehiddencourse", $frm->enrol_db_ignorehiddencourse, false);
-        if (isset($err['enrol_db_ignorehiddencourse'])) {
-            echo $OUTPUT->error_text($err['enrol_db_ignorehiddencourse']);
-        }
-    ?>
-    </td>
-    <td>
-        <?php print_string('ignorehiddencourse', 'enrol_database' ); ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_db_disableunenrol:</td>
-    <td>
-    <?php
-        echo html_writer::select($yesno, "enrol_db_disableunenrol", $frm->enrol_db_disableunenrol, false);
-        if (isset($err['enrol_db_disableunenrol'])) {
-            echo $OUTPUT->error_text($err['enrol_db_disableunenrol']);
-        }
-    ?>
-    </td>
-    <td>
-        <?php print_string('disableunenrol', 'enrol_database' ); ?>
-    </td>
-</tr>
-</table>
diff --git a/enrol/database/db/access.php b/enrol/database/db/access.php
new file mode 100644
index 000000000000..4ccb27b2d915
--- /dev/null
+++ b/enrol/database/db/access.php
@@ -0,0 +1,45 @@
+<?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/>.
+
+/**
+ * Capabilities for database enrolment plugin.
+ *
+ * @package   enrol_database
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$capabilities = array(
+    'enrol/database:config' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+    'enrol/database:manage' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+);
+
+
diff --git a/enrol/database/db/install.php b/enrol/database/db/install.php
new file mode 100644
index 000000000000..5d974ede90c3
--- /dev/null
+++ b/enrol/database/db/install.php
@@ -0,0 +1,107 @@
+<?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/>.
+
+/**
+ * Database enrolment plugin installation.
+ *
+ * @package   enrol_database
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+function xmldb_enrol_database_install() {
+    global $CFG, $DB;
+
+    // migrate old config settings first
+    if (isset($CFG->enrol_dbtype)) {
+        set_config('dbtype', $CFG->enrol_dbtype, 'enrol_database');
+        unset_config('enrol_dbtype');
+    }
+    if (isset($CFG->enrol_dbhost)) {
+        set_config('dbhost', $CFG->enrol_dbhost, 'enrol_database');
+        unset_config('enrol_dbhost');
+    }
+    if (isset($CFG->enrol_dbuser)) {
+        set_config('dbuser', $CFG->enrol_dbuser, 'enrol_database');
+        unset_config('enrol_dbuser');
+    }
+    if (isset($CFG->enrol_dbpass)) {
+        set_config('dbpass', $CFG->enrol_dbpass, 'enrol_database');
+        unset_config('enrol_dbpass');
+    }
+    if (isset($CFG->enrol_dbname)) {
+        set_config('dbname', $CFG->enrol_dbname, 'enrol_database');
+        unset_config('enrol_dbname');
+    }
+    if (isset($CFG->enrol_dbtable)) {
+        set_config('remoteenroltable', $CFG->enrol_dbtable, 'enrol_database');
+        unset_config('enrol_dbtable');
+    }
+    if (isset($CFG->enrol_localcoursefield)) {
+        set_config('localcoursefield', $CFG->enrol_localcoursefield, 'enrol_database');
+        unset_config('enrol_localcoursefield');
+    }
+    if (isset($CFG->enrol_localuserfield)) {
+        set_config('localuserfield', $CFG->enrol_localuserfield, 'enrol_database');
+        unset_config('enrol_localuserfield');
+    }
+    if (isset($CFG->enrol_localrolefield)) {
+        set_config('localrolefield', $CFG->enrol_localrolefield, 'enrol_database');
+        unset_config('enrol_localrolefield');
+    }
+    if (isset($CFG->enrol_remotecoursefield)) {
+        set_config('remotecoursefield', $CFG->enrol_remotecoursefield, 'enrol_database');
+        unset_config('enrol_remotecoursefield');
+    }
+    if (isset($CFG->enrol_remoteuserfield)) {
+        set_config('remoteuserfield', $CFG->enrol_remoteuserfield, 'enrol_database');
+        unset_config('enrol_remoteuserfield');
+    }
+    if (isset($CFG->enrol_remoterolefield)) {
+        set_config('remoterolefield', $CFG->enrol_remoterolefield, 'enrol_database');
+        unset_config('enrol_remoterolefield');
+    }
+    if (isset($CFG->enrol_db_defaultcourseroleid)) {
+        set_config('defaultrole', $CFG->enrol_db_defaultcourseroleid, 'enrol_database');
+        unset_config('enrol_db_defaultcourseroleid');
+    }
+    unset_config('enrol_db_autocreate'); // replaced by new coruse temple sync
+    if (isset($CFG->enrol_db_category)) {
+        set_config('defaultcategory', $CFG->enrol_db_category, 'enrol_database');
+        unset_config('enrol_db_category');
+    }
+    if (isset($CFG->enrol_db_template)) {
+        set_config('templatecourse', $CFG->enrol_db_template, 'enrol_database');
+        unset_config('enrol_db_template');
+    }
+    if (isset($CFG->enrol_db_ignorehiddencourse)) {
+        set_config('ignorehiddencourses', $CFG->enrol_db_ignorehiddencourse, 'enrol_database');
+        unset_config('enrol_db_ignorehiddencourse');
+    }
+
+
+
+
+
+
+
+    // just make sure there are no leftovers after disabled plugin
+    if (!$DB->record_exists('enrol', array('enrol'=>'database'))) {
+        role_unassign_all(array('component'=>'enrol_database'));
+        return;
+    }
+}
diff --git a/enrol/database/enrol.php b/enrol/database/enrol.php
deleted file mode 100644
index 594f82d44ccd..000000000000
--- a/enrol/database/enrol.php
+++ /dev/null
@@ -1,681 +0,0 @@
-<?php
-
-require_once($CFG->libdir.'/adodb/adodb.inc.php');
-require_once($CFG->dirroot.'/enrol/enrol.class.php');
-
-class enrolment_plugin_database {
-
-    var $log;
-
-/**
- * For the given user, let's go out and look in an external database
- * for an authoritative list of enrolments, and then adjust the
- * local Moodle assignments to match.
- */
-function setup_enrolments(&$user) {
-    global $CFG, $DB;
-
-    // NOTE: if $this->enrol_connect() succeeds you MUST remember to call
-    // $this->enrol_disconnect() as it is doing some nasty vodoo with table prefix
-    $enroldb = $this->enrol_connect();
-    if (!$enroldb) {
-        error_log('[ENROL_DB] Could not make a connection');
-        return;
-    }
-
-    // If we are expecting to get role information from our remote db, then
-    // we execute the below code for every role type.  Otherwise we just
-    // execute it once with null (hence the dummy array).
-    $roles = !empty($CFG->enrol_db_remoterolefield) && !empty($CFG->enrol_db_localrolefield)
-        ? get_all_roles()
-        : array(null);
-
-    //error_log('[ENROL_DB] found ' . count($roles) . ' roles:');
-
-    foreach($roles as $role) {
-
-        //error_log('[ENROL_DB] setting up enrolments for '.$role->shortname);
-
-        /// Get the authoritative list of enrolments from the external database table
-        /// We're using the ADOdb functions natively here and not our datalib functions
-        /// because we didn't want to mess with the $ db global
-
-        $useridfield = $enroldb->quote($user->{$CFG->enrol_localuserfield});
-
-        list($have_role, $remote_role_name, $remote_role_value) = $this->role_fields($enroldb, $role);
-
-        /// Check if a particular role has been forced by the plugin site-wide
-        /// (if we aren't doing a role-based select)
-        if (!$have_role && $CFG->enrol_db_defaultcourseroleid) {
-            $role = $DB->get_record('role', array('id'=>$CFG->enrol_db_defaultcourseroleid));
-        }
-
-        /// Whether to fetch the default role on a per-course basis (below) or not.
-        $use_default_role = !$role;
-
-        /*
-        if ($have_role) {
-            error_log('[ENROL_DB] Doing role-specific select from db for role: '.$role->shortname);
-        } elseif ($use_default_role) {
-            error_log('[ENROL_DB] Using course default for roles - assuming that database lists defaults');
-        } else {
-            error_log('[ENROL_DB] Using config default for roles: '.$role->shortname);
-        }*/
-
-        if ($rs = $enroldb->Execute("SELECT {$CFG->enrol_remotecoursefield} as enrolremotecoursefield
-                                       FROM {$CFG->enrol_dbtable}
-                                      WHERE {$CFG->enrol_remoteuserfield} = " . $useridfield .
-                                        (isset($remote_role_name, $remote_role_value) ? ' AND '.$remote_role_name.' = '.$remote_role_value : ''))) {
-
-            // We'll use this to see what to add and remove
-            $existing = $role
-                ? $DB->get_records_sql("SELECT *
-                                          FROM {role_assignments}
-                                         WHERE userid = ? AND roleid = ?",
-                                       array($user->id, $role->id))
-                : $DB->get_records('role_assignments', array('userid'=>$user->id));
-
-            if (!$existing) {
-                $existing = array();
-            }
-
-
-            if (!$rs->EOF) {   // We found some courses
-
-                //$count = 0;
-                $courselist = array();
-                while ($fields = $rs->FetchRow()) {         // Make a nice little array of courses to process
-                    $fields = array_change_key_case($fields, CASE_LOWER);
-                    $courselist[] = $fields['enrolremotecoursefield'];
-                    //$count++;
-                }
-                $rs->close();
-
-                //error_log('[ENROL_DB] Found '.count($existing).' existing roles and '.$count.' in external database');
-
-                foreach ($courselist as $coursefield) {   /// Check the list of courses against existing
-                    $course = $DB->get_record('course', array($CFG->enrol_localcoursefield=>$coursefield));
-                    if (!is_object($course)) {
-                        if (empty($CFG->enrol_db_autocreate)) { // autocreation not allowed
-                            if (debugging('',DEBUG_ALL)) {
-                                error_log( "Course $coursefield does not exist, skipping") ;
-                            }
-                            continue; // next foreach course
-                        }
-                        // ok, now then let's create it!
-                        // prepare any course properties we actually have
-                        $course = new StdClass;
-                        $course->{$CFG->enrol_localcoursefield} = $coursefield;
-                        $course->fullname  = $coursefield;
-                        $course->shortname = $coursefield;
-                        if (!($newcourseid = $this->create_course($course, true)
-                            and $course = $DB->get_record( 'course', array('id'=>$newcourseid)))) {
-                            error_log( "Creating course $coursefield failed");
-                            continue; // nothing left to do...
-                        }
-                    }
-
-                    // if the course is hidden and we don't want to enrol in hidden courses
-                    // then just skip it
-                    if (!$course->visible and $CFG->enrol_db_ignorehiddencourse) {
-                        continue;
-                    }
-
-                    /// If there's no role specified, we get the default course role (usually student)
-                    if ($use_default_role) {
-                        $role = get_default_course_role($course);
-                    }
-
-                    $context = get_context_instance(CONTEXT_COURSE, $course->id);
-
-                    // Couldn't get a role or context, skip.
-                    if (!$role || !$context) {
-                        continue;
-                    }
-
-                    // Search the role assignments to see if this user
-                    // already has this role in this context.  If it is, we
-                    // skip to the next course.
-                    foreach($existing as $key => $role_assignment) {
-                        if ($role_assignment->roleid == $role->id
-                            && $role_assignment->contextid == $context->id) {
-                            unset($existing[$key]);
-                            //error_log('[ENROL_DB] User is already enroled in course '.$course->idnumber);
-                            continue 2;
-                        }
-                    }
-
-                    //error_log('[ENROL_DB] Enrolling user in course '.$course->idnumber);
-                    role_assign($role->id, $user->id, 0, $context->id, 0, 0, 0, 'database');
-                }
-            } // We've processed all external courses found
-
-            /// We have some courses left that we might need to unenrol from
-            /// Note: we only process enrolments that we (ie 'database' plugin) made
-            /// Do not unenrol anybody if the disableunenrol option is 'yes'
-            if (!$CFG->enrol_db_disableunenrol) {
-                foreach ($existing as $role_assignment) {
-                    if ($role_assignment->enrol == 'database') {
-                        //error_log('[ENROL_DB] Removing user from context '.$role_assignment->contextid);
-                        role_unassign($role_assignment->roleid, $user->id, '', $role_assignment->contextid);
-                    }
-                }
-            }
-        } else {
-            error_log('[ENROL_DB] Couldn\'t get rows from external db: '.$enroldb->ErrorMsg());
-        }
-    }
-    $this->enrol_disconnect($enroldb);
-}
-
-/**
- * sync enrolments with database, create courses if required.
- *
- * @param object The role to sync for. If no role is specified, defaults are
- * used.
- */
-function sync_enrolments($role = null) {
-    global $CFG, $DB, $OUTPUT;
-    error_reporting(E_ALL);
-
-    // Connect to the external database
-    $enroldb = $this->enrol_connect();
-    if (!$enroldb) {
-        echo $OUTPUT->notification("enrol/database cannot connect to server");
-        return false;
-    }
-
-    if (isset($role)) {
-        echo '=== Syncing enrolments for role: '.$role->shortname." ===\n";
-    } else {
-        echo "=== Syncing enrolments for default role ===\n";
-    }
-
-    // first, pack the sortorder...
-    fix_course_sortorder();
-
-    list($have_role, $remote_role_name, $remote_role_value) = $this->role_fields($enroldb, $role);
-
-    if (!$have_role) {
-        if (!empty($CFG->enrol_db_defaultcourseroleid)
-         and $role = $DB->get_record('role', array('id'=>$CFG->enrol_db_defaultcourseroleid))) {
-            echo "=== Using enrol_db_defaultcourseroleid: {$role->id} ({$role->shortname}) ===\n";
-        } elseif (isset($role)) {
-            echo "!!! WARNING: Role specified by caller, but no (or invalid) role configuration !!!\n";
-        }
-    }
-
-    // get enrolments per-course
-    $sql =  "SELECT DISTINCT {$CFG->enrol_remotecoursefield} " .
-        " FROM {$CFG->enrol_dbtable} " .
-        " WHERE {$CFG->enrol_remoteuserfield} IS NOT NULL" .
-        (isset($remote_role_name, $remote_role_value) ? ' AND '.$remote_role_name.' = '.$remote_role_value : '');
-
-    $rs = $enroldb->Execute($sql);
-    if (!$rs) {
-        trigger_error($enroldb->ErrorMsg() .' STATEMENT: '. $sql);
-        return false;
-    }
-    if ( $rs->EOF ) { // no courses! outta here...
-        return true;
-    }
-
-    $transaction = $DB->start_delegated_transaction();
-
-    $extcourses = array();
-    while ($rsextcourse = $rs->FetchRow()) { // there are more course records
-        $rsextcourse = array_change_key_case($rsextcourse, CASE_LOWER);
-        $extcourse = $rsextcourse[strtolower($CFG->enrol_remotecoursefield)];
-        array_push($extcourses, $extcourse);
-
-        // does the course exist in moodle already?
-        $course = false;
-        $course = $DB->get_record('course', array($CFG->enrol_localcoursefield=>$extcourse));
-
-        if (!is_object($course)) {
-            if (empty($CFG->enrol_db_autocreate)) { // autocreation not allowed
-                if (debugging('', DEBUG_ALL)) {
-                    error_log( "Course $extcourse does not exist, skipping");
-                }
-                continue; // next foreach course
-            }
-            // ok, now then let's create it!
-            // prepare any course properties we actually have
-            $course = new StdClass;
-            $course->{$CFG->enrol_localcoursefield} = $extcourse;
-            $course->fullname  = $extcourse;
-            $course->shortname = $extcourse;
-            if (!($newcourseid = $this->create_course($course, true)
-             and $course = $DB->get_record('course', array('id'=>$newcourseid)))) {
-                error_log( "Creating course $extcourse failed");
-                continue; // nothing left to do...
-            }
-
-        }
-
-        $context = get_context_instance(CONTEXT_COURSE, $course->id);
-
-        // If we don't have a proper role setup, then we default to the default
-        // role for the current course.
-        if (!$have_role) {
-            $role = get_default_course_role($course);
-        }
-
-        // get a list of the student ids the are enrolled
-        // in the external db -- hopefully it'll fit in memory...
-        $extenrolments = array();
-        $sql = "SELECT {$CFG->enrol_remoteuserfield} " .
-            " FROM {$CFG->enrol_dbtable} " .
-            " WHERE {$CFG->enrol_remotecoursefield} = " . $enroldb->quote($extcourse) .
-                ($have_role ? ' AND '.$remote_role_name.' = '.$remote_role_value : '');
-
-        $crs = $enroldb->Execute($sql);
-        if (!$crs) {
-            trigger_error($enroldb->ErrorMsg() .' STATEMENT: '. $sql);
-            return false;
-        }
-        if ( $crs->EOF ) { // shouldn't happen, but cover all bases
-            continue;
-        }
-
-        // slurp results into an array
-        while ($rscrs = $crs->FetchRow()) {
-            $rscrs = array_change_key_case($rscrs, CASE_LOWER);
-            array_push($extenrolments, $rscrs[strtolower($CFG->enrol_remoteuserfield)]);
-        }
-        $crs->close(); // release the handle
-
-        //
-        // prune enrolments to users that are no longer in ext auth
-        // hopefully they'll fit in the max buffer size for the RDBMS
-        //
-        // TODO: This doesn't work perfectly.  If we are operating without
-        // roles in the external DB, then this doesn't handle changes of role
-        // within a course (because the user is still enrolled in the course,
-        // so NOT IN misses the course).
-        //
-        // When the user logs in though, their role list will be updated
-        // correctly.
-        //
-        if (!$CFG->enrol_db_disableunenrol) {
-            if ($extenrolments) {
-                list($extlist, $params) = $DB->get_in_or_equal($extenrolments, SQL_PARAMS_NAMED, 'e0', false);
-                $extsql = "AND u.{$CFG->enrol_localuserfield} $extlist";
-            } else {
-                $extsql = "";
-                $params = array();
-            }
-            $params['roleid']    = $role->id;
-            $params['contextid'] = $context->id;
-
-            $to_prune = $DB->get_records_sql("
-                             SELECT ra.*
-                               FROM {role_assignments} ra
-                               JOIN {user} u ON ra.userid = u.id
-                              WHERE ra.enrol = 'database'
-                                    AND ra.contextid = :contextid
-                                    AND ra.roleid = :roleid", $params);
-
-            if ($to_prune) {
-                foreach ($to_prune as $role_assignment) {
-                    if (role_unassign($role->id, $role_assignment->userid, 0, $role_assignment->contextid)){
-                        error_log( "Unassigned {$role->shortname} assignment #{$role_assignment->id} for course {$course->id} (" . format_string($course->shortname) . "); user {$role_assignment->userid}");
-                    } else {
-                        error_log( "Failed to unassign {$role->shortname} assignment #{$role_assignment->id} for course {$course->id} (" . format_string($course->shortname) . "); user {$role_assignment->userid}");
-                    }
-                }
-            }
-        }
-
-        //
-        // insert current enrolments
-        // bad we can't do INSERT IGNORE with postgres...
-        //
-        foreach ($extenrolments as $member) {
-            // Get the user id and whether is enrolled in one fell swoop
-            $sql = "
-                SELECT u.id, ra.id AS enrolmentid
-                  FROM {user} u
-                  LEFT JOIN {role_assignments} ra ON u.id = ra.userid
-                       AND ra.roleid = ?
-                       AND ra.contextid = ?
-                 WHERE u.{$CFG->enrol_localuserfield} = ?
-                       AND (u.deleted IS NULL OR u.deleted=0)";
-
-            $eusers = $DB->get_records($sql, array($role->id, $context->id, $member));
-            if ($eusers === false) {
-                trigger_error('error STATEMENT: '. $sql);
-                return false;
-            }
-            if (!$eusers) { // if this returns empty, it means we don't have the student record.
-                                              // should not happen -- but skip it anyway
-                trigger_error('weird! no user record entry?');
-                continue;
-            }
-            $userid      = $user_obj->id;
-            $enrolmentid = $user_obj->enrolmentid;
-
-            if ($enrolmentid) { // already enrolled - skip
-                continue;
-            }
-
-            if (role_assign($role->id, $userid, 0, $context->id, 0, 0, 0, 'database')){
-                error_log( "Assigned role {$role->shortname} to user {$userid} in course {$course->id} (" . format_string($course->shortname) . ")");
-            } else {
-                error_log( "Failed to assign role {$role->shortname} to user {$userid} in course {$course->id} (" . format_string($course->shortname) . ")");
-            }
-
-        } // end foreach member
-    } // end while course records
-    $rs->close(); //Close the main course recordset
-
-    //
-    // prune enrolments to courses that are no longer in ext auth
-    //
-    // TODO: This doesn't work perfectly.  If we are operating without
-    // roles in the external DB, then this doesn't handle changes of role
-    // within a course (because the user is still enrolled in the course,
-    // so NOT IN misses the course).
-    //
-    // When the user logs in though, their role list will be updated
-    // correctly.
-    //
-    if (!$CFG->enrol_db_disableunenrol) {
-        if ($extcourses) {
-            list($extlist, $params) = $DB->get_in_or_equal($extcourses, SQL_PARAMS_QM, 'e0', false);
-            $extsql = "AND u.{$CFG->enrol_localcoursefield} $extlist";
-        } else {
-            $extsql = "";
-            $params = array();
-        }
-        $params['roleid']    = $role->id;
-        $params['contextid'] = $context->id;
-
-        $sql = "
-            SELECT ra.roleid, ra.userid, ra.contextid
-              FROM {role_assignments} ra
-              JOIN {context} cn ON cn.id = ra.contextid
-              JOIN {course} c ON c.id = cn.instanceid
-             WHERE ra.enrol = 'database'
-                   AND cn.contextlevel = ".CONTEXT_COURSE." " .
-                  ($have_role ? " AND ra.roleid = :roleid " : '') ."
-                   $extsql";
-
-        if (!$ers = $DB->get_recordset_sql($sql, $params)) {
-            trigger_error('error STATEMENT: '. $sql);
-            return false;
-        }
-        foreach ($ers as $user_obj) {
-            $roleid     = $user_obj->roleid;
-            $user       = $user_obj->userid;
-            $contextid  = $user_obj->contextid;
-            if (role_unassign($roleid, $user, 0, $contextid)){
-                error_log( "Unassigned role {$roleid} from user $user in context $contextid");
-            } else {
-                error_log( "Failed unassign role {$roleid} from user $user in context $contextid");
-                }
-            }
-        $ers->close(); // release the handle
-    }
-
-    $transaction->allow_commit();
-
-    // we are done now, a bit of housekeeping
-    fix_course_sortorder();
-
-    $this->enrol_disconnect($enroldb);
-    return true;
-}
-
-/// Overide the get_access_icons() function
-function get_access_icons($course) {
-}
-
-
-/// Overide the base config_form() function
-function config_form($frm) {
-    global $CFG, $OUTPUT;
-
-    $vars = array('enrol_dbhost', 'enrol_dbuser', 'enrol_dbpass',
-                  'enrol_dbname', 'enrol_dbtable',
-                  'enrol_localcoursefield', 'enrol_localuserfield',
-                  'enrol_remotecoursefield', 'enrol_remoteuserfield',
-                  'enrol_db_autocreate', 'enrol_db_category', 'enrol_db_template',
-                  'enrol_db_localrolefield', 'enrol_db_remoterolefield',
-                  'enrol_remotecoursefield', 'enrol_remoteuserfield',
-                  'enrol_db_ignorehiddencourse', 'enrol_db_defaultcourseroleid',
-                  'enrol_db_disableunenrol');
-
-    foreach ($vars as $var) {
-        if (!isset($frm->$var)) {
-            $frm->$var = '';
-        }
-    }
-    include("$CFG->dirroot/enrol/database/config.html");
-}
-
-/// Override the base process_config() function
-function process_config($config) {
-
-    if (!isset($config->enrol_dbtype)) {
-        $config->enrol_dbtype = 'mysql';
-    }
-    set_config('enrol_dbtype', $config->enrol_dbtype);
-
-    if (!isset($config->enrol_dbhost)) {
-        $config->enrol_dbhost = '';
-    }
-    set_config('enrol_dbhost', $config->enrol_dbhost);
-
-    if (!isset($config->enrol_dbuser)) {
-        $config->enrol_dbuser = '';
-    }
-    set_config('enrol_dbuser', $config->enrol_dbuser);
-
-    if (!isset($config->enrol_dbpass)) {
-        $config->enrol_dbpass = '';
-    }
-    set_config('enrol_dbpass', $config->enrol_dbpass);
-
-    if (!isset($config->enrol_dbname)) {
-        $config->enrol_dbname = '';
-    }
-    set_config('enrol_dbname', $config->enrol_dbname);
-
-    if (!isset($config->enrol_dbtable)) {
-        $config->enrol_dbtable = '';
-    }
-    set_config('enrol_dbtable', $config->enrol_dbtable);
-
-    if (!isset($config->enrol_localcoursefield)) {
-        $config->enrol_localcoursefield = '';
-    }
-    set_config('enrol_localcoursefield', $config->enrol_localcoursefield);
-
-    if (!isset($config->enrol_localuserfield)) {
-        $config->enrol_localuserfield = '';
-    }
-    set_config('enrol_localuserfield', $config->enrol_localuserfield);
-
-    if (!isset($config->enrol_remotecoursefield)) {
-        $config->enrol_remotecoursefield = '';
-    }
-    set_config('enrol_remotecoursefield', $config->enrol_remotecoursefield);
-
-    if (!isset($config->enrol_remoteuserfield)) {
-        $config->enrol_remoteuserfield = '';
-    }
-    set_config('enrol_remoteuserfield', $config->enrol_remoteuserfield);
-
-    if (!isset($config->enrol_db_autocreate)) {
-        $config->enrol_db_autocreate = '';
-    }
-    set_config('enrol_db_autocreate', $config->enrol_db_autocreate);
-
-    if (!isset($config->enrol_db_category)) {
-        $config->enrol_db_category = '';
-    }
-    set_config('enrol_db_category', $config->enrol_db_category);
-
-    if (!isset($config->enrol_db_template)) {
-        $config->enrol_db_template = '';
-    }
-    set_config('enrol_db_template', $config->enrol_db_template);
-
-    if (!isset($config->enrol_db_defaultcourseroleid)) {
-        $config->enrol_db_defaultcourseroleid = '';
-    }
-    set_config('enrol_db_defaultcourseroleid', $config->enrol_db_defaultcourseroleid);
-
-    if (!isset($config->enrol_db_localrolefield)) {
-        $config->enrol_db_localrolefield = '';
-    }
-    set_config('enrol_db_localrolefield', $config->enrol_db_localrolefield);
-
-    if (!isset($config->enrol_db_remoterolefield)) {
-        $config->enrol_db_remoterolefield = '';
-    }
-    set_config('enrol_db_remoterolefield', $config->enrol_db_remoterolefield);
-
-    if (!isset($config->enrol_db_ignorehiddencourse)) {
-        $config->enrol_db_ignorehiddencourse = '';
-    }
-    set_config('enrol_db_ignorehiddencourse', $config->enrol_db_ignorehiddencourse );
-
-    if (!isset($config->enrol_db_disableunenrol)) {
-        $config->enrol_db_disableunenrol = '';
-    }
-    set_config('enrol_db_disableunenrol', $config->enrol_db_disableunenrol );
-
-    return true;
-}
-
-// will create the moodle course from the template
-// course_ext is an array as obtained from ldap -- flattened somewhat
-// NOTE: if you pass true for $skip_fix_course_sortorder
-// you will want to call fix_course_sortorder() after your are done
-// with course creation
-function create_course ($course,$skip_fix_course_sortorder=0){
-    global $CFG, $DB, $OUTPUT;
-
-    // define a template
-    if (!empty($CFG->enrol_db_template)){
-        $template = $DB->get_record("course", array('shortname'=>$CFG->enrol_db_template));
-        $template = (array)$template;
-    } else {
-        $site = get_site();
-        $template = array(
-                          'startdate'      => time() + 3600 * 24,
-                          'summary'        => get_string("defaultcoursesummary"),
-                          'format'         => "weeks",
-                          'password'       => "",
-                          'guest'          => 0,
-                          'numsections'    => 10,
-                          'idnumber'       => '',
-                          'cost'           => '',
-                          'newsitems'      => 5,
-                          'showgrades'     => 1,
-                          'groupmode'      => 0,
-                          'groupmodeforce' => 0,
-                          );
-    }
-    // overlay template
-    foreach (array_keys($template) AS $key) {
-        if (empty($course->$key)) {
-            $course->$key = $template[$key];
-        }
-    }
-
-    $category = get_course_category($CFG->enrol_db_category);
-
-    // put at the end of category
-    $course->sortorder = $category->sortorder + MAX_COURSES_IN_CATEGORY - 1;
-
-    // override with local data
-    $course->startdate   = time() + 3600 * 24;
-    $course->timecreated = time();
-    $course->visible     = 1;
-
-    // clear out id just in case
-    unset($course->id);
-
-    // truncate a few key fields
-    $course->idnumber  = substr($course->idnumber, 0, 100);
-    $course->shortname = substr($course->shortname, 0, 100);
-
-    // store it and log
-    if ($newcourseid = $DB->insert_record("course", $course)) {  // Set up new course
-        $section = new object();
-        $section->course  = $newcourseid;   // Create a default section.
-        $section->section = 0;
-        $section->summaryformat = FORMAT_HTML;
-        $section->id = $DB->insert_record("course_sections", $section);
-        $course = $DB->get_record('course', array('id' => $newcourseid));
-        blocks_add_default_course_blocks($course);
-
-        if (!$skip_fix_course_sortorder){
-            fix_course_sortorder();
-        }
-        add_to_log($newcourseid, "course", "new", "view.php?id=$newcourseid", "enrol/database auto-creation");
-    } else {
-        trigger_error("Could not create new course $extcourse from  from database");
-        echo $OUTPUT->notification("Serious Error! Could not create the new course!");
-        return false;
-    }
-
-    return $newcourseid;
-}
-
-/// DB Connect
-/// NOTE: You MUST remember to disconnect
-/// when you stop using it -- as this call will
-/// sometimes modify table prefix for the whole of Moodle!
-function enrol_connect() {
-    global $CFG;
-
-    // Try to connect to the external database (forcing new connection)
-    $enroldb = &ADONewConnection($CFG->enrol_dbtype);
-    if ($enroldb->Connect($CFG->enrol_dbhost, $CFG->enrol_dbuser, $CFG->enrol_dbpass, $CFG->enrol_dbname, true)) {
-        $enroldb->SetFetchMode(ADODB_FETCH_ASSOC); ///Set Assoc mode always after DB connection
-        return $enroldb;
-    } else {
-        trigger_error("Error connecting to enrolment DB backend with: "
-                      . "$CFG->enrol_dbhost,$CFG->enrol_dbuser,$CFG->enrol_dbpass,$CFG->enrol_dbname");
-        return false;
-    }
-}
-
-/// DB Disconnect
-function enrol_disconnect($enroldb) {
-    global $CFG;
-
-    $enroldb->Close();
-}
-
-/**
- * This function returns the name and value of the role field to query the db
- * for, or null if there isn't one.
- *
- * @param object The ADOdb connection
- * @param object The role
- * @return array (boolean, string, db quoted string)
- */
-function role_fields($enroldb, $role) {
-    global $CFG;
-
-    if ($have_role = !empty($role)
-     && !empty($CFG->enrol_db_remoterolefield)
-     && !empty($CFG->enrol_db_localrolefield)
-     && !empty($role->{$CFG->enrol_db_localrolefield})) {
-        $remote_role_name = $CFG->enrol_db_remoterolefield;
-        $remote_role_value = $enroldb->quote($role->{$CFG->enrol_db_localrolefield});
-    } else {
-        $remote_role_name = $remote_role_value = null;
-    }
-
-    return array($have_role, $remote_role_name, $remote_role_value);
-}
-
-} // end of class
-
-
diff --git a/enrol/database/enrol_database_sync.php b/enrol/database/enrol_database_sync.php
deleted file mode 100644
index 8e2fa82ac9f1..000000000000
--- a/enrol/database/enrol_database_sync.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-    if(!empty($_SERVER['GATEWAY_INTERFACE'])){
-        error_log("should not be called from apache!");
-        exit;
-    }
-    error_reporting(E_ALL);
-
-    require_once(dirname(dirname(dirname(__FILE__))).'/config.php'); // global moodle config file.
-
-    require_once($CFG->dirroot . '/course/lib.php');
-    require_once($CFG->dirroot . '/enrol/database/enrol.php');
-
-    // ensure errors are well explained
-    $CFG->debug=E_ALL;
-
-    if (!is_enabled_enrol('database')) {
-         error_log("Database enrol plugin not enabled!");
-         die;
-    }
-
-    // update enrolments -- these handlers should autocreate courses if required
-    $enrol = new enrolment_plugin_database();
-
-    // If we have settings to handle roles individually, through each type of
-    // role and update it.  Otherwise, just got through once (with no role
-    // specified).
-    $roles = !empty($CFG->enrol_db_remoterolefield) && !empty($CFG->enrol_db_localrolefield)
-        ? get_all_roles()
-        : array(null);
-
-    foreach ($roles as $role) {
-        $enrol->sync_enrolments($role);
-    }
-
-    // sync metacourses
-    if (function_exists('sync_metacourses')) {
-        sync_metacourses();
-    }
-
-
diff --git a/enrol/database/lang/en/enrol_database.php b/enrol/database/lang/en/enrol_database.php
index e42fbe3d9bc1..2f65c8058748 100644
--- a/enrol/database/lang/en/enrol_database.php
+++ b/enrol/database/lang/en/enrol_database.php
@@ -23,46 +23,55 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['autocreate'] = 'Courses can be created automatically if there are enrolments to a course that doesn\'t yet exist in Moodle.';
-$string['autocreation_settings'] = 'Autocreation Settings';
-$string['category'] = 'The category for auto-created courses.';
-$string['course_fullname'] = 'The name of the field where the course fullname is stored.';
-$string['course_id'] = 'The name of the field where the course ID is stored. The values of this field are used to match those in the "enrol_db_l_coursefield" field in Moodle\'s course table.';
-$string['course_shortname'] = 'The name of the field where the course shortname is stored.';
-$string['course_table'] = 'Then name of the table where we expect to find the course details in (short name, fullname, ID, etc.)';
-$string['dbhost'] = 'Server IP name or number';
+$string['database:config'] = 'Configure external database enrol instances';
+$string['database:manage'] = 'Manage external database enrol instances';
+$string['dbencoding'] = 'Database encoding';
+$string['dbhost'] = 'Database host';
+$string['dbhost_desc'] = 'Type database server IP address or host name';
 $string['dbname'] = 'Database name';
-$string['dbpass'] = 'Server password';
-$string['dbtable'] = 'Database table';
-$string['dbtype'] = 'Database type';
-$string['dbuser'] = 'Server user';
-$string['defaultcourseroleid'] = 'The role that will be assigned by default if no other role is specified.';
-$string['description'] = 'You can use a external database (of nearly any kind) to control your enrolments. It is assumed your external database contains a field containing a course ID, and a field containing a user ID. These are compared against fields that you choose in the local course and user tables.';
-$string['disableunenrol'] = 'If set to yes users previously enrolled by the external database plugin will not be unenrolled by the same plugin regardless of the database contents.';
-$string['enrol_database_autocreation_settings'] = 'Auto-creation of new courses';
-$string['enrolname'] = 'External Database';
-$string['general_options'] = 'General Options';
-$string['host'] = 'Database server hostname.';
-$string['ignorehiddencourse'] = 'If set to yes users will not be enroled on courses that are set to be unavailable to students.';
-$string['localcoursefield'] = 'The name of the field in the course table that we are using to match entries in the remote database (eg idnumber).';
-$string['local_fields_mapping'] = 'Moodle (local) database fields';
-$string['localrolefield'] = 'The name of the field in the roles table that we are using to match entries in the remote database (eg shortname).';
-$string['localuserfield'] = 'The name of the field in the user table that we are using to match entries in the remote database (eg idnumber).';
-$string['name'] = 'The specific database to use.';
-$string['pass'] = 'Password to access the server.';
-$string['remotecoursefield'] = 'The name of the field in the remote table that we are using to match entries in the course table.';
-$string['remote_fields_mapping'] = 'Enrolment (remote) database fields.';
-$string['remoterolefield'] = 'The name of the field in the remote table that we are using to match entries in the roles table.';
-$string['remoteuserfield'] = 'The name of the field in the remote table that we are using to match entries in the user table.';
-$string['server_settings'] = 'External Database Server Settings';
-$string['student_coursefield'] = 'The name of the field in the student enrolment table that we expect to find the course ID in.';
-$string['student_l_userfield'] = 'The name of the field in the local user table that we use to match the user to a remote record for students (eg idnumber).';
-$string['student_r_userfield'] = 'The name of the field in the remote student enrolment table that we expect to find the user ID in.';
-$string['student_table'] = 'The name of the table where student enrolments are stored.';
-$string['teacher_coursefield'] = 'The name of the field in the teacher enrolment table that we expect to find the course ID in.';
-$string['teacher_l_userfield'] = 'The name of the field in the local user table that we use to match the user to a remote record for teachers (eg idnumber).';
-$string['teacher_r_userfield'] = 'The name of the field in the remote teacher enrolment table that we expect to find the user ID in.';
-$string['teacher_table'] = 'The name of the table where teacher enrolments are stored.';
-$string['template'] = 'Optional: auto-created courses can copy their settings from a template course. Type here the shortname of the template course.';
-$string['type'] = 'Database server type.';
-$string['user'] = 'Username to access the server.';
+$string['dbpass'] = 'Database password';
+$string['dbsetupsql'] = 'Database setup command';
+$string['dbsetupsql_desc'] = 'SQL command for special database setup, often used to setup communication encoding - example for MySQL and PostgreSQL: <em>SET NAMES \'utf8\'</em>';
+$string['dbsybasequoting'] = 'Use sybase quotes';
+$string['dbsybasequoting_desc'] = 'Sybase style single quote escaping - needed for Oracle, MS SQL and some other databases. Do not use for MySQL!';
+$string['dbtype'] = 'Database driver';
+$string['dbtype_desc'] = 'ADOdb database driver name, type of the external database engine.';
+$string['dbuser'] = 'Database user';
+$string['debugdb'] = 'Debug ADOdb';
+$string['debugdb_desc'] = 'Debug ADOdb connection to external database - use when getting empty page during login. Not suitable for production sites!';
+$string['defaultcategory'] = 'Default new course category';
+$string['defaultcategory_desc'] = 'The default category for auto-created courses. Used when no new category id specified or not found.';
+$string['defaultrole'] = 'Default role';
+$string['defaultrole_desc'] = 'The role that will be assigned by default if no other role is specified in external table.';
+$string['ignorehiddencourses'] = 'Ignore hidden courses';
+$string['ignorehiddencourses_desc'] = 'If enabled users will not be enrolled on courses that are set to be unavailable to students.';
+$string['localcoursefield'] = 'Local course field';
+$string['localrolefield'] = 'Local role field';
+$string['localuserfield'] = 'Local user field';
+$string['newcoursetable'] = 'Remote new courses table';
+$string['newcoursetable_desc'] = 'Specify of the name of the table that contains list of courses that should be created automatically. Empty means no courses are created.';
+$string['newcoursecategory'] = 'New course category id field';
+$string['newcoursefullname'] = 'New course full name field';
+$string['newcourseidnumber'] = 'New course ID number field';
+$string['newcourseshortname'] = 'New course short name field';
+$string['pluginname'] = 'External database';
+$string['pluginname_desc'] = 'You can use an external database (of nearly any kind) to control your enrolments. It is assumed your external database contains at least a field containing a course ID, and a field containing a user ID. These are compared against fields that you choose in the local course and user tables.';
+$string['remotecoursefield'] = 'Remote course field';
+$string['remotecoursefield_desc'] = 'The name of the field in the remote table that we are using to match entries in the course table.';
+$string['remoteenroltable'] = 'Remote user enrolment table';
+$string['remoteenroltable_desc'] = 'Specify the name of the table that contains list of user enrolments. Empty means no user enrolment sync.';
+$string['remoterolefield'] = 'Remote role field';
+$string['remoterolefield_desc'] = 'The name of the field in the remote table that we are using to match entries in the roles table.';
+$string['remoteuserfield'] = 'Remote user field';
+$string['settingsheaderdb'] = 'External database connection';
+$string['settingsheaderlocal'] = 'Local field mapping';
+$string['settingsheaderremote'] = 'Remote enrolment sync';
+$string['settingsheadernewcourses'] = 'Creation of new courses';
+$string['remoteuserfield_desc'] = 'The name of the field in the remote table that we are using to match entries in the user table.';
+$string['templatecourse'] = 'New course template';
+$string['templatecourse_desc'] = 'Optional: auto-created courses can copy their settings from a template course. Type here the shortname of the template course.';
+$string['unenrolaction'] = 'External unenrol action';
+$string['unenrolaction_desc'] = 'Select action to carry our when user enrolment disappears from external enrolment table. Please note that some user data and settings are purged from course during course unenrolment.';
+$string['unenrolactiondisable'] = 'Disable course enrolment';
+$string['unenrolactionkeep'] = 'Keep user enrolled';
+$string['unenrolactionunenrol'] = 'Unenrol user from course';
diff --git a/enrol/database/lib.php b/enrol/database/lib.php
new file mode 100644
index 000000000000..07b4fd497954
--- /dev/null
+++ b/enrol/database/lib.php
@@ -0,0 +1,487 @@
+<?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/>.
+
+/**
+ * Database enrolment plugin.
+ *
+ * This plugin synchronises enrolment and roles with external database table.
+ *
+ * @package   enrol_database
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Database enrolment plugin implementation.
+ * @author  Petr Skoda - based on code by Martin Dougiamas, Martin Langhoff and others
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class enrol_database_plugin extends enrol_plugin {
+    /**
+     * Is it possible to delete enrol instance via standard UI?
+     *
+     * @param object $instance
+     * @return bool
+     */
+    public function instance_deleteable($instance) {
+        if (!enrol_is_enabled('database')) {
+            return true;
+        }
+        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')) {
+            return true;
+        }
+
+        //TODO: connect to external system and make sure no users are to be enrolled in this course
+        return false;
+    }
+
+    /**
+     * Forces synchronisation of user enrolments with external database.
+     *
+     * @param object $user user record
+     * @return void
+     */
+    public function sync_user_enrolments($user = NULL) {
+
+        //TODO: full sync with external system is very expensive, it could cause big perf problems if we did that during each log-in,
+        //      so do the sync only once in a while or rely on cron
+    }
+
+    /**
+     * Forces synchronisation of all enrolments with external database.
+     *
+     * @return void
+     */
+    public function sync_enrolments() {
+        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')) {
+            return;
+        }
+
+        // we may need a lot of memory here
+        @set_time_limit(0);
+        @raise_memory_limit("512M");
+
+        $extdb = $this->db_init();
+
+        // second step is to sync instances and users
+        $table          = $this->get_config('remoteenroltable');
+        $coursefield    = strtolower($this->get_config('remotecoursefield'));
+        $userfield      = strtolower($this->get_config('remoteuserfield'));
+        $rolefield      = strtolower($this->get_config('remoterolefield'));
+        $localrolefield = $this->get_config('localrolefield');
+        $localuserfield = $this->get_config('localuserfield');
+        $unenrolaction  = $this->get_config('unenrolaction');
+
+        // create roles mapping
+        $allroles = get_all_roles();
+        $defaultrole = $this->get_config('defaultrole');
+        if (!isset($allroles[$defaultrole])) {
+            $defaultrole = 0;
+        }
+        $roles = array();
+        foreach ($allroles as $role) {
+            $roles[$role->$localrolefield] = $role->id;
+        }
+
+        // first find all existing courses with enrol instance
+        $localcoursefiled = $this->get_config('localcoursefield');
+        $sql = "SELECT c.id, c.visible, c.$localcoursefiled AS mapping, e.id AS enrolid
+                  FROM {course} c
+                  JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')";
+        $existing = array();
+        $rs = $DB->get_recordset_sql($sql); // watch out for idnumber duplicates
+        foreach ($rs as $course) {
+            if (empty($course->mapping)) {
+                continue;
+            }
+            $existing[$course->mapping] = $course;
+        }
+        $rs->close();
+
+        // add necessary enrol instances that are not present yet;
+        $sql = $this->db_get_sql($table, array(), array($coursefield), true);
+        if ($rs = $extdb->Execute($sql)) {
+            if (!$rs->EOF) {
+                $sql = "SELECT c.id, c.visible
+                          FROM {course} c
+                          JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = 'database')
+                         WHERE c.$localcoursefiled = :mapping";
+                $params = array();
+                while ($mapping = $rs->FetchRow()) {
+                    $mapping = reset($mapping);
+                    $mapping = $this->db_decode($mapping);
+                    if (!empty($mapping) and !isset($existing[$mapping])) {
+                        $params['mapping'] = $mapping;
+                        if ($course = $DB->get_record_sql($sql, $params, IGNORE_MULTIPLE)) {
+                            $new = new object();
+                            $new->id      = $course->id;
+                            $new->visible = $course->visible;
+                            $new->mapping = $mapping;
+                            $new->enrolid = $this->add_instance($course);
+                            $existing[$mapping] = $new;
+                        }
+                    }
+                }
+            }
+            $rs->Close();
+        } else {
+            debugging('Error while communicating with external enrolment database');
+            $extdb->Close();
+            return;
+        }
+
+        // sync enrolments
+        $ignorehidden = $this->get_config('ignorehiddencourses');
+        $fields = array($userfield);
+        if ($rolefield) {
+            $fields[] = $rolefield;
+        }
+        foreach ($existing as $course) {
+            if ($ignorehidden and !$course->visible) {
+                continue;
+            }
+            if (!$instance = $DB->get_record('enrol', array('id'=>$course->enrolid))) {
+                continue; //weird
+            }
+            $context = get_context_instance(CONTEXT_COURSE, $course->id);
+
+            // get current list of enrolled users with their roles
+            $current_roles  = array();
+            $current_status = array();
+            $user_mapping   = array();
+            $sql = "SELECT u.$localuserfield AS mapping, u.id, ue.status, ue.userid, ra.roleid
+                      FROM {user} u
+                      JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
+                      JOIN {role_assignments} ra ON (ra.userid = u.id AND ra.itemid = ue.enrolid AND ra.component = 'enrol_database')
+                     WHERE u.deleted = 0";
+            $params = array('enrolid'=>$instance->id);
+            if ($localuserfield === 'username') {
+                $sql .= " AND u.mnethostid = :mnethostid";
+                $params['mnethostid'] = $CFG->mnet_localhost_id;
+            }
+            $rs = $DB->get_recordset_sql($sql, $params);
+            foreach ($rs as $ue) {
+                $current_roles[$ue->userid][$ue->roleid] = $ue->roleid;
+                $current_status[$ue->userid] = $ue->status;
+                $user_mapping[$ue->mapping] = $ue->userid;
+            }
+            $rs->close();
+
+            // get list of users that need to be enrolled and their roles
+            $requested_roles = array();
+            $sql = $this->db_get_sql($table, array($coursefield=>$course->mapping), $fields);
+            if ($rs = $extdb->Execute($sql)) {
+                if (!$rs->EOF) {
+                    if ($localuserfield === 'username') {
+                        $usersearch = array('mnethostid'=>$CFG->mnet_localhost_id, 'deleted' =>0);
+                    }
+                    while ($fields = $rs->FetchRow()) {
+                        $fields = array_change_key_case($fields, CASE_LOWER);
+                        if (empty($fields[$userfield])) {
+                            //user identification is mandatory!
+                        }
+                        $mapping = $fields[$userfield];
+                        if (!isset($user_mapping[$mapping])) {
+                            $usersearch[$localuserfield] = $mapping;
+                            if (!$user = $DB->get_record('user', $usersearch, 'id', IGNORE_MULTIPLE)) {
+                                // user does not exist or was deleted
+                                continue;
+                            }
+                            $user_mapping[$mapping] = $user->id;
+                            $userid = $user->id;
+                        } else {
+                            $userid = $user_mapping[$mapping];
+                        }
+                        if (empty($fields[$rolefield]) or !isset($roles[$fields[$rolefield]])) {
+                            if (!$defaultrole) {
+                                // role is mandatory
+                                continue;
+                            }
+                            $roleid = $defaultrole;
+                        } else {
+                            $roleid = $roles[$fields[$rolefield]];
+                        }
+
+                        $requested_roles[$userid][$roleid] = $roleid;
+                    }
+                }
+                $rs->Close();
+            } else {
+                debugging('Error while communicating with external enrolment database');
+                $extdb->Close();
+                return;
+            }
+            unset($user_mapping);
+
+            // enrol all users and sync roles
+            foreach ($requested_roles as $userid=>$roles) {
+                foreach ($roles as $roleid) {
+                    if (empty($current_roles[$userid])) {
+                        $this->enrol_user($instance, $userid, $roleid);
+                        $current_roles[$userid][$roleid] = $roleid;
+                        $current_status[$userid] = ENROL_USER_ACTIVE;
+                    }
+                }
+
+                // unassign removed roles
+                foreach($current_roles[$userid] as $cr) {
+                    if (empty($roles[$cr])) {
+                        role_unassign($cr, $userid, $context->id, 'enrol_database', $instance->id);
+                        unset($current_roles[$userid][$cr]);
+                    }
+                }
+
+                // reenable enrolment when previously disable enrolment refreshed
+                if ($current_status[$userid] == ENROL_USER_SUSPENDED) {
+                    $DB->set_field('user_enrolments', 'status', ENROL_USER_ACTIVE, array('enrolid'=>$instance->id, 'userid'=>$userid));
+                }
+            }
+
+            // deal with enrolments removed from external table
+            if ($unenrolaction == 0) {
+                // unenrol
+                if (!empty($requested_roles)) {
+                    // we might get some error or connection problem, better not unenrol everybody
+                    foreach ($current_status as $userid=>$status) {
+                        if (isset($requested_roles[$userid])) {
+                            continue;
+                        }
+                        $this->unenrol_user($instance, $userid);
+                    }
+                }
+
+            } else if ($unenrolaction == 1) {
+                // keep - only adding enrolments
+
+            } else if ($unenrolaction == 2) {
+                // disable
+                foreach ($current_status as $userid=>$status) {
+                    if (isset($requested_roles[$userid])) {
+                        continue;
+                    }
+                    if ($status != ENROL_USER_SUSPENDED) {
+                        $DB->set_field('user_enrolments', 'status', ENROL_USER_SUSPENDED, array('enrolid'=>$instance->id, 'userid'=>$userid));
+                    }
+                }
+            }
+        }
+
+        // close db connection
+        $extdb->Close();
+    }
+
+    /**
+     * Performs a full sync with external database.
+     *
+     * First it creates new courses if necessary, then
+     * enrols and unenrols users.
+     * @return void
+     */
+    public function sync_courses() {
+        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')) {
+            return;
+        }
+
+        // we may need a lot of memory here
+        @set_time_limit(0);
+        @raise_memory_limit("512M");
+
+        $extdb = $this->db_init();
+
+        // first create new courses
+        $table     = $this->get_config('newcoursetable');
+        $fullname  = strtolower($this->get_config('newcoursefullname'));
+        $shortname = strtolower($this->get_config('newcourseshortname'));
+        $idnumber  = strtolower($this->get_config('newcourseidnumber'));
+        $category  = strtolower($this->get_config('newcoursecategory'));
+
+        $fields = array($fullname, $shortname, $idnumber);
+        if ($category) {
+            $fields[] = $category;
+        }
+        if ($idnumber) {
+            $fields[] = $idnumber;
+        }
+        $sql = $this->db_get_sql($table, array(), $fields);
+        $createcourses = array();
+        if ($rs = $extdb->Execute($sql)) {
+            if (!$rs->EOF) {
+                $courselist = array();
+                while ($fields = $rs->FetchRow()) {
+                    $fields = array_change_key_case($fields, CASE_LOWER);
+                    if (empty($fields[$shortname]) or empty($fields[$fullname])) {
+                        //invalid record - these two are mandatory
+                        continue;
+                    }
+                    $fields = $this->db_decode($fields);
+                    if ($DB->record_exists('course', array('shortname'=>$fields[$shortname]))) {
+                        // already exists
+                        continue;
+                    }
+                    if ($idnumber and $DB->record_exists('course', array('idnumber'=>$fields[$idnumber]))) {
+                        // idnumber duplicates are not allowed
+                        continue;
+                    }
+                    if ($category and !$DB->record_exists('course_categories', array('id'=>$fields[$category]))) {
+                        // invalid category id, better to skip
+                        continue;
+                    }
+                    $course = new object();
+                    $course->fullname  = $fields[$fullname];
+                    $course->shortname = $fields[$shortname];
+                    $course->idnumber  = $idnumber ? $fields[$idnumber] : NULL;
+                    $course->category  = $category ? $fields[$category] : NULL;
+                    $createcourses[] = $course;
+                }
+            }
+            $rs->Close();
+        } else {
+            debugging('Error while communicating with external enrolment database');
+            $extdb->Close();
+            return;
+        }
+        if ($createcourses) {
+            require_once("$CFG->dirroot/course/lib.php");
+
+            $template        = $this->get_config('templatecourse');
+            $defaultcategory = $this->get_config('defaultcategory');
+
+            if ($template) {
+                if ($template = $DB->get_record('course', array('shortname'=>$template))) {
+                    unset($template->id);
+                    unset($template->fullname);
+                    unset($template->shortname);
+                    unset($template->idnumber);
+                } else {
+                    $template = new object();
+                }
+            } else {
+                $template = new object();
+            }
+            if (!$DB->record_exists('course_categories', array('id'=>$defaultcategory))) {
+                $categories = $DB->get_records('course_categories', array(), 'sortorder', 'id', 0, 1);
+                $first = reset($categories);
+                $defaultcategory = $first->id;
+            }
+
+            foreach ($createcourses as $fields) {
+                $newcourse = clone($template);
+                $newcourse->fullname  = $fields->fullname;
+                $newcourse->shortname = $fields->shortname;
+                $newcourse->idnumber  = $fields->idnumber;
+                $newcourse->category  = $fields->category ? $fields->category : $defaultcategory;
+
+                create_course($newcourse);
+            }
+
+            unset($createcourses);
+            unset($template);
+        }
+
+        // close db connection
+        $extdb->Close();
+    }
+
+    protected function db_get_sql($table, array $conditions, array $fields, $distinct = false, $sort = "") {
+        $fields = $fields ? implode(',', $fields) : "*";
+        $where = array();
+        if ($conditions) {
+            foreach ($conditions as $key=>$value) {
+                $value = $this->db_encode($this->db_addslashes($value));
+
+                $where[] = "$key = '$value'";
+            }
+        }
+        $where = $where ? "WHERE ".implode(" AND ", $where) : "";
+        $sort = $sort ? "ORDER BY $sort" : "";
+        $distinct = $distinct ? "DISTINCT" : "";
+        $sql = "SELECT $distinct $fields
+                  FROM $table
+                 $where
+                  $sort";
+
+        return $sql;
+    }
+
+    protected function db_init() {
+        global $CFG;
+
+        require_once($CFG->libdir.'/adodb/adodb.inc.php');
+
+        // Connect to the external database (forcing new connection)
+        $extdb = ADONewConnection($this->get_config('dbtype'));
+        if ($this->get_config('debugdb')) {
+            $extdb->debug = true;
+            ob_start(); //start output buffer to allow later use of the page headers
+        }
+
+        $extdb->Connect($this->get_config('dbhost'), $this->get_config('dbuser'), $this->get_config('dbpass'), $this->get_config('dbname'), true);
+        $extdb->SetFetchMode(ADODB_FETCH_ASSOC);
+        if ($this->get_config('dbsetupsql')) {
+            $extdb->Execute($this->get_config('dbsetupsql'));
+        }
+        return $extdb;
+    }
+
+    protected function db_addslashes($text) {
+        // using custom made function for now
+        if ($this->get_config('dbsybasequoting')) {
+            $text = str_replace('\\', '\\\\', $text);
+            $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text);
+        } else {
+            $text = str_replace("'", "''", $text);
+        }
+        return $text;
+    }
+
+    protected function db_encode($text) {
+        $dbenc = $this->get_config('dbencoding');
+        if (empty($dbenc) or $dbenc == 'utf-8') {
+            return $text;
+        }
+        if (is_array($text)) {
+            foreach($text as $k=>$value) {
+                $text[$k] = $this->db_encode($value);
+            }
+            return $text;
+        } else {
+            return textlib_get_instance()->convert($text, 'utf-8', $dbenc);
+        }
+    }
+
+    protected function db_decode($text) {
+        $dbenc = $this->get_config('dbencoding');
+        if (empty($dbenc) or $dbenc == 'utf-8') {
+            return $text;
+        }
+        if (is_array($text)) {
+            foreach($text as $k=>$value) {
+                $text[$k] = $this->db_decode($value);
+            }
+            return $text;
+        } else {
+            return textlib_get_instance()->convert($text, $dbenc, 'utf-8');
+        }
+    }
+}
+
diff --git a/enrol/database/settings.php b/enrol/database/settings.php
new file mode 100644
index 000000000000..f776956d9e30
--- /dev/null
+++ b/enrol/database/settings.php
@@ -0,0 +1,115 @@
+<?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/>.
+
+/**
+ * Database enrolment plugin settings and presets.
+ *
+ * @package   enrol_database
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_database_settings', '', get_string('pluginname_desc', 'enrol_database')));
+
+    $settings->add(new admin_setting_heading('enrol_database_exdbheader', get_string('settingsheaderdb', 'enrol_database'), ''));
+
+    $options = array('', "access","ado_access", "ado", "ado_mssql", "borland_ibase", "csv", "db2", "fbsql", "firebird", "ibase", "informix72", "informix", "mssql", "mssql_n", "mysql", "mysqli", "mysqlt", "oci805", "oci8", "oci8po", "odbc", "odbc_mssql", "odbc_oracle", "oracle", "postgres64", "postgres7", "postgres", "proxy", "sqlanywhere", "sybase", "vfp");
+    $options = array_combine($options, $options);
+    $settings->add(new admin_setting_configselect('enrol_database/dbtype', get_string('dbtype', 'enrol_database'), get_string('dbtype_desc', 'enrol_database'), '', $options));
+
+    $settings->add(new admin_setting_configtext('enrol_database/dbhost', get_string('dbhost', 'enrol_database'), get_string('dbhost_desc', 'enrol_database'), 'localhost'));
+
+    $settings->add(new admin_setting_configtext('enrol_database/dbuser', get_string('dbuser', 'enrol_database'), '', ''));
+
+    $settings->add(new admin_setting_configpasswordunmask('enrol_database/dbpass', get_string('dbpass', 'enrol_database'), '', ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/dbname', get_string('dbname', 'enrol_database'), '', ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/dbencoding', get_string('dbencoding', 'enrol_database'), '', 'utf-8'));
+
+    $settings->add(new admin_setting_configtext('enrol_database/dbsetupsql', get_string('dbsetupsql', 'enrol_database'), get_string('dbsetupsql_desc', 'enrol_database'), ''));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_database/dbsybasequoting', get_string('dbsybasequoting', 'enrol_database'), get_string('dbsybasequoting_desc', 'enrol_database'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_database/debugdb', get_string('debugdb', 'enrol_database'), get_string('debugdb_desc', 'enrol_database'), 0));
+
+
+
+    $settings->add(new admin_setting_heading('enrol_database_localheader', get_string('settingsheaderlocal', 'enrol_database'), ''));
+
+    $options = array('id'=>'id', 'idnumber'=>'idnumber', 'shortname'=>'shortname');
+    $settings->add(new admin_setting_configselect('enrol_database/localcoursefield', get_string('localcoursefield', 'enrol_database'), '', 'idnumber', $options));
+
+    $options = array('id'=>'id', 'idnumber'=>'idnumber', 'email'=>'email', 'username'=>'username'); // only local users if username selected, no mnet users!
+    $settings->add(new admin_setting_configselect('enrol_database/localuserfield', get_string('localuserfield', 'enrol_database'), '', 'idnumber', $options));
+
+    $options = array('id'=>'id', 'shortname'=>'shortname', 'fullname'=>'fullname');
+    $settings->add(new admin_setting_configselect('enrol_database/localrolefield', get_string('localrolefield', 'enrol_database'), '', 'shortname', $options));
+
+
+
+    $settings->add(new admin_setting_heading('enrol_database_remoteheader', get_string('settingsheaderremote', 'enrol_database'), ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/remoteenroltable', get_string('remoteenroltable', 'enrol_database'), get_string('remoteenroltable_desc', 'enrol_database'), ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/remotecoursefield', get_string('remotecoursefield', 'enrol_database'), get_string('remotecoursefield_desc', 'enrol_database'), ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/remoteuserfield', get_string('remoteuserfield', 'enrol_database'), get_string('remoteuserfield_desc', 'enrol_database'), ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/remoterolefield', get_string('remoterolefield', 'enrol_database'), get_string('remoterolefield_desc', 'enrol_database'), ''));
+
+    if (!during_initial_install()) {
+        $options = get_default_enrol_roles(get_context_instance(CONTEXT_SYSTEM));
+        $student = get_archetype_roles('student');
+        $student = reset($student);
+        $settings->add(new admin_setting_configselect('enrol_database/defaultrole', get_string('defaultrole', 'enrol_database'), get_string('defaultrole_desc', 'enrol_database'), $student->id, $options));
+    }
+
+    $settings->add(new admin_setting_configcheckbox('enrol_database/ignorehiddencourses', get_string('ignorehiddencourses', 'enrol_database'), get_string('ignorehiddencourses_desc', 'enrol_database'), 0));
+
+    $options = array(0=>get_string('unenrolactionunenrol', 'enrol_database'), 1=>get_string('unenrolactionkeep', 'enrol_database'), 2=>get_string('unenrolactiondisable', 'enrol_database'));
+    $settings->add(new admin_setting_configselect('enrol_database/unenrolaction', get_string('unenrolaction', 'enrol_database'), get_string('unenrolaction_desc', 'enrol_database'), 0, $options));
+
+
+
+    $settings->add(new admin_setting_heading('enrol_database_newcoursesheader', get_string('settingsheadernewcourses', 'enrol_database'), ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/newcoursetable', get_string('newcoursetable', 'enrol_database'), get_string('newcoursetable_desc', 'enrol_database'), ''));
+
+    $settings->add(new admin_setting_configtext('enrol_database/newcoursefullname', get_string('newcoursefullname', 'enrol_database'), '', 'fullname'));
+
+    $settings->add(new admin_setting_configtext('enrol_database/newcourseshortname', get_string('newcourseshortname', 'enrol_database'), '', 'shortname'));
+
+    $settings->add(new admin_setting_configtext('enrol_database/newcourseidnumber', get_string('newcourseidnumber', 'enrol_database'), '', 'idnumber'));
+
+    $settings->add(new admin_setting_configtext('enrol_database/newcoursecategory', get_string('newcoursecategory', 'enrol_database'), '', ''));
+
+    if (!during_initial_install()) {
+        $options = array();
+        $parentlist = array();
+        make_categories_list($options, $parentlist);
+        $settings->add(new admin_setting_configselect('enrol_database/defaultcategory', get_string('defaultcategory', 'enrol_database'), get_string('defaultcategory_desc', 'enrol_database'), 1, $options));
+        unset($parentlist);
+    }
+
+    $settings->add(new admin_setting_configtext('enrol_database/templatecourse', get_string('templatecourse', 'enrol_database'), get_string('templatecourse_desc', 'enrol_database'), ''));
+}
diff --git a/enrol/database/version.php b/enrol/database/version.php
new file mode 100644
index 000000000000..1b8e1c60a5f8
--- /dev/null
+++ b/enrol/database/version.php
@@ -0,0 +1,27 @@
+<?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/>.
+
+/**
+ * Database enrolment plugin version specification.
+ *
+ * @package   enrol_database
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$plugin->version = 2010061500;
+//TODO: should we add cron sync?
\ No newline at end of file
diff --git a/enrol/enrol.class.php b/enrol/enrol.class.php
deleted file mode 100644
index 8ff8afd60503..000000000000
--- a/enrol/enrol.class.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-/**
-* enrolment_factory is used to "manufacture" an instance of required enrolment plugin.
-*/
-
-class enrolment_factory {
-    function factory($enrol = '') {
-        global $CFG, $OUTPUT;
-        if (!$enrol) {
-            $enrol = $CFG->enrol;
-        }
-        if (file_exists("$CFG->dirroot/enrol/$enrol/enrol.php")) {
-            require_once("$CFG->dirroot/enrol/$enrol/enrol.php");
-            $class = "enrolment_plugin_$enrol";
-            return new $class;
-        } else {
-            error_log("$CFG->dirroot/enrol/$enrol/enrol.php does not exist");
-            echo $OUTPUT->notification("Enrolment file $enrol/enrol.php does not exist");
-        }
-    }
-}
diff --git a/enrol/externallib.php b/enrol/externallib.php
index c48239e6f54d..85cbf80a69fc 100644
--- a/enrol/externallib.php
+++ b/enrol/externallib.php
@@ -16,35 +16,119 @@
 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 
 /**
- * External enrol API
+ * External course participation api.
  *
- * @package    moodlecore
- * @subpackage webservice
+ * This api is mostly read only, the actual enrol and unenrol
+ * support is in each enrol plugin.
+ *
+ * @package    core
+ * @subpackage enrol
  * @copyright  2009 Moodle Pty Ltd (http://moodle.com)
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+defined('MOODLE_INTERNAL') || die;
+
 require_once("$CFG->libdir/externallib.php");
 
+
 class moodle_enrol_external extends external_api {
 
     /**
      * Returns description of method parameters
      * @return external_function_parameters
      */
-    public static function role_assign_parameters() {
-        global $CFG;
+    public static function get_enrolled_users() {
+        return new external_function_parameters(
+            array(
+                'courseid'       => new external_value(PARAM_INT, 'Course id'),
+                'withcapability' => new external_value(PARAM_CAPABILITY, 'User should have this capability'),
+                'groupid'        => new external_value(PARAM_INT, 'Group id, null means all groups'),
+                'activeonly'     => new external_value(PARAM_INT, 'True means only active, false means all participants'),
+            )
+        );
+    }
+
+    /**
+     * Get list of course participants.
+     *
+     * @param int $courseid
+     * @param text $withcapability
+     * @param int $groupid
+     * @param bool $onlyactive
+     * @return array of course participants
+     */
+    public static function get_enrolled_users($courseid, $withcapability, $groupid, $onlyactive) {
+        global $DB;
+
+        // Do basic automatic PARAM checks on incoming data, using params description
+        // If any problems are found then exceptions are thrown with helpful error messages
+        $params = self::validate_parameters(self::get_enrolled_users(), array('courseid'=>$courseid, 'withcapability'=>$withcapability, 'groupid'=>$groupid, 'onlyactive'=>$onlyactive));
+
+        $coursecontext = get_context_instance(CONTEXT_COURSE, $params['courseid']);
+        if ($courseid == SITEID) {
+            $systemcontext = get_context_instance(CONTEXT_SYSTEM);
+        } else {
+            $context = $coursecontext;
+        }
+
+        self::validate_context($context);
+
+        if ($courseid == SITEID) {
+            require_capability('moodle/site:viewparticipants', $context);
+        } else {
+            require_capability('moodle/course:viewparticipants', $context);
+        }
+
+        if ($withcapability) {
+            require_capability('moodle/role:review', $coursecontext);
+        }
+        if ($groupid) {
+            if (groups_is_member($groupid)) {
+                require_capability('moodle/site:accessallgroups', $coursecontext);
+            }
+        }
+        if ($activeonly) {
+            require_capability('moodle/course:enrolreview', $coursecontext);
+        }
+
+        list($sql, $params) =  get_enrolled_sql($coursecontext, $withcapability, $groupid, $onlyactive);
+        $sql = "SELECT e.courseid, ue.userid
+                  FROM {user_enrolments} ue
+                  JOIN {enrol} e ON (e.id = ue.enrolid)
+                 WHERE e.courseid = :courseid AND ue.userid IN ($sql)";
+        $params['courseid'] = $courseid;
 
+        return $DB->get_records_sql($sql, $params);
+    }
+
+    /**
+     * Returns description of method result value
+     * @return external_description
+     */
+    public static function get_enrolled_users_returns() {
+        new external_single_structure(
+            array(
+                'courseid' => new external_value(PARAM_INT, 'id of course'),
+                'userid' => new external_value(PARAM_INT, 'id of user'),
+            )
+        );
+    }
+
+
+    /**
+     * Returns description of method parameters
+     * @return external_function_parameters
+     */
+    public static function role_assign_parameters() {
         return new external_function_parameters(
             array(
-                'enrolments' => new external_multiple_structure(
+                'assignments' => new external_multiple_structure(
                     new external_single_structure(
                         array(
                             'roleid'    => new external_value(PARAM_INT, 'Role to assign to the user'),
                             'userid'    => new external_value(PARAM_INT, 'The user that is going to be assigned'),
-                            'contextid' => new external_value(PARAM_INT, 'The context to assign the user into '),
-                            'timestart' => new external_value(PARAM_INT, 'A valid and unique email address', VALUE_DEFAULT, 0),
-                            'timeend'   => new external_value(PARAM_INT, 'Auth plugins include manual, ldap, imap, etc', VALUE_DEFAULT, 0)
+                            'contextid' => new external_value(PARAM_INT, 'The context to assign the user role in'),
                         )
                     )
                 )
@@ -53,36 +137,30 @@ public static function role_assign_parameters() {
     }
 
     /**
-     * Assign roles to users
+     * Manual role assignments to users
      *
-     * @param array $enrolment  An array of enrolment
+     * @param array $assignment  An array of manual role assignment
      * @return null
      */
-    public static function role_assign($enrolments) {
-        global $CFG, $DB;
+    public static function role_assign($assignments) {
+        global $DB;
 
         // Do basic automatic PARAM checks on incoming data, using params description
         // If any problems are found then exceptions are thrown with helpful error messages
-        $params = self::validate_parameters(self::role_assign_parameters(), array('enrolments'=>$enrolments));
+        $params = self::validate_parameters(self::role_assign_parameters(), array('assignments'=>$assignments));
 
         $transaction = $DB->start_delegated_transaction();
 
-        $success = true;
-
-        foreach ($params['enrolments'] as $enrolment) {
+        foreach ($params['assignments'] as $assignment) {
             // Ensure the current user is allowed to run this function in the enrolment context
-            $context = get_context_instance_by_id($enrolment['contextid']);
+            $context = get_context_instance_by_id($assignment['contextid']);
             self::validate_context($context);
             require_capability('moodle/role:assign', $context);
 
-            if(!role_assign($enrolment['roleid'], $enrolment['userid'], null, $enrolment['contextid'], $enrolment['timestart'], $enrolment['timeend'])) {
-                $success = false;
-            }
+            role_assign($assignment['roleid'], $assignment['userid'], $assignment['contextid']);
         }
 
         $transaction->allow_commit();
-
-        return $success;
     }
 
     /**
@@ -90,7 +168,7 @@ public static function role_assign($enrolments) {
      * @return external_description
      */
     public static function role_assign_returns() {
-        return new external_value(PARAM_BOOL, 'If all assignement succeed returns true');
+        return null;
     }
 
 
@@ -101,50 +179,44 @@ public static function role_assign_returns() {
     public static function role_unassign_parameters() {
         return new external_function_parameters(
             array(
-               'unenrolments' => new external_multiple_structure(
+                'unassignments' => new external_multiple_structure(
                     new external_single_structure(
                         array(
                             'roleid'    => new external_value(PARAM_INT, 'Role to assign to the user'),
                             'userid'    => new external_value(PARAM_INT, 'The user that is going to be assigned'),
-                            'contextid' => new external_value(PARAM_INT, 'The context to assign the user into '),
-                            )
+                            'contextid' => new external_value(PARAM_INT, 'The context to unassign the user role from'),
+                        )
                     )
+                )
             )
-        )
-       );
+        );
     }
 
      /**
-     * Unassign roles to users
+     * Unassign roles from users
      *
-     * @param array $unenrolment  An array of unenrolment
+     * @param array $unassignment  An array of unassignment
      * @return null
      */
-    public static function role_unassign($unenrolments) {
-         global $CFG, $DB;
+    public static function role_unassign($unassignments) {
+         global $DB;
 
         // Do basic automatic PARAM checks on incoming data, using params description
         // If any problems are found then exceptions are thrown with helpful error messages
-        $params = self::validate_parameters(self::role_unassign_parameters(), array('unenrolments'=>$unenrolments));
+        $params = self::validate_parameters(self::role_unassign_parameters(), array('unassignments'=>$unassignments));
 
         $transaction = $DB->start_delegated_transaction();
 
-        $success = true;
-
-        foreach ($params['unenrolments'] as $unenrolment) {
-            // Ensure the current user is allowed to run this function in the unenrolment context
-            $context = get_context_instance_by_id($unenrolment['contextid']);
+        foreach ($params['unassignments'] as $unassignment) {
+            // Ensure the current user is allowed to run this function in the unassignment context
+            $context = get_context_instance_by_id($unassignment['contextid']);
             self::validate_context($context);
             require_capability('moodle/role:assign', $context);
 
-            if (!role_unassign($unenrolment['roleid'], $unenrolment['userid'], null, $unenrolment['contextid'])) {
-                $success = false;
-            }
+            role_unassign($unassignment['roleid'], $unassignment['userid'], $unassignment['contextid']);
         }
 
         $transaction->allow_commit();
-
-        return $success;
     }
 
    /**
@@ -152,7 +224,6 @@ public static function role_unassign($unenrolments) {
      * @return external_description
      */
     public static function role_unassign_returns() {
-        return new external_value(PARAM_BOOL, 'If all unassignement succeed returns true');
+        return null;
     }
-   
 }
diff --git a/enrol/flatfile/enrol.php b/enrol/flatfile/enrol.php
index e6a71073e94b..229162484b0e 100644
--- a/enrol/flatfile/enrol.php
+++ b/enrol/flatfile/enrol.php
@@ -187,9 +187,10 @@ function cron() {
                     $context = get_context_instance(CONTEXT_COURSE, $course->id);
 
                     if ($fields[0] == 'add') {
-                        role_assign($roleid, $user->id, null, $context->id, $fields[4], $fields[5], 0, 'flatfile');
+                        // TODO: real enrol, and maybe manual
+                        role_assign($roleid, $user->id, $context->id, 'enrol_flatfile');
                     } else {
-                        role_unassign($roleid, $user->id, null, $context->id);
+                        role_unassign($roleid, $user->id, $context->id);
                     }
 
 
diff --git a/enrol/guest/addinstance.php b/enrol/guest/addinstance.php
new file mode 100644
index 000000000000..92f77c2726ec
--- /dev/null
+++ b/enrol/guest/addinstance.php
@@ -0,0 +1,43 @@
+<?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/>.
+
+/**
+ * Adds new instance of enrol_guest to specified course.
+ *
+ * @package   enrol_guest
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+
+$id = required_param('id', PARAM_INT); // course id
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('moodle/course:enrolconfig', $context);
+require_sesskey();
+
+$enrol = enrol_get_plugin('guest');
+
+if ($enrol->get_candidate_link($course->id)) {
+    $enrol->add_default_instance($course);
+}
+
+redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
diff --git a/enrol/guest/db/access.php b/enrol/guest/db/access.php
new file mode 100644
index 000000000000..81a4fb277619
--- /dev/null
+++ b/enrol/guest/db/access.php
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * Capabilities for guest access plugin.
+ *
+ * @package   enrol_guest
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$capabilities = array(
+
+    'enrol/guest:config' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+        )
+    ),
+
+);
+
+
diff --git a/enrol/guest/lang/en/enrol_guest.php b/enrol/guest/lang/en/enrol_guest.php
new file mode 100644
index 000000000000..4643cb0c9d9a
--- /dev/null
+++ b/enrol/guest/lang/en/enrol_guest.php
@@ -0,0 +1,40 @@
+<?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/>.
+
+/**
+ * Strings for component 'enrol_guest', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package   enrol_guest
+ * @copyright 2010 onwards Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['guest:config'] = 'Configure guest access instances';
+$string['password'] = 'Password';
+$string['passwordinvalid'] = 'Incorrect access password, please try again';
+$string['passwordinvalidhint'] = 'That access password was incorrect, please try again<br />
+(Here\'s a hint - it starts with \'{$a}\')';
+$string['pluginname'] = 'Guest access';
+$string['pluginname_desc'] = 'Guest access plugin is only granting temporary access to courses, it is not actually enrolling users.';
+$string['requirepassword'] = 'Require guest access passsword';
+$string['requirepassword_desc'] = 'Require access password in new courses and prevent removing of access password from existing courses.';
+$string['showhint'] = 'Show hint';
+$string['showhint_desc'] = 'Show first letter of the guest access password.';
+$string['status'] = 'Allow guest access';
+$string['status_desc'] = 'Allow temporary guest access by default.';
+$string['usepasswordpolicy'] = 'Use password policy';
+$string['usepasswordpolicy_desc'] = 'Use standard password policy for guest access passwords.';
diff --git a/enrol/guest/lib.php b/enrol/guest/lib.php
new file mode 100644
index 000000000000..6d832911be9a
--- /dev/null
+++ b/enrol/guest/lib.php
@@ -0,0 +1,291 @@
+<?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/>.
+
+/**
+ * Guest access plugin.
+ *
+ * This plugin does not add any entries into the user_enrolments table,
+ * the access control is granted on the fly via the tricks in require_login().
+ *
+ * @package   enrol_guest
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+class enrol_guest_plugin extends enrol_plugin {
+
+    public function enrol_user(stdClass $instance, $userid, $roleid = null, $timestart = 0, $timeend = 0) {
+        // no real enrolments here!
+        return;
+    }
+
+    public function unenrol_user(stdClass $instance, $userid) {
+        // nothing to do, we never enrol here!
+        return;
+    }
+
+    /**
+     * Attempt to automatically gain temporary guest access to course,
+     * calling code has to make sure the plugin and instance are active.
+     *
+     * @param stdClass $instance course enrol instance
+     * @param stdClass $user record
+     * @return bool|int false means no guest access, integer means end of cached time
+     */
+    public function try_guestaccess(stdClass $instance) {
+        global $USER, $CFG;
+
+        if (empty($instance->password)) {
+            // Temporarily assign them some guest role for this context
+            $context = get_context_instance(CONTEXT_COURSE, $instance->courseid);
+            $USER->access = load_temp_role($context, $CFG->guestroleid, $USER->access);
+            return ENROL_REQUIRE_LOGIN_CACHE_PERIOD;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * @param int $courseid
+     * @return moodle_url page url
+     */
+    public function get_candidate_link($courseid) {
+        global $DB;
+
+        if (!has_capability('moodle/course:enrolconfig', get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST))) {
+            return NULL;
+        }
+
+        if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'guest'))) {
+            return NULL;
+        }
+
+        return new moodle_url('/enrol/guest/addinstance.php', array('sesskey'=>sesskey(), 'id'=>$courseid));
+    }
+
+    /**
+     * Creates course enrol form, checks if form submitted
+     * and enrols user if necessary. It can also redirect.
+     *
+     * @param stdClass $instance
+     * @return string html text, usually a form in a text box
+     */
+    public function enrol_page_hook(stdClass $instance) {
+        global $CFG, $OUTPUT, $SESSION, $USER;
+
+        if (empty($instance->password)) {
+            return null;
+        }
+
+        require_once("$CFG->dirroot/enrol/guest/locallib.php");
+        $form = new enrol_guest_enrol_form(NULL, $instance);
+        $instanceid = optional_param('instance', 0, PARAM_INT);
+
+        if ($instance->id == $instanceid) {
+            if ($data = $form->get_data()) {
+                // set up primitive require_login() caching
+                unset($USER->enrol['enrolled'][$instance->courseid]);
+                $USER->enrol['tempguest'][$instance->courseid] = time() + 60*60*8; // 8 hours access before asking for pw again
+
+                // add guest role
+                $context = get_context_instance(CONTEXT_COURSE, $instance->courseid);
+                $USER->access = load_temp_role($context, $CFG->guestroleid, $USER->access);
+
+                // go to the originally requested page
+                if (!empty($SESSION->wantsurl)) {
+                    $destination = $SESSION->wantsurl;
+                    unset($SESSION->wantsurl);
+                } else {
+                    $destination = "$CFG->wwwroot/course/view.php?id=$instance->courseid";
+                }
+                redirect($destination);
+            }
+        }
+
+        ob_start();
+        $form->display();
+        $output = ob_get_clean();
+
+        return $OUTPUT->box($output, 'generalbox');
+    }
+
+    /**
+     * Adds enrol instance UI to course edit form
+     *
+     * @param object $instance enrol instance or null if does not exist yet
+     * @param MoodleQuickForm $mform
+     * @param object $data
+     * @param object $context context of existing course or parent category if course does not exist
+     * @return void
+     */
+    public function course_edit_form($instance, MoodleQuickForm $mform, $data, $context) {
+
+        $i = isset($instance->id) ? $instance->id : 0;
+        $plugin = enrol_get_plugin('guest');
+        $header = $plugin->get_instance_name($instance);
+        $config = has_capability('enrol/guest:config', $context);
+
+        $mform->addElement('header', 'enrol_guest_header_'.$i, $header);
+
+
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        $mform->addElement('select', 'enrol_guest_status_'.$i, get_string('status', 'enrol_guest'), $options);
+        $mform->setDefault('enrol_guest_status_'.$i, $this->get_config('status'));
+        $mform->setAdvanced('enrol_guest_status_'.$i, $this->get_config('status_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_guest_status_'.$i);
+        }
+
+        $mform->addElement('passwordunmask', 'enrol_guest_password_'.$i, get_string('password', 'enrol_guest'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_guest_password_'.$i);
+        } else {
+            $mform->disabledIf('enrol_guest_password_'.$i, 'enrol_guest_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        // now add all values from enrol table
+        if ($instance) {
+            foreach($instance as $key=>$val) {
+                $data->{'enrol_guest_'.$key.'_'.$i} = $val;
+            }
+        }
+    }
+
+    /**
+     * Validates course edit form data
+     *
+     * @param object $instance enrol instance or null if does not exist yet
+     * @param array $data
+     * @param object $context context of existing course or parent category if course does not exist
+     * @return array errors array
+     */
+    public function course_edit_validation($instance, array $data, $context) {
+        $errors = array();
+
+        if (!has_capability('enrol/guest:config', $context)) {
+            // we are going to ignore the data later anyway, they would nto be able to fix the form anyway
+            return $errors;
+        }
+
+        $i = isset($instance->id) ? $instance->id : 0;
+
+        $password = empty($data['enrol_guest_password_'.$i]) ? '' : $data['enrol_guest_password_'.$i];
+        $checkpassword = false;
+
+        if ($instance) {
+            if ($data['enrol_guest_status_'.$i] == ENROL_INSTANCE_ENABLED) {
+                if ($instance->password !== $password) {
+                    $checkpassword = true;
+                }
+            }
+        } else {
+            if ($data['enrol_guest_status_'.$i] == ENROL_INSTANCE_ENABLED) {
+                $checkpassword = true;
+            }
+        }
+
+        if ($checkpassword) {
+            $require = $this->get_config('requirepassword');
+            $policy  = $this->get_config('usepasswordpolicy');
+            if ($require and empty($password)) {
+                $errors['enrol_guest_password_'.$i] = get_string('required');
+            } else if ($policy) {
+                $errmsg = '';//prevent eclipse warning
+                if (!check_password_policy($password, $errmsg)) {
+                    $errors['enrol_guest_password_'.$i] = $errmsg;
+                }
+            }
+        }
+
+        return $errors;
+    }
+
+    /**
+     * Called after updating/inserting course.
+     *
+     * @param bool $inserted true if course just inserted
+     * @param object $course
+     * @param object $data form data
+     * @return void
+     */
+    public function course_updated($inserted, $course, $data) {
+        global $DB;
+
+        $context = get_context_instance(CONTEXT_COURSE, $course->id);
+
+        if (has_capability('enrol/guest:config', $context)) {
+            if ($inserted) {
+                if (isset($data->enrol_guest_status_0)) {
+                    $fields = array('status'=>$data->enrol_guest_status_0);
+                    if ($fields['status'] == ENROL_INSTANCE_ENABLED) {
+                        $fields['password'] = $data->enrol_guest_password_0;
+                    } else {
+                        if ($this->get_config('requirepassword')) {
+                            $fields['password'] = generate_password(20);
+                        }
+                    }
+                    $this->add_instance($course, $fields);
+                }
+            } else {
+                $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'guest'));
+                foreach ($instances as $instance) {
+                    $i = $instance->id;
+
+                    if (isset($data->{'enrol_guest_status_'.$i})) {
+                        $instance->status       = $data->{'enrol_guest_status_'.$i};
+                        $instance->timemodified = time();
+                        if ($instance->status == ENROL_INSTANCE_ENABLED) {
+                            $instance->password = $data->{'enrol_guest_password_'.$i};
+                        }
+                        $DB->update_record('enrol', $instance);
+                    }
+                }
+            }
+
+        } else {
+            if ($inserted) {
+                if ($this->get_config('defaultenrol')) {
+                    $this->add_default_instance($course);
+                }
+            } else {
+                // bad luck, user can not change anything
+            }
+        }
+    }
+
+    /**
+     * Add new instance of enrol plugin with default settings.
+     * @param object $course
+     * @return int id of new instance
+     */
+    public function add_default_instance($course) {
+        $fields = array('status'=>$this->get_config('status'));
+
+        if ($this->get_config('requirepassword')) {
+            $fields['password'] = generate_password(20);
+        }
+
+        return $this->add_instance($course, $fields);
+    }
+
+}
+
diff --git a/enrol/guest/locallib.php b/enrol/guest/locallib.php
new file mode 100644
index 000000000000..ab9f07b99469
--- /dev/null
+++ b/enrol/guest/locallib.php
@@ -0,0 +1,76 @@
+<?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/>.
+
+/**
+ * Guest access plugin implementation.
+ *
+ * @package   enrol_guest
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/formslib.php");
+
+class enrol_guest_enrol_form extends moodleform {
+    protected $instance;
+
+    public function definition() {
+        $mform = $this->_form;
+        $instance = $this->_customdata;
+        $this->instance = $instance;
+        $plugin = enrol_get_plugin('guest');
+
+        $heading = $plugin->get_instance_name($instance);
+        $mform->addElement('header', 'guestheader', $heading);
+
+        $mform->addElement('passwordunmask', 'guestpassword', get_string('password', 'enrol_guest'));
+
+        $this->add_action_buttons(false, get_string('submit'));
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+        $mform->setDefault('id', $instance->courseid);
+
+        $mform->addElement('hidden', 'instance');
+        $mform->setType('instance', PARAM_INT);
+        $mform->setDefault('instance', $instance->id);
+    }
+
+    public function validation($data, $files) {
+        global $DB, $CFG;
+
+        $errors = parent::validation($data, $files);
+        $instance = $this->instance;
+
+        if ($instance->password) {
+            if ($data['guestpassword'] !== $instance->password) {
+                $plugin = enrol_get_plugin('guest');
+                if ($plugin->get_config('showhint')) {
+                    $textlib = textlib_get_instance();
+                    $hint = $textlib->substr($instance->password, 0, 1);
+                    $errors['guestpassword'] = get_string('passwordinvalidhint', 'enrol_guest', $hint);
+                } else {
+                    $errors['guestpassword'] = get_string('passwordinvalid', 'enrol_guest');
+                }
+            }
+        }
+
+        return $errors;
+    }
+}
\ No newline at end of file
diff --git a/enrol/guest/settings.php b/enrol/guest/settings.php
new file mode 100644
index 000000000000..a161c5787535
--- /dev/null
+++ b/enrol/guest/settings.php
@@ -0,0 +1,56 @@
+<?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/>.
+
+/**
+ * Guest access plugin settings and presets.
+ *
+ * @package   enrol_guest
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_guest_settings', '', get_string('pluginname_desc', 'enrol_guest')));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_guest/requirepassword',
+        get_string('requirepassword', 'enrol_guest'), get_string('requirepassword_desc', 'enrol_guest'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_guest/usepasswordpolicy',
+        get_string('usepasswordpolicy', 'enrol_guest'), get_string('usepasswordpolicy_desc', 'enrol_guest'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_guest/showhint',
+        get_string('showhint', 'enrol_guest'), get_string('showhint_desc', 'enrol_guest'), 0));
+
+
+    //--- enrol instance defaults ----------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_guest_defaults',
+        get_string('enrolinstancedefaults', 'admin'), get_string('enrolinstancedefaults_desc', 'admin')));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_guest/defaultenrol',
+        get_string('defaultenrol', 'enrol'), get_string('defaultenrol_desc', 'enrol'), 1));
+
+    $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                     ENROL_INSTANCE_DISABLED => get_string('no'));
+    $settings->add(new admin_setting_configselect_with_advanced('enrol_guest/status',
+        get_string('status', 'enrol_guest'), get_string('status_desc', 'enrol_guest'),
+        array('value'=>ENROL_INSTANCE_DISABLED, 'adv'=>false), $options));
+}
+
diff --git a/enrol/guest/version.php b/enrol/guest/version.php
new file mode 100644
index 000000000000..4c5241d77d48
--- /dev/null
+++ b/enrol/guest/version.php
@@ -0,0 +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/>.
+
+/**
+ * Guest access plugin version specification.
+ *
+ * @package   enrol_guest
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$plugin->version = 2010061500;
diff --git a/enrol/imsenterprise/enrol.php b/enrol/imsenterprise/enrol.php
index 316c77444b66..1575f28905ff 100644
--- a/enrol/imsenterprise/enrol.php
+++ b/enrol/imsenterprise/enrol.php
@@ -761,7 +761,8 @@ function process_membership_tag($tagcontents){
 
                     // Enrol unsing the generic role_assign() function
 
-                    if ((!role_assign($moodleroleid, $memberstoreobj->userid, 0, $rolecontext, $timeframe->begin, $timeframe->end, 0, 'imsenterprise')) && (trim($memberstoreobj->userid)!='')) {
+                    //TODO: some real enrolment here
+                    if ((!role_assign($moodleroleid, $memberstoreobj->userid, $rolecontext, 'enrol_imsenterprise')) && (trim($memberstoreobj->userid)!='')) {
                         $this->log_line("Error enrolling user #$memberstoreobj->userid ($member->idnumber) to role $member->roletype in course $memberstoreobj->course");
                     }else{
                         $this->log_line("Enrolled user #$memberstoreobj->userid ($member->idnumber) to role $member->roletype in course $memberstoreobj->course");
@@ -798,12 +799,9 @@ function process_membership_tag($tagcontents){
                 }elseif($CFG->enrol_imsunenrol){
                     // Unenrol
 
-                    if (! role_unassign($moodleroleid, $memberstoreobj->userid, 0, $rolecontext, 'imsenterprise')) {
-                        $this->log_line("Error unenrolling $memberstoreobj->userid from role $moodleroleid in course");
-                    }else{
-                        $membersuntally++;
-                        $this->log_line("Unenrolled $member->idnumber from role $moodleroleid in course");
-                    }
+                    role_unassign($moodleroleid, $memberstoreobj->userid, 0, $rolecontext, 'imsenterprise');
+                    $membersuntally++;
+                    $this->log_line("Unenrolled $member->idnumber from role $moodleroleid in course");
                 }
 
             }
diff --git a/enrol/index.html b/enrol/index.html
deleted file mode 100644
index 8b137891791f..000000000000
--- a/enrol/index.html
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/enrol/index.php b/enrol/index.php
new file mode 100644
index 000000000000..d8200cd2e77b
--- /dev/null
+++ b/enrol/index.php
@@ -0,0 +1,99 @@
+<?php
+
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * This page shows all course enrolment options for current user.
+ *
+ * @package    core
+ * @subpackage course
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../config.php');
+require_once("$CFG->libdir/formslib.php");
+
+$id = required_param('id', PARAM_INT);
+
+if (!isloggedin()) {
+    // do not use require_login here because we are usually coming from it,
+    // it would also mess up the SESSION->wantsurl
+    redirect(get_login_url());
+}
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+// Everybody is enrolled on the frontpage
+if ($course->id == SITEID) {
+    redirect("$CFG->wwwroot/");
+}
+
+$PAGE->set_url('/enrol/index.php', array('id'=>$course->id));
+
+// do not allow enrols when in login-as session
+if (session_is_loggedinas() and $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
+    print_error('loginasnoenrol', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid);
+}
+
+// get all enrol forms available in this course
+$enrols = enrol_get_plugins(true);
+$enrolinstances = enrol_get_instances($course->id, true);
+$forms = array();
+foreach($enrolinstances as $instance) {
+    if (!isset($enrols[$instance->enrol])) {
+        continue;
+    }
+    $form = $enrols[$instance->enrol]->enrol_page_hook($instance);
+    if ($form) {
+        $forms[$instance->id] = $form;
+    }
+}
+
+// Check if user already enrolled
+if (is_enrolled($context, $USER, '', true)) {
+    if (!empty($SESSION->wantsurl)) {
+        $destination = $SESSION->wantsurl;
+        unset($SESSION->wantsurl);
+    } else {
+        $destination = "$CFG->wwwroot/course/view.php?id=$course->id";
+    }
+    redirect($destination);   // Bye!
+}
+
+$PAGE->set_title($course->shortname);
+$PAGE->set_heading($course->fullname);
+$PAGE->navbar->add($course->fullname);
+
+
+echo $OUTPUT->header();
+
+//TODO: find if future enrolments present and display some info
+
+foreach ($forms as $form) {
+    echo $form;
+}
+
+if (!$forms) {
+    if (isguestuser()) {
+        notice(get_string('noguestaccess', 'enrol'), get_login_url());
+    } else {
+        notice(get_string('notenrollable', 'enrol'), "$CFG->wwwroot/index.php");
+    }
+}
+
+echo $OUTPUT->footer();
diff --git a/enrol/instances.php b/enrol/instances.php
new file mode 100644
index 000000000000..078442339350
--- /dev/null
+++ b/enrol/instances.php
@@ -0,0 +1,237 @@
+<?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/>.
+
+/**
+ * Main course enrolment management UI.
+ *
+ * @package    core
+ * @subpackage enrol
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../config.php');
+
+$id         = required_param('id', PARAM_INT); // course id
+$action     = optional_param('action', '', PARAM_ACTION);
+$instanceid = optional_param('instance', 0, PARAM_INT);
+$confirm    = optional_param('confirm', 0, PARAM_BOOL);
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+if ($course->id == SITEID) {
+    redirect("$CFG->wwwroot/");
+}
+
+require_login($course);
+require_capability('moodle/course:enrolreview', $context);
+
+$canconfig = has_capability('moodle/course:enrolconfig', $context);
+
+$PAGE->set_url('/enrol/instances.php', array('id'=>$course->id));
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title(get_string('enrolmentinstances', 'enrol'));
+
+$instances = enrol_get_instances($course->id, false);
+$plugins   = enrol_get_plugins(false);
+
+if ($canconfig and $action and confirm_sesskey()) {
+    if (isset($instances[$instanceid]) and isset($plugins[$instances[$instanceid]->enrol])) {
+        if ($action === 'up') {
+            $order = array_keys($instances);
+            $order = array_flip($order);
+            $pos = $order[$instanceid];
+            if ($pos > 0) {
+                $switch = $pos - 1;
+                $resorted = array_values($instances);
+                $temp = $resorted[$pos];
+                $resorted[$pos] = $resorted[$switch];
+                $resorted[$switch] = $temp;
+                // now update db sortorder
+                foreach ($resorted as $sortorder=>$instance) {
+                    if ($instance->sortorder != $sortorder) {
+                        $instance->sortorder = $sortorder;
+                        $DB->update_record('enrol', $instance);
+                    }
+                }
+            }
+            redirect($PAGE->url);
+
+        } else if ($action === 'down') {
+            $order = array_keys($instances);
+            $order = array_flip($order);
+            $pos = $order[$instanceid];
+            if ($pos < count($instances) - 1) {
+                $switch = $pos + 1;
+                $resorted = array_values($instances);
+                $temp = $resorted[$pos];
+                $resorted[$pos] = $resorted[$switch];
+                $resorted[$switch] = $temp;
+                // now update db sortorder
+                foreach ($resorted as $sortorder=>$instance) {
+                    if ($instance->sortorder != $sortorder) {
+                        $instance->sortorder = $sortorder;
+                        $DB->update_record('enrol', $instance);
+                    }
+                }
+            }
+            redirect($PAGE->url);
+
+        } else if ($action === 'delete') {
+            $instance = $instances[$instanceid];
+            $plugin = $plugins[$instance->enrol];
+
+            if ($confirm) {
+                $plugin->delete_instance($instance);
+                redirect($PAGE->url);
+            }
+
+            echo $OUTPUT->header();
+            $yesurl = new moodle_url('/enrol/instances.php', array('id'=>$course->id, 'action'=>'delete', 'instance'=>$instance->id, 'confirm'=>1,'sesskey'=>sesskey()));
+            $displayname = $plugin->get_instance_name($instance);
+            $users = $DB->count_records('user_enrolments', array('enrolid'=>$instance->id));
+            $message = get_string('deleteinstanceconfirm', 'enrol', array('name'=>$displayname, 'users'=>$users));
+            echo $OUTPUT->confirm($message, $yesurl, $PAGE->url);
+            echo $OUTPUT->footer();
+            die();
+
+        } else if ($action === 'disable') {
+            $instance = $instances[$instanceid];
+            if ($instance->status == ENROL_INSTANCE_ENABLED) {
+                $instance->status = ENROL_INSTANCE_DISABLED;
+                $DB->update_record('enrol', $instance);
+                redirect($PAGE->url);
+            }
+
+        } else if ($action === 'enable') {
+            $instance = $instances[$instanceid];
+            if ($instance->status == ENROL_INSTANCE_DISABLED) {
+                $instance->status = ENROL_INSTANCE_ENABLED;
+                $DB->update_record('enrol', $instance);
+                redirect($PAGE->url);
+            }
+        }
+    }
+}
+
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('enrolmentinstances', 'enrol'));
+
+echo $OUTPUT->box_start('generalbox boxaligncenter boxwidthnormal');
+
+// display strings
+$strup      = get_string('up');
+$strdown    = get_string('down');
+$strdelete  = get_string('delete');
+$strenable  = get_string('enable');
+$strdisable = get_string('disable');
+$strmanage  = get_string('manageinstance', 'enrol');
+
+$table = new html_table();
+$table->head  = array(get_string('name'), get_string('users'), $strup.'/'.$strdown, get_string('edit'));
+$table->align = array('left', 'center', 'center', 'center');
+$table->width = '100%';
+$table->data  = array();
+
+// iterate through enrol plugins and add to the display table
+$updowncount = 1;
+$icount = count($instances);
+$url = new moodle_url('/enrol/instances.php', array('sesskey'=>sesskey(), 'id'=>$course->id));
+foreach ($instances as $instance) {
+    if (!isset($plugins[$instance->enrol])) {
+        continue;
+    }
+    $plugin = $plugins[$instance->enrol];
+
+    $displayname = $plugin->get_instance_name($instance);
+    if (!enrol_is_enabled($instance->enrol) or $instance->status != ENROL_INSTANCE_ENABLED) {
+        $displayname = html_writer::tag('span', $displayname, array('class'=>'dimmed_text'));
+    }
+
+    $users = $DB->count_records('user_enrolments', array('enrolid'=>$instance->id));
+
+    $updown = '';
+    $edit = '';
+
+    if ($canconfig) {
+        // up/down link
+        $updown = '';
+        if ($updowncount > 1) {
+            $aurl = new moodle_url($url, array('action'=>'up', 'instance'=>$instance->id));
+            $updown .= html_writer::link($aurl, html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/up'), 'alt'=>$strup, 'class'=>'smallicon'))).'&nbsp;';
+        } else {
+            $updown .= html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('spacer'), 'alt'=>'', 'class'=>'smallicon')).'&nbsp;';
+        }
+        if ($updowncount < $icount) {
+            $aurl = new moodle_url($url, array('action'=>'down', 'instance'=>$instance->id));
+            $updown .= html_writer::link($aurl, html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/down'), 'alt'=>$strdown, 'class'=>'smallicon'))).'&nbsp;';
+        } else {
+            $updown .= html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('spacer'), 'alt'=>'', 'class'=>'smallicon')).'&nbsp;';
+        }
+        ++$updowncount;
+
+        // edit links
+        if ($plugin->instance_deleteable($instance)) {
+            $aurl = new moodle_url($url, array('action'=>'delete', 'instance'=>$instance->id));
+            $edit .= html_writer::link($aurl, html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/delete'), 'alt'=>$strdelete, 'class'=>'smallicon'))).'&nbsp;';
+        }
+
+        if (enrol_is_enabled($instance->enrol)) {
+            if ($instance->status == ENROL_INSTANCE_ENABLED) {
+                $aurl = new moodle_url($url, array('action'=>'disable', 'instance'=>$instance->id));
+                $edit .= html_writer::link($aurl, html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/hide'), 'alt'=>$strdisable, 'class'=>'smallicon'))).'&nbsp;';
+            } else if ($instance->status == ENROL_INSTANCE_DISABLED) {
+                $aurl = new moodle_url($url, array('action'=>'enable', 'instance'=>$instance->id));
+                $edit .= html_writer::link($aurl, html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/show'), 'alt'=>$strenable, 'class'=>'smallicon'))).'&nbsp;';
+            } else {
+                // plugin specific state - do not mess with it!
+                $edit .= html_writer::empty_tag('img', array('src'=>$OUTPUT->pix_url('t/show'), 'alt'=>'', 'class'=>'smallicon')).'&nbsp;';
+            }
+
+        }
+    }
+
+    // link to instance management
+    if ($managelink = $plugin->get_manage_link($instance)) {
+        $displayname = html_writer::link($managelink, $displayname);
+    }
+
+    // add a row to the table
+    $table->data[] = array($displayname, $users, $updown, $edit);
+
+}
+echo html_writer::table($table);
+
+// access security is in each plugin
+$candidates = array();
+foreach (enrol_get_plugins(true) as $name=>$plugin) {
+    if (!$link = $plugin->get_candidate_link($course->id)) {
+        continue;
+    }
+    $candidates[$link->out(false)] = get_string('pluginname', 'enrol_'.$name);
+}
+
+if ($candidates) {
+    $select = new url_select($candidates);
+    $select->set_label(get_string('addinstance', 'enrol'));
+    echo $OUTPUT->render($select);
+}
+
+echo $OUTPUT->box_end();
+
+echo $OUTPUT->footer();
diff --git a/enrol/ldap/enrol.php b/enrol/ldap/enrol.php
index 9f1827c39c09..a909f4492948 100755
--- a/enrol/ldap/enrol.php
+++ b/enrol/ldap/enrol.php
@@ -1,7 +1,5 @@
 <?php
 
-require_once("$CFG->dirroot/enrol/enrol.class.php");
-
 class enrolment_plugin_ldap {
 
     var $log;
@@ -81,7 +79,8 @@ function setup_enrolments(&$user) {
 
                 if (!$DB->get_record('role_assignments', array('roleid'=>$role->id, 'userid'=>$user->id, 'contextid'=>$context->id))) {
                     //error_log("[ENROL_LDAP] Assigning role '{$role->name}' to {$user->id} ({$user->username}) in course {$course_obj->id} ({$course_obj->shortname})");
-                    if (!role_assign($role->id, $user->id, 0, $context->id, 0, 0, 0, 'ldap')){
+                    //TODO: some real enrolment here
+                    if (!role_assign($role->id, $user->id, $context->id, 'enrol_ldap')){
                         error_log("[ENROL_LDAP] Failed to assign role '{$role->name}' to $user->id ($user->username) into course $course_obj->id ($course_obj->shortname)");
                     }
                 } else {
@@ -106,7 +105,7 @@ function setup_enrolments(&$user) {
     foreach ($ldap_assignments as $ra) {
         if($ra->enrol === 'ldap') {
             error_log("Unassigning role_assignment with id '{$ra->id}' from user {$user->id} ({$user->username})");
-            role_unassign($ra->roleid, $user->id, 0, $ra->contextid, 'ldap');
+            role_unassign($ra->roleid, $user->id, $ra->contextid, 'enrol_ldap');
         }
     }
 
@@ -265,11 +264,8 @@ function sync_enrolments($type, $enrol = false) {
                         foreach ($todelete as $member) {
                             $member = $member->user;
 
-                            if (role_unassign($role->id, $member, 0, $context->id, 'ldap')) {
-                                print "Unassigned $type from $member for course $course_obj->id ($course_obj->shortname)\n";
-                            } else {
-                                print "Failed to unassign $type from $member for course $course_obj->id ($course_obj->shortname)\n";
-                            }
+                            role_unassign($role->id, $member, $context->id, 'enrol_ldap');
+                            print "Unassigned $type from $member for course $course_obj->id ($course_obj->shortname)\n";
                         }
                     }
 
@@ -287,7 +283,7 @@ function sync_enrolments($type, $enrol = false) {
                         $member = $member->id;
                         if (!$DB->get_record('role_assignments', array('roleid'=>$role->id,
                                              'contextid'=>$context->id, 'userid'=>$member, 'enrol'=>'ldap'))){
-                            if (role_assign($role->id, $member, 0, $context->id, 0, 0, 0, 'ldap')){
+                            if (role_assign($role->id, $member, $context->id, 'enrol_ldap')){
                                 print "Assigned role $type to $member ($ldapmember) for course $course_obj->id ($course_obj->shortname)\n";
                             } else {
                                 print "Failed to assign role $type to $member ($ldapmember) for course $course_obj->id ($course_obj->shortname)\n";
diff --git a/enrol/ldap/enrol_ldap_sync.php b/enrol/ldap/enrol_ldap_sync.php
index c49995f329ba..c94a86d46a16 100755
--- a/enrol/ldap/enrol_ldap_sync.php
+++ b/enrol/ldap/enrol_ldap_sync.php
@@ -27,9 +27,4 @@
         $enrol->sync_enrolments($role->shortname, true);
     }
 
-    // sync metacourses
-    if (function_exists('sync_metacourses')) {
-        sync_metacourses();
-    }
-
 
diff --git a/enrol/manual/addinstance.php b/enrol/manual/addinstance.php
new file mode 100644
index 000000000000..0f89a5f84f5c
--- /dev/null
+++ b/enrol/manual/addinstance.php
@@ -0,0 +1,43 @@
+<?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/>.
+
+/**
+ * Adds new instance of enrol_manual to specified course.
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+
+$id = required_param('id', PARAM_INT); // course id
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('moodle/course:enrolconfig', $context);
+require_sesskey();
+
+$enrol = enrol_get_plugin('manual');
+
+if ($enrol->get_candidate_link($course->id)) {
+    $enrol->add_default_instance($course);
+}
+
+redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
diff --git a/enrol/manual/config.html b/enrol/manual/config.html
deleted file mode 100644
index 01b3a115b1c7..000000000000
--- a/enrol/manual/config.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<table cellspacing="0" cellpadding="5" border="0" class="boxaligncenter">
-
-<tr valign="top">
-    <td align="right">enrol_manual_keyholderrole:</td>
-    <td>
-    <?php
-        $roles = get_all_roles();
-        $rolenames = array();
-        foreach ($roles as $id=>$role) {
-            $rolenames[$id]=$role->name;
-        }
-        echo html_writer::select($rolenames, 'enrol_manual_keyholderrole', $frm->enrol_manual_keyholderrole);
-    ?>
-    </td>
-    <td>
-    <?php  print_string("keyholderrole", "enrol_manual") ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_manual_showhint:</td>
-    <td>
-<?php
-    $choices = array();
-    $choices['0'] = get_string('no');
-    $choices['1'] = get_string('yes');
-    echo html_writer::select($choices, 'enrol_manual_showhint', $frm->enrol_manual_showhint, false);
-?>
-    </td><td>
-    <?php  print_string('enrol_manual_showhint', 'enrol_manual') ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_manual_usepasswordpolicy:</td>
-    <td>
-<?php
-    $choices = array();
-    $choices['0'] = get_string('no');
-    $choices['1'] = get_string('yes');
-    echo html_writer::select($choices, 'enrol_manual_usepasswordpolicy', $frm->enrol_manual_usepasswordpolicy, false);
-?>
-    </td><td>
-    <?php  print_string('enrol_manual_usepasswordpolicy', 'enrol_manual') ?>
-    </td>
-</tr>
-
-<tr>
-    <td align="right">enrol_manual_requirekey:</td>
-    <td>
-<?php
-    $choices = array();
-    $choices['0'] = get_string('no');
-    $choices['1'] = get_string('yes');
-    echo html_writer::select($choices, 'enrol_manual_requirekey', $frm->enrol_manual_requirekey, false);
-?>
-    </td><td>
-    <?php  print_string('enrol_manual_requirekey', 'enrol_manual') ?>
-    </td>
-</tr>
-
-
-</table>
diff --git a/enrol/manual/db/access.php b/enrol/manual/db/access.php
new file mode 100644
index 000000000000..5c0194bf7e12
--- /dev/null
+++ b/enrol/manual/db/access.php
@@ -0,0 +1,64 @@
+<?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/>.
+
+/**
+ * Capabilities for manual enrolment plugin.
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+$capabilities = array(
+
+    'enrol/manual:config' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+    'enrol/manual:manage' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+        )
+    ),
+
+
+    'enrol/manual:unenrol' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+            'editingteacher' => CAP_ALLOW,
+        )
+    ),
+
+    'enrol/manual:unenrolself' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+        )
+    ),
+
+);
+
diff --git a/enrol/manual/db/install.php b/enrol/manual/db/install.php
new file mode 100644
index 000000000000..eb6c9e2613be
--- /dev/null
+++ b/enrol/manual/db/install.php
@@ -0,0 +1,32 @@
+<?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/>.
+
+/**
+ * Manual enrol plugin installation script
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+function xmldb_enrol_manual_install() {
+    global $CFG;
+
+    // migrate settings during 2.0 upgrade
+
+}
+
diff --git a/enrol/manual/enrol.html b/enrol/manual/enrol.html
deleted file mode 100644
index b865562d9b9b..000000000000
--- a/enrol/manual/enrol.html
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-    if ($course->password != '' and !(isguestuser() and !empty($USER->enrolkey[$course->id]))) {   // password
-        echo $OUTPUT->box_start('generalbox centerpara');
-        echo '<p align="center">';
-
-        $this->print_enrolmentkeyfrom( $course );
- ?>
-      </p>
-
-      <p align="center"><?php if (! empty($this->errormsg)) {echo $OUTPUT->error_text($this->errormsg);} ?></p>
-
-
-      <form method="post" action="enrol.php">
-        <table align="center" width="100%">
-          <tr>
-            <td align="right"><?php print_string("enrolmentkey") ?>:</td>
-            <td>
-              <input type="password" name="password" size="20" value="<?php p($password) ?>"  />
-              <input type="hidden" name="id" value="<?php p($course->id) ?>"  />
-              <input type="hidden" name="enrol" value="manual" />
-              <input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
-              <input type="submit" value="<?php print_string("enrolme") ?>" />
-            </td>
-          </tr>
-        </table>
-      </form>
-      <br />
-
-      <div align="center">
-      <form action="<?php p($CFG->wwwroot)?>/index.php" method="post">
-      <input type="submit" value="<?php print_string("cancel") ?>" />
-      </form>
-      </div>
-
-<?php
-        echo $OUTPUT->box_end();
-    }
-
-
-    if (isguestuser()) {
-        echo $OUTPUT->box_start('centerpara');
-        $loginurl = get_login_url();
-        echo $OUTPUT->single_button($loginurl, get_string('login'));
-        echo $OUTPUT->box_end();
-    }
-?>
diff --git a/enrol/manual/enrol.php b/enrol/manual/enrol.php
deleted file mode 100644
index eeff29346ce2..000000000000
--- a/enrol/manual/enrol.php
+++ /dev/null
@@ -1,455 +0,0 @@
-<?php
-///////////////////////////////////////////////////////////////////////////
-//                                                                       //
-// NOTICE OF COPYRIGHT                                                   //
-//                                                                       //
-// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
-//          http://moodle.org                                            //
-//                                                                       //
-// Copyright (C) 2004  Martin Dougiamas  http://moodle.com               //
-//                                                                       //
-// This program 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 2 of the License, or     //
-// (at your option) any later version.                                   //
-//                                                                       //
-// This program 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:                          //
-//                                                                       //
-//          http://www.gnu.org/copyleft/gpl.html                         //
-//                                                                       //
-///////////////////////////////////////////////////////////////////////////
-
-require_once($CFG->dirroot.'/group/lib.php');
-require_once($CFG->libdir.'/eventslib.php');
-
-/**
-* enrolment_plugin_manual is the default enrolment plugin
-*
-* This class provides all the functionality for an enrolment plugin
-* In fact it includes all the code for the default, "manual" method
-* so that other plugins can override these as necessary.
-*/
-
-class enrolment_plugin_manual {
-
-var $errormsg;
-
-/**
-* Prints the entry form/page for this enrolment
-*
-* This is only called from course/enrol.php
-* Most plugins will probably override this to print payment
-* forms etc, or even just a notice to say that manual enrolment
-* is disabled
-*
-* @param    course  current course object
-*/
-function print_entry($course) {
-    global $CFG, $USER, $SESSION, $OUTPUT, $PAGE;
-
-    $strloginto = get_string('loginto', '', $course->shortname);
-    $strcourses = get_string('courses');
-
-/// Automatically enrol into courses without password
-
-    $context = get_context_instance(CONTEXT_SYSTEM);
-
-    $PAGE->navbar->add($strcourses);
-    $PAGE->navbar->add($strloginto);
-
-    if ($course->password == '') {   // no password, so enrol
-
-        if (isguestuser()) {
-            add_to_log($course->id, 'course', 'guest', 'view.php?id='.$course->id, getremoteaddr());
-
-        } else if (empty($_GET['confirm']) && empty($_GET['cancel'])) {
-            $PAGE->set_title($strloginto);
-            $PAGE->set_heading($course->fullname);
-            echo $OUTPUT->header();
-            echo '<br />';
-            echo $OUTPUT->confirm(get_string('enrolmentconfirmation'), "enrol.php?id=$course->id&confirm=1&amp;sesskey=".sesskey(), "enrol.php?id=$course->id&cancel=1");
-            echo $OUTPUT->footer();
-            exit;
-
-        } else if (!empty($_GET['confirm']) and confirm_sesskey()) {
-
-            if (!enrol_into_course($course, $USER, 'manual')) {
-                print_error('couldnotassignrole');
-            }
-            // force a refresh of mycourses
-            unset($USER->mycourses);
-
-            if (!empty($SESSION->wantsurl)) {
-                $destination = $SESSION->wantsurl;
-                unset($SESSION->wantsurl);
-            } else {
-                $destination = "$CFG->wwwroot/course/view.php?id=$course->id";
-            }
-
-            redirect($destination);
-
-        } else if (!empty($_GET['cancel'])) {
-            unset($SESSION->wantsurl);
-            if (!empty($SESSION->enrolcancel)) {
-                $destination = $SESSION->enrolcancel;
-                unset($SESSION->enrolcancel);
-            } else {
-                $destination = $CFG->wwwroot;
-            }
-            redirect($destination);
-        }
-    }
-
-    // if we get here we are going to display the form asking for the enrolment key
-    // and (hopefully) provide information about who to ask for it.
-    if (!isset($password)) {
-        $password = '';
-    }
-
-    $PAGE->set_title($strloginto);
-    $PAGE->set_heading($course->fullname);
-    $PAGE->set_focuscontrol('form.password');
-    echo $OUTPUT->header();
-
-    print_course($course, "80%");
-
-    include("$CFG->dirroot/enrol/manual/enrol.html");
-
-    echo $OUTPUT->footer();
-
-}
-
-
-
-/**
-* The other half to print_entry, this checks the form data
-*
-* This function checks that the user has completed the task on the
-* enrolment entry page and then enrolls them.
-*
-* @param    form    the form data submitted, as an object
-* @param    course  the current course, as an object
-*/
-function check_entry($form, $course) {
-    global $CFG, $USER, $SESSION;
-
-    if (empty($form->password)) {
-        $form->password = '';
-    }
-
-    if (empty($course->password) or !confirm_sesskey()) {
-        // do not allow entry when no course password set
-        // automatic login when manual primary, no login when secondary at all!!
-        print_error('invalidenrol');
-    }
-
-    $groupid = $this->check_group_entry($course->id, $form->password);
-
-    if (($form->password == $course->password) or ($groupid !== false) ) {
-
-        if (isguestuser()) { // only real user guest, do not use this for users with guest role
-            $USER->enrolkey[$course->id] = true;
-            add_to_log($course->id, 'course', 'guest', 'view.php?id='.$course->id, getremoteaddr());
-
-        } else {  /// Update or add new enrolment
-            if (enrol_into_course($course, $USER, 'manual')) {
-                // force a refresh of mycourses
-                unset($USER->mycourses);
-                if ($groupid !== false) {
-                    if (!groups_add_member($groupid, $USER->id)) {
-                        print_error('couldnotassigngroup');
-                    }
-                }
-            } else {
-                print_error('couldnotassignrole');
-            }
-        }
-
-        if ($SESSION->wantsurl) {
-            $destination = $SESSION->wantsurl;
-            unset($SESSION->wantsurl);
-        } else {
-            $destination = "$CFG->wwwroot/course/view.php?id=$course->id";
-        }
-
-        redirect($destination);
-
-    } else if (!isset($CFG->enrol_manual_showhint) or $CFG->enrol_manual_showhint) {
-        $this->errormsg = get_string('enrolmentkeyhint', '', substr($course->password, 0, 1));
-
-    } else {
-        $this->errormsg = get_string('enrolmentkeyerror', 'enrol_manual');
-    }
-}
-
-
-/**
-* Check if the given enrolment key matches a group enrolment key for the given course
-*
-* @param    courseid  the current course id
-* @param    password  the submitted enrolment key
-*/
-function check_group_entry ($courseid, $password) {
-
-    if ($groups = groups_get_all_groups($courseid)) {
-        foreach ($groups as $group) {
-            if ( !empty($group->enrolmentkey) and ($password == $group->enrolmentkey) ) {
-                return $group->id;
-            }
-        }
-    }
-
-    return false;
-}
-
-
-/**
-* Prints a form for configuring the current enrolment plugin
-*
-* This function is called from admin/enrol.php, and outputs a
-* full page with a form for defining the current enrolment plugin.
-*
-* @param    frm  an object containing all the data for this page
-*/
-function config_form($frm) {
-    global $CFG, $OUTPUT;
-
-    if (!isset( $frm->enrol_manual_keyholderrole )) {
-        $frm->enrol_manual_keyholderrole = '';
-    }
-
-    if (!isset($frm->enrol_manual_showhint)) {
-        $frm->enrol_manual_showhint = 1;
-    }
-
-    if (!isset($frm->enrol_manual_usepasswordpolicy)) {
-        $frm->enrol_manual_usepasswordpolicy = 0;
-    }
-
-    if (!isset($frm->enrol_manual_requirekey)) {
-        $frm->enrol_manual_requirekey = 0;
-    }
-
-    include ("$CFG->dirroot/enrol/manual/config.html");
-}
-
-
-/**
-* Processes and stored configuration data for the enrolment plugin
-*
-* @param    config  all the configuration data as entered by the admin
-*/
-function process_config($config) {
-    foreach ($config as $name => $value) {
-        set_config($name, $value);
-    }
-
-    return true;
-}
-
-
-/**
-* Notify users about enrolments that are going to expire soon!
-* This function is run by admin/cron.php
-* @return void
-*/
-function cron() {
-    global $CFG, $USER, $SITE, $DB;
-
-    if (!isset($CFG->lastexpirynotify)) {
-        set_config('lastexpirynotify', 0);
-    }
-
-    // notify once a day only - TODO: add some tz handling here, maybe use timestamps
-    if ($CFG->lastexpirynotify == date('Ymd')) {
-        return;
-    }
-
-    if ($rs = $DB->get_recordset_select('course', 'enrolperiod > 0 AND expirynotify > 0 AND expirythreshold > 0')) {
-        $admin = get_admin();
-
-        foreach ($rs as $course) {
-            $a = new object();
-            $a->coursename = $course->shortname .'/'. $course->fullname; // must be processed by format_string later
-            $a->threshold  = $course->expirythreshold / 86400;
-            $a->extendurl  = $CFG->wwwroot . '/user/index.php?id=' . $course->id;
-            $a->current    = array();
-            $a->past       = array();
-
-            $expiry = time() + $course->expirythreshold;
-            $cname  = $course->fullname;
-
-            /// Get all the manual role assignments for this course that have expired.
-
-            if (!$context = get_context_instance(CONTEXT_COURSE, $course->id)) {
-                continue;
-            }
-
-            if ($oldenrolments = $DB->get_records_sql("
-                      SELECT u.*, ra.timeend
-                        FROM {user} u
-                             JOIN {role_assignments} ra ON (ra.userid = u.id)
-                        WHERE ra.contextid = $context->id
-                              AND ra.timeend > 0 AND ra.timeend <= $expiry
-                              AND ra.enrol = 'manual'")) {
-
-                // inform user who can assign roles or admin
-                if ($teachers = get_users_by_capability($context, 'moodle/role:assign', '', '', '', '', '', '', false)) {
-                    $teachers = sort_by_roleassignment_authority($teachers, $context);
-                    $teacher  = reset($teachers);
-                } else {
-                    $teachers = array($admin);
-                    $teacher  = $admin;
-                }
-
-                $a->teacherstr = fullname($teacher, true);
-
-                foreach ($oldenrolments as $user) {       /// Email all users about to expire
-                    $a->studentstr = fullname($user, true);
-                    if ($user->timeend < ($expiry - 86400)) {
-                        $a->past[] = fullname($user) . " <$user->email>";
-                    } else {
-                        $a->current[] = fullname($user) . " <$user->email>";
-                        if ($course->notifystudents) {     // Send this guy notice
-                            // setup global $COURSE properly - needed for languages
-                            cron_setup_user($user, $course);
-                            $a->coursename = format_string($cname);
-                            $a->course     = $a->coursename;
-                            $strexpirynotifystudentsemail = get_string('expirynotifystudentsemail', '', $a);
-                            $strexpirynotify              = get_string('expirynotify');
-
-                            $eventdata = new object();
-                            $eventdata->modulename        = 'moodle';
-                            $eventdata->userfrom          = $teacher;
-                            $eventdata->userto            = $user;
-                            $eventdata->subject           = format_string($SITE->fullname) .' '. $strexpirynotify;
-                            $eventdata->fullmessage       = $strexpirynotifystudentsemail;
-                            $eventdata->fullmessageformat = FORMAT_PLAIN;
-                            $eventdata->fullmessagehtml   = '';
-                            $eventdata->smallmessage      = '';
-                            message_send($eventdata);
-                        }
-                    }
-                }
-
-                $a->current = implode("\n", $a->current);
-                $a->past    = implode("\n", $a->past);
-
-                if ($a->current || $a->past) {
-                    foreach ($teachers as $teacher) {
-                        // setup global $COURSE properly - needed for languages
-                        cron_setup_user($teacher, $course);
-                        $a->coursename = format_string($cname);
-                        $strexpirynotifyemail = get_string('expirynotifyemail', '', $a);
-                        $strexpirynotify      = get_string('expirynotify');
-
-                        $eventdata = new object();
-                        $eventdata->modulename        = 'moodle';
-                        $eventdata->userfrom          = $admin;
-                        $eventdata->userto            = $teacher;
-                        $eventdata->subject           = $a->coursename .' '. $strexpirynotify;
-                        $eventdata->fullmessage       = $strexpirynotifyemail;
-                        $eventdata->fullmessageformat = FORMAT_PLAIN;
-                        $eventdata->fullmessagehtml   = '';
-                        $eventdata->smallmessage      = '';
-                        message_send($eventdata);
-                    }
-                }
-            }
-        }
-        $rs->close();
-        cron_setup_user();
-    }
-
-    set_config('lastexpirynotify', date('Ymd'));
-}
-
-
-/**
-* Returns the relevant icons for a course
-*
-* @param    course  the current course, as an object
-*/
-function get_access_icons($course) {
-    global $CFG, $OUTPUT;
-
-    global $strallowguests;
-    global $strrequireskey;
-
-    if (empty($strallowguests)) {
-        $strallowguests = get_string('allowguests');
-        $strrequireskey = get_string('requireskey');
-    }
-
-    $str = '';
-
-    if (!empty($course->guest)) {
-        $str .= '<a title="'.$strallowguests.'" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">';
-        $str .= '<img class="accessicon" alt="'.$strallowguests.'" src="'.$OUTPUT->pix_url('i/guest') . '" /></a>&nbsp;&nbsp;';
-    }
-    if (!empty($course->password)) {
-        $str .= '<a title="'.$strrequireskey.'" href="'.$CFG->wwwroot.'/course/view.php?id='.$course->id.'">';
-        $str .= '<img class="accessicon" alt="'.$strrequireskey.'" src="'.$OUTPUT->pix_url('i/key') . '" /></a>';
-    }
-
-    return $str;
-}
-
-/**
- * Prints the message telling you were to get the enrolment key
- * appropriate for the prevailing circumstances
- * A bit clunky because I didn't want to change the standard strings
- */
-function print_enrolmentkeyfrom($course) {
-    global $CFG, $USER;
-
-    $context = get_context_instance(CONTEXT_SYSTEM);
-
-    // if a keyholder role is defined we list teachers in that role (if any exist)
-    $contactslisted = false;
-    if (!empty($CFG->enrol_manual_keyholderrole)) {
-        if ($contacts = get_role_users($CFG->enrol_manual_keyholderrole, get_context_instance(CONTEXT_COURSE, $course->id),true,'','u.lastname ASC')) {
-            // guest user has a slightly different message
-            if (isguestuser()) {
-                print_string('enrolmentkeyfromguest', '', ':<br />' );
-            }
-            else {
-                print_string('enrolmentkeyfrom', '', ':<br />');
-            }
-            foreach ($contacts as $contact) {
-                $contactname = "<a href=\"../user/view.php?id=$contact->id&course=".SITEID."\">".fullname($contact)."</a>.";
-                echo "$contactname<br />";
-            }
-            $contactslisted = true;
-        }
-    }
-
-    // if no keyholder role is defined OR nobody is in that role we do this the 'old' way
-    // (show the first person with update rights)
-    if (!$contactslisted) {
-        if ($teachers = get_users_by_capability(get_context_instance(CONTEXT_COURSE, $course->id), 'moodle/course:update',
-            'u.*', 'u.id ASC', 0, 1, '', '', false, true)) {
-            $teacher = array_shift($teachers);
-        }
-        if (!empty($teacher)) {
-            $teachername = "<a href=\"../user/view.php?id=$teacher->id&course=".SITEID."\">".fullname($teacher)."</a>.";
-        } else {
-            $teachername = strtolower( get_string('defaultcourseteacher') );
-        }
-
-        // guest user has a slightly different message
-        if (isguestuser()) {
-            print_string('enrolmentkeyfromguest', '', $teachername );
-        }
-        else {
-            print_string('enrolmentkeyfrom', '', $teachername);
-        }
-    }
-}
-
-} /// end of class
-
-
diff --git a/enrol/manual/lang/en/enrol_manual.php b/enrol/manual/lang/en/enrol_manual.php
index 439561b13891..52fd1d118ef2 100644
--- a/enrol/manual/lang/en/enrol_manual.php
+++ b/enrol/manual/lang/en/enrol_manual.php
@@ -23,16 +23,19 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-$string['description'] = 'This is the default form of enrolment. There are two main ways a student can be enrolled in a particular course.
-<ul>
-<li>A teacher or admin can enrol them manually using the link in the Course Administration menu 
-    within the course.</li>
-<li>A course can have a password defined, known as an "enrolment key".  Anyone who knows this key is 
-    able to add themselves to a course.</li>
-</ul>';
-$string['enrol_manual_requirekey'] = 'Require course enrolment keys in new courses and prevent removing of existing keys.';
-$string['enrol_manual_showhint'] = 'Enable this setting to reveal the first character of the enrolment key as a hint if one enters an incorrect key.';
-$string['enrol_manual_usepasswordpolicy'] = 'Use current user password policy for course enrolment keys.';
-$string['enrolmentkeyerror'] = 'That enrolment key was incorrect, please try again.';
-$string['enrolname'] = 'Internal Enrolment';
-$string['keyholderrole'] = 'The role of the user that holds the enrolment key for a course. Displayed to students attempting to enrol on the course.';
+$string['assignrole'] = 'Assignrole';
+$string['defaultperiod'] = 'Default enrolment period';
+$string['defaultperiod_desc'] = 'Default length of the default enrolment period setting (in seconds).'; //TODO: fixme
+$string['enrolledincourserole'] = 'Enrolled in "{$a->course}" as "{$a->role}"';
+$string['enrolusers'] = 'Enrol users';
+$string['manual:config'] = 'Configure manual enrol instances';
+$string['manual:manage'] = 'Manage enrolled users';
+$string['manual:unenrol'] = 'Unenrol users from the course';
+$string['manual:unenrolself'] = 'Unenrol self from the course';
+$string['pluginname'] = 'Manual enrolments';
+$string['pluginname_desc'] = 'Internal enrolments is a basic enrolment plugin which allows simple user enrolments. It should be kept enabled in most cases. Some other plugins such as self enrolment may use this pugin internally.';
+$string['status'] = 'Enable manual enrolments';
+$string['status_desc'] = 'Allow course access of internally enrolled users. This should be kept enabled in most cases.';
+$string['statusenabled'] = 'Enabled';
+$string['statusdisabled'] = 'Disabled';
+$string['unenrolselfconfirm'] = 'Do you really want to unenrol yourself from course "{$a}"?';
\ No newline at end of file
diff --git a/enrol/manual/lib.php b/enrol/manual/lib.php
new file mode 100644
index 000000000000..57c7d57229e7
--- /dev/null
+++ b/enrol/manual/lib.php
@@ -0,0 +1,222 @@
+<?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/>.
+
+/**
+ * Manual enrolment plugin main library file.
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+class enrol_manual_plugin extends enrol_plugin {
+
+    public function roles_protected() {
+        // users may tweak the roles later
+        return false;
+    }
+
+    public function allow_unenrol(stdClass $instance) {
+        // users with unenrol cap may unenrol other users manually manually
+        return true;
+    }
+
+    public function allow_manage(stdClass $instance) {
+        // users with manage cap may tweak period and status
+        return true;
+    }
+
+    /**
+     * Returns link to manual enrol UI if exists.
+     * Does the access control tests automatically.
+     *
+     * @param object $instance
+     * @return moodle_url
+     */
+    public function get_manual_enrol_link($instance) {
+        $name = $this->get_name();
+        if ($instance->enrol !== $name) {
+            throw new coding_exception('invalid enrol instance!');
+        }
+
+        if ($instance->courseid == SITEID) {
+            return NULL;
+        }
+
+        if (!enrol_is_enabled($name)) {
+            return NULL;
+        }
+
+        $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
+
+        if (!has_capability('enrol/manual:manage', $context)) {
+            return NULL;
+        }
+
+        return new moodle_url('/enrol/manual/manage.php', array('enrolid'=>$instance->id, 'id'=>$instance->courseid));
+    }
+
+    /**
+     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * @param int $courseid
+     * @return moodle_url page url
+     */
+    public function get_candidate_link($courseid) {
+        global $DB;
+
+        if (!has_capability('moodle/course:enrolconfig', get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST))) {
+            return NULL;
+        }
+
+        if ($DB->record_exists('enrol', array('courseid'=>$courseid, 'enrol'=>'manual'))) {
+            return NULL;
+        }
+
+        return new moodle_url('/enrol/manual/addinstance.php', array('sesskey'=>sesskey(), 'id'=>$courseid));
+    }
+
+    /**
+     * Adds enrol instance UI to course edit form
+     *
+     * @param object $instance enrol instance or null if does not exist yet
+     * @param MoodleQuickForm $mform
+     * @param object $data
+     * @param object $context context of existing course or parent category if course does not exist
+     * @return void
+     */
+    public function course_edit_form($instance, MoodleQuickForm $mform, $data, $context) {
+
+        $i = isset($instance->id) ? $instance->id : 0;
+        $plugin = enrol_get_plugin('manual');
+        $header = $plugin->get_instance_name($instance);
+        $config = has_capability('enrol/manual:config', $context);
+
+        $mform->addElement('header', 'enrol_manual_header_'.$i, $header);
+
+
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        $mform->addElement('select', 'enrol_manual_status_'.$i, get_string('status', 'enrol_manual'), $options);
+        $mform->setDefault('enrol_manual_status_'.$i, $this->get_config('status'));
+        $mform->setAdvanced('enrol_manual_status_'.$i, $this->get_config('status_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_manual_status_'.$i);
+        }
+
+
+        $mform->addElement('duration', 'enrol_manual_enrolperiod_'.$i, get_string('defaultperiod', 'enrol_manual'), array('optional' => true, 'defaultunit' => 86400));
+        $mform->setDefault('enrol_manual_enrolperiod_'.$i, $this->get_config('enrolperiod'));
+        $mform->setAdvanced('enrol_manual_enrolperiod_'.$i, $this->get_config('enrolperiod_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_manual_enrolperiod_'.$i);
+        } else {
+            $mform->disabledIf('enrol_manual_enrolperiod_'.$i, 'enrol_manual_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        if ($instance) {
+            $roles = get_default_enrol_roles($context, $instance->roleid);
+        } else {
+            $roles = get_default_enrol_roles($context, $this->get_config('roleid'));
+        }
+        $mform->addElement('select', 'enrol_manual_roleid_'.$i, get_string('defaultrole', 'role'), $roles);
+        $mform->setDefault('enrol_manual_roleid_'.$i, $this->get_config('roleid'));
+        $mform->setAdvanced('enrol_manual_roleid_'.$i, $this->get_config('roleid_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_manual_roleid_'.$i);
+        } else {
+            $mform->disabledIf('enrol_manual_roleid_'.$i, 'enrol_manual_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        // now add all values from enrol table
+        if ($instance) {
+            foreach($instance as $key=>$val) {
+                $data->{'enrol_manual_'.$key.'_'.$i} = $val;
+            }
+        }
+    }
+
+    /**
+     * Called after updating/inserting course.
+     *
+     * @param bool $inserted true if course just inserted
+     * @param object $course
+     * @param object $data form data
+     * @return void
+     */
+    public function course_updated($inserted, $course, $data) {
+        global $DB;
+
+        $context = get_context_instance(CONTEXT_COURSE, $course->id);
+        if (has_capability('enrol/manual:config', $context)) {
+            if ($inserted) {
+                if (isset($data->enrol_manual_status_0)) {
+                    $fields = array('status'=>$data->enrol_manual_status_0);
+                    if ($fields['status'] == ENROL_INSTANCE_ENABLED) {
+                        $fields['enrolperiod'] = $data->enrol_manual_enrolperiod_0;
+                        $fields['roleid']      = $data->enrol_manual_roleid_0;
+                    } else {
+                        $fields['enrolperiod'] = $this->get_config('enrolperiod');
+                        $fields['roleid']      = $this->get_config('roleid');
+                    }
+                    $this->add_instance($course, $fields);
+                }
+            } else {
+                $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'manual'));
+                foreach ($instances as $instance) {
+                    $i = $instance->id;
+                    if (isset($data->{'enrol_manual_status_'.$i})) {
+                        $instance->status       = $data->{'enrol_manual_status_'.$i};
+                        $instance->timemodified = time();
+                        if ($instance->status == ENROL_INSTANCE_ENABLED) {
+                            $instance->enrolperiod = $data->{'enrol_manual_enrolperiod_'.$i};
+                            $instance->roleid      = $data->{'enrol_manual_roleid_'.$i};
+                        }
+                        $DB->update_record('enrol', $instance);
+                    }
+                }
+            }
+
+        } else {
+            if ($inserted) {
+                if ($this->get_config('defaultenrol')) {
+                    $this->add_default_instance($course);
+                }
+            } else {
+                // bad luck, user can not change anything
+            }
+        }
+    }
+
+    /**
+     * Add new instance of enrol plugin with default settings.
+     * @param object $course
+     * @return int id of new instance
+     */
+    public function add_default_instance($course) {
+        $fields = array('status'=>$this->get_config('status'), 'enrolperiod'=>$this->get_config('enrolperiod', 0), 'roleid'=>$this->get_config('roleid', 0));
+        return $this->add_instance($course, $fields);
+    }
+
+    public function cron() {
+        // TODO: deal with $CFG->longtimenosee
+    }
+}
+
diff --git a/enrol/manual/locallib.php b/enrol/manual/locallib.php
new file mode 100644
index 000000000000..bdf74a4ac684
--- /dev/null
+++ b/enrol/manual/locallib.php
@@ -0,0 +1,154 @@
+<?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/>.
+
+/**
+ * Auxiliary manual user enrolment lib, the main purpose is to lower memory requirements...
+ *
+ * @package    enrol_manual
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once($CFG->dirroot . '/user/selector/lib.php');
+
+/**
+ * Enrol candidates
+ */
+class enrol_manual_potential_participant extends user_selector_base {
+    protected $enrolid;
+
+    public function __construct($name, $options) {
+        $this->enrolid  = $options['enrolid'];
+        parent::__construct($name, $options);
+    }
+
+    /**
+     * Candidate users
+     * @param <type> $search
+     * @return array
+     */
+    public function find_users($search) {
+        global $DB;
+        //by default wherecondition retrieves all users except the deleted, not confirmed and guest
+        list($wherecondition, $params) = $this->search_sql($search, 'u');
+        $params['enrolid'] = $this->enrolid;
+
+        $fields      = 'SELECT ' . $this->required_fields_sql('u');
+        $countfields = 'SELECT COUNT(1)';
+
+        $sql = " FROM {user} u
+                WHERE $wherecondition
+                      AND u.id NOT IN (SELECT ue.userid
+                                         FROM {user_enrolments} ue
+                                         JOIN {enrol} e ON (e.id = ue.enrolid AND e.id = :enrolid))";
+
+        $order = ' ORDER BY u.lastname ASC, u.firstname ASC';
+
+        if (!$this->is_validating()) {
+            $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
+            if ($potentialmemberscount > 100) {
+                return $this->too_many_results($search, $potentialmemberscount);
+            }
+        }
+
+        $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
+
+        if (empty($availableusers)) {
+            return array();
+        }
+
+
+        if ($search) {
+            $groupname = get_string('enrolcandidatesmatching', 'enrol', $search);
+        } else {
+            $groupname = get_string('enrolcandidates', 'enrol');
+        }
+
+        return array($groupname => $availableusers);
+    }
+
+    protected function get_options() {
+        $options = parent::get_options();
+        $options['enrolid'] = $this->enrolid;
+        $options['file']    = 'enrol/manual/locallib.php';
+        return $options;
+    }
+}
+
+/**
+ * Enroled users
+ */
+class enrol_manual_current_participant extends user_selector_base {
+    protected $courseid;
+    protected $enrolid;
+
+    public function __construct($name, $options) {
+        $this->enrolid  = $options['enrolid'];
+        parent::__construct($name, $options);
+    }
+
+    /**
+     * Candidate users
+     * @param <type> $search
+     * @return array
+     */
+    public function find_users($search) {
+        global $DB;
+        //by default wherecondition retrieves all users except the deleted, not confirmed and guest
+        list($wherecondition, $params) = $this->search_sql($search, 'u');
+        $params['enrolid'] = $this->enrolid;
+
+        $fields      = 'SELECT ' . $this->required_fields_sql('u');
+        $countfields = 'SELECT COUNT(1)';
+
+        $sql = " FROM {user} u
+                 JOIN {user_enrolments} ue ON (ue.userid = u.id AND ue.enrolid = :enrolid)
+                WHERE $wherecondition";
+
+        $order = ' ORDER BY u.lastname ASC, u.firstname ASC';
+
+        if (!$this->is_validating()) {
+            $potentialmemberscount = $DB->count_records_sql($countfields . $sql, $params);
+            if ($potentialmemberscount > 100) {
+                return $this->too_many_results($search, $potentialmemberscount);
+            }
+        }
+
+        $availableusers = $DB->get_records_sql($fields . $sql . $order, $params);
+
+        if (empty($availableusers)) {
+            return array();
+        }
+
+
+        if ($search) {
+            $groupname = get_string('enrolledusersmatching', 'enrol', $search);
+        } else {
+            $groupname = get_string('enrolledusers', 'enrol');
+        }
+
+        return array($groupname => $availableusers);
+    }
+
+    protected function get_options() {
+        $options = parent::get_options();
+        $options['enrolid'] = $this->enrolid;
+        $options['file']    = 'enrol/manual/locallib.php';
+        return $options;
+    }
+}
diff --git a/enrol/manual/manage.php b/enrol/manual/manage.php
new file mode 100644
index 000000000000..3a37aa866d65
--- /dev/null
+++ b/enrol/manual/manage.php
@@ -0,0 +1,188 @@
+<?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/>.
+
+/**
+ * Manual user enrolment UI.
+ *
+ * @package    enrol_manual
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+require_once($CFG->dirroot.'/enrol/manual/locallib.php');
+
+$enrolid      = required_param('enrolid', PARAM_INT);
+$roleid       = optional_param('roleid', -1, PARAM_INT);
+$extendperiod = optional_param('extendperiod', 0, PARAM_INT);
+$extendbase   = optional_param('extendbase', 3, PARAM_INT);
+
+$instance = $DB->get_record('enrol', array('id'=>$enrolid, 'enrol'=>'manual'), '*', MUST_EXIST);
+$course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('enrol/manual:manage', $context);
+
+$instance = $DB->get_record('enrol', array('id'=>$enrolid, 'enrol'=>'manual'), '*', MUST_EXIST);
+if ($roleid < 0) {
+    $roleid = $instance->roleid;
+}
+$roles = get_assignable_roles($context);
+$roles = array('0'=>get_string('none')) + $roles;
+
+if (!isset($roles[$roleid])) {
+    // weird - security always first!
+    $roleid = 0;
+}
+
+if (!$enrol_manual = enrol_get_plugin('manual')) {
+    throw coding_error('Can not instantiate enrol_manual');
+}
+
+$instancename = $enrol_manual->get_instance_name($instance);
+
+$PAGE->set_url('/enrol/manual/manage.php', array('enrolid'=>$instance->id));
+$PAGE->set_pagelayout('admin');
+$PAGE->set_title($enrol_manual->get_instance_name($instance));
+
+// Create the user selector objects.
+$options = array('enrolid' => $enrolid);
+
+$potentialuserselector = new enrol_manual_potential_participant('addselect', $options);
+$currentuserselector = new enrol_manual_current_participant('removeselect', $options);
+
+// Build the list of options for the enrolment period dropdown.
+$unlimitedperiod = get_string('unlimited');
+$periodmenu = array();
+for ($i=1; $i<=365; $i++) {
+    $seconds = $i * 86400;
+    $periodmenu[$seconds] = get_string('numdays', '', $i);
+}
+// Work out the apropriate default setting.
+if ($extendperiod) {
+    $defaultperiod = $extendperiod;
+} else {
+    $defaultperiod = $instance->enrolperiod;
+}
+
+// Build the list of options for the starting from dropdown.
+$timeformat = get_string('strftimedatefullshort');
+$today = time();
+$today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
+
+// enrolment start
+$basemenu = array();
+if ($course->startdate > 0) {
+    $basemenu[2] = get_string('coursestart') . ' (' . userdate($course->startdate, $timeformat) . ')';
+}
+$basemenu[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
+
+// process add and removes
+if (optional_param('add', false, PARAM_BOOL) && confirm_sesskey()) {
+    $userstoassign = $potentialuserselector->get_selected_users();
+    if (!empty($userstoassign)) {
+        foreach($userstoassign as $adduser) {
+            switch($extendbase) {
+                case 2:
+                    $timestart = $course->startdate;
+                    break;
+                case 3:
+                default:
+                    $timestart = $today;
+                    break;
+            }
+
+            if ($extendperiod <= 0) {
+                $timestart = 0;
+                $timeend = 0;
+            } else {
+                $timeend = $timestart + $extendperiod;
+            }
+            $enrol_manual->enrol_user($instance, $adduser->id, $roleid, $timestart, $timeend, true);
+            add_to_log($course->id, 'course', 'enrol', '../enrol/users.php?id='.$course->id, $course->id); //there should be userid somewhere!
+        }
+
+        $potentialuserselector->invalidate_selected_users();
+        $currentuserselector->invalidate_selected_users();
+
+        //TODO: log
+    }
+}
+
+// Process incoming role unassignments
+if (optional_param('remove', false, PARAM_BOOL) && confirm_sesskey()) {
+    $userstounassign = $currentuserselector->get_selected_users();
+    if (!empty($userstounassign)) {
+        foreach($userstounassign as $removeuser) {
+            $enrol_manual->unenrol_user($instance, $removeuser->id);
+            add_to_log($course->id, 'course', 'unenrol', '../enrol/users.php?id='.$course->id, $course->id); //there should be userid somewhere!
+        }
+
+        $potentialuserselector->invalidate_selected_users();
+        $currentuserselector->invalidate_selected_users();
+
+        //TODO: log
+    }
+}
+
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading($instancename);
+
+?>
+<form id="assignform" method="post" action="<?php echo $PAGE->url ?>"><div>
+  <input type="hidden" name="sesskey" value="<?php echo sesskey() ?>" />
+
+  <table summary="" class="roleassigntable generaltable generalbox boxaligncenter" cellspacing="0">
+    <tr>
+      <td id="existingcell">
+          <p><label for="removeselect"><?php print_string('enrolledusers', 'enrol'); ?></label></p>
+          <?php $currentuserselector->display() ?>
+      </td>
+      <td id="buttonscell">
+          <div id="addcontrols">
+              <input name="add" id="add" type="submit" value="<?php echo $OUTPUT->larrow().'&nbsp;'.get_string('add'); ?>" title="<?php print_string('add'); ?>" /><br />
+
+              <div class="enroloptions">
+
+              <p><label for="roleid"><?php print_string('assignrole', 'enrol_manual') ?></label><br />
+              <?php echo html_writer::select($roles, 'roleid', $roleid, false); ?></p>
+
+              <p><label for="extendperiod"><?php print_string('enrolperiod', 'enrol') ?></label><br />
+              <?php echo html_writer::select($periodmenu, 'extendperiod', $defaultperiod, $unlimitedperiod); ?></p>
+
+              <p><label for="extendbase"><?php print_string('startingfrom') ?></label><br />
+              <?php echo html_writer::select($basemenu, 'extendbase', $extendbase, false); ?></p>
+
+              </div>
+          </div>
+
+          <div id="removecontrols">
+              <input name="remove" id="remove" type="submit" value="<?php echo get_string('remove').'&nbsp;'.$OUTPUT->rarrow(); ?>" title="<?php print_string('remove'); ?>" />
+          </div>
+      </td>
+      <td id="potentialcell">
+          <p><label for="addselect"><?php print_string('enrolcandidates', 'enrol'); ?></label></p>
+          <?php $potentialuserselector->display() ?>
+      </td>
+    </tr>
+  </table>
+</div></form>
+<?php
+
+
+echo $OUTPUT->footer();
diff --git a/enrol/manual/settings.php b/enrol/manual/settings.php
new file mode 100644
index 000000000000..851ccbfc062c
--- /dev/null
+++ b/enrol/manual/settings.php
@@ -0,0 +1,60 @@
+<?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/>.
+
+/**
+ * Manual enrolment plugin settings and presets.
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_manual_settings', '', get_string('pluginname_desc', 'enrol_manual')));
+
+
+    //--- enrol instance defaults ----------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_manual_defaults',
+        get_string('enrolinstancedefaults', 'admin'), get_string('enrolinstancedefaults_desc', 'admin')));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_manual/defaultenrol',
+        get_string('defaultenrol', 'enrol'), get_string('defaultenrol_desc', 'enrol'), 1));
+
+    $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                     ENROL_INSTANCE_DISABLED => get_string('no'));
+    $settings->add(new admin_setting_configselect_with_advanced('enrol_manual/status',
+        get_string('status', 'enrol_manual'), get_string('status_desc', 'enrol_manual'),
+        array('value'=>ENROL_INSTANCE_ENABLED, 'adv'=>false), $options));
+
+    $settings->add(new admin_setting_configtext_with_advanced('enrol_manual/enrolperiod',
+        get_string('defaultperiod', 'enrol_manual'), get_string('defaultperiod_desc', 'enrol_manual'),
+        array('value'=>0, 'adv'=>true), PARAM_INT));
+
+    if (!during_initial_install()) {
+        $options = get_default_enrol_roles(get_context_instance(CONTEXT_SYSTEM));
+        $student = get_archetype_roles('student');
+        $student = reset($student);
+        $settings->add(new admin_setting_configselect_with_advanced('enrol_manual/roleid',
+            get_string('defaultrole', 'role'), '',
+            array('value'=>$student->id, 'adv'=>true), $options));
+    }
+}
+
diff --git a/enrol/manual/unenrolself.php b/enrol/manual/unenrolself.php
new file mode 100644
index 000000000000..fdb2a45ae9fd
--- /dev/null
+++ b/enrol/manual/unenrolself.php
@@ -0,0 +1,58 @@
+<?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/>.
+
+/**
+ * Manual enrolment plugin - support for user self unenrolment.
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+
+$enrolid = required_param('enrolid', PARAM_INT);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+$instance = $DB->get_record('enrol', array('id'=>$enrolid, 'enrol'=>'manual'), '*', MUST_EXIST);
+$course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+
+$plugin = enrol_get_plugin('manual');
+
+// security defined inside following function
+if (!$plugin->get_unenrolself_link($instance)) {
+    redirect(new moodle_url('/course/view.php', array('id'=>$course->id)));
+}
+
+$PAGE->set_url('/enrol/manual/unenrolself.php', array('enrolid'=>$instance->id));
+$PAGE->set_title($plugin->get_instance_name($instance));
+
+if ($confirm and confirm_sesskey()) {
+    $plugin->unenrol_user($instance, $USER->id);
+    add_to_log($course->id, 'course', 'unenrol', '../enrol/users.php?id='.$course->id, $course->id); //there should be userid somewhere!
+    redirect(new moodle_url('/index.php'));
+}
+
+echo $OUTPUT->header();
+$yesurl = new moodle_url($PAGE->url, array('confirm'=>1, 'sesskey'=>sesskey()));
+$nourl = new moodle_url('/course/view.php', array('id'=>$course->id));
+$message = get_string('unenrolselfconfirm', 'enrol_manual', format_string($course->fullname));
+echo $OUTPUT->confirm($message, $yesurl, $nourl);
+echo $OUTPUT->footer();
diff --git a/enrol/manual/version.php b/enrol/manual/version.php
new file mode 100644
index 000000000000..fd209b7a96a7
--- /dev/null
+++ b/enrol/manual/version.php
@@ -0,0 +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/>.
+
+/**
+ * Manual enrolment plugin version specification.
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$plugin->version = 2010061500;
diff --git a/enrol/meta/addinstance.php b/enrol/meta/addinstance.php
new file mode 100644
index 000000000000..6fa2f1e099a4
--- /dev/null
+++ b/enrol/meta/addinstance.php
@@ -0,0 +1,60 @@
+<?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/>.
+
+/**
+ * Adds new instance of enrol_meta to specified course.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+require_once("$CFG->dirroot/enrol/meta/addinstance_form.php");
+require_once("$CFG->dirroot/enrol/meta/locallib.php");
+
+$id = required_param('id', PARAM_INT); // course id
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('moodle/course:enrolconfig', $context);
+
+$enrol = enrol_get_plugin('meta');
+if (!$enrol->get_candidate_link($course->id)) {
+    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
+}
+
+$mform = new enrol_meta_addinstance_form(NULL, $course);
+
+if ($mform->is_cancelled()) {
+    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
+
+} else if ($data = $mform->get_data()) {
+    $eid = $enrol->add_instance($course, array('customint1'=>$data->link));
+    enrol_meta_sync($course->id);
+    redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
+}
+
+$PAGE->set_url('/enrol/meta/addinstance.php', array('id'=>$course->id));
+
+echo $OUTPUT->header();
+
+$mform->display();
+
+echo $OUTPUT->footer();
diff --git a/enrol/meta/addinstance_form.php b/enrol/meta/addinstance_form.php
new file mode 100644
index 000000000000..980aaf71cc80
--- /dev/null
+++ b/enrol/meta/addinstance_form.php
@@ -0,0 +1,96 @@
+<?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/>.
+
+/**
+ * Adds instance form
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/formslib.php");
+
+class enrol_meta_addinstance_form extends moodleform {
+    protected $course;
+
+    function definition() {
+        global $CFG, $DB;
+
+        $mform  = $this->_form;
+        $course = $this->_customdata;
+        $this->course = $course;
+
+        $existing = $DB->get_records('enrol', array('enrol'=>'meta', 'courseid'=>$course->id), '', 'customint1, id');
+
+        // TODO: this has to be done via ajax or else it will fail very badly on large sites!
+        $courses = array('' => get_string('choosedots'));
+        $rs = $DB->get_recordset('course', array(), 'sortorder ASC', 'id, fullname, shortname, visible');
+        foreach ($rs as $c) {
+            if ($c->id == SITEID or $c->id == $course->id or isset($existing[$c->id])) {
+                continue;
+            }
+            $coursecontext = get_context_instance(CONTEXT_COURSE, $c->id);
+            if (!$c->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+                continue;
+            }
+            if (!has_capability('enrol/meta:selectaslinked', $coursecontext)) {
+                continue;
+            }
+            $courses[$c->id] = format_string($c->fullname). ' ['.$c->shortname.']';
+        }
+        $rs->close();
+
+        $mform->addElement('header','general', get_string('pluginname', 'enrol_meta'));
+
+        $mform->addElement('select', 'link', get_string('linkedcourse', 'enrol_meta'), $courses);
+        $mform->addRule('link', get_string('required'), 'required', null, 'client');
+
+        $mform->addElement('hidden', 'id', null);
+        $mform->setType('id', PARAM_INT);
+
+        $this->add_action_buttons();
+
+        $this->set_data(array('id'=>$course->id));
+    }
+
+    function validation($data, $files) {
+        global $DB, $CFG;
+
+        // TODO: this is duplicated here because it may be necessary one we implement ajax course selection element
+
+        $errors = parent::validation($data, $files);
+        if (!$c = $DB->get_record('course', array('id'=>$data['link']))) {
+            $errors['link'] = get_string('required');
+        } else {
+            $coursecontext = get_context_instance(CONTEXT_COURSE, $c->id);
+            $existing = $DB->get_records('enrol', array('enrol'=>'meta', 'courseid'=>$this->course->id), '', 'customint1, id');
+            if (!$c->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+                $errors['link'] = get_string('error');
+            } else if (!has_capability('enrol/meta:selectaslinked', $coursecontext)) {
+                $errors['link'] = get_string('error');
+            } else if ($c->id == SITEID or $c->id == $this->course->id or isset($existing[$c->id])) {
+                $errors['link'] = get_string('error');
+            }
+        }
+
+        return $errors;
+    }
+}
+
diff --git a/enrol/meta/db/access.php b/enrol/meta/db/access.php
new file mode 100644
index 000000000000..1bbb946ed576
--- /dev/null
+++ b/enrol/meta/db/access.php
@@ -0,0 +1,45 @@
+<?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/>.
+
+/**
+ * Capabilities for meta link access plugin.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$capabilities = array(
+    'enrol/meta:config' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+    // select some course as source
+    'enrol/meta:selectaslinked' => array(
+        'captype' => 'read',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'manager' => CAP_ALLOW,
+        )
+    ),
+);
+
+
diff --git a/enrol/meta/db/events.php b/enrol/meta/db/events.php
new file mode 100644
index 000000000000..541d9b66e2f0
--- /dev/null
+++ b/enrol/meta/db/events.php
@@ -0,0 +1,57 @@
+<?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/>.
+
+/**
+ * Meta course enrolment plugin event handler definition.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/* List of handlers */
+$handlers = array (
+    'role_assigned' => array (
+        'handlerfile'      => '/enrol/meta/locallib.php',
+        'handlerfunction'  => array('enrol_meta_handler', 'role_assigned'),
+        'schedule'         => 'instant'
+    ),
+
+    'role_unassigned' => array (
+        'handlerfile'      => '/enrol/meta/locallib.php',
+        'handlerfunction'  => array('enrol_meta_handler', 'role_unassigned'),
+        'schedule'         => 'instant'
+    ),
+
+    'user_enrolled' => array (
+        'handlerfile'      => '/enrol/meta/locallib.php',
+        'handlerfunction'  => array('enrol_meta_handler', 'user_enrolled'),
+        'schedule'         => 'instant'
+    ),
+
+    'user_unenrolled' => array (
+        'handlerfile'      => '/enrol/meta/locallib.php',
+        'handlerfunction'  => array('enrol_meta_handler', 'user_unenrolled'),
+        'schedule'         => 'instant'
+    ),
+
+    'course_deleted' => array (
+        'handlerfile'      => '/enrol/meta/locallib.php',
+        'handlerfunction'  => array('enrol_meta_handler', 'course_deleted'),
+        'schedule'         => 'instant'
+    ),
+);
diff --git a/enrol/meta/db/install.php b/enrol/meta/db/install.php
new file mode 100644
index 000000000000..e490cc404435
--- /dev/null
+++ b/enrol/meta/db/install.php
@@ -0,0 +1,42 @@
+<?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/>.
+
+/**
+ * Meta link enrolment plugin installation.
+ *
+ * @package   enrol_manual
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+function xmldb_enrol_meta_install() {
+    global $CFG, $DB;
+
+    if (isset($CFG->nonmetacoursesyncroleids)) {
+        set_config('nosyncroleids', $CFG->nonmetacoursesyncroleids, 'enrol_meta');
+        unset_config('nonmetacoursesyncroleids');
+    }
+
+    if (!$DB->record_exists('enrol', array('enrol'=>'meta'))) {
+        // no need to syn roles and enrolments
+        return;
+    }
+
+    // brute force course resync, this may take a while
+    require_once("$CFG->dirroot/enrol/meta/locallib.php");
+    enrol_meta_sync();
+}
diff --git a/enrol/meta/lang/en/enrol_meta.php b/enrol/meta/lang/en/enrol_meta.php
new file mode 100644
index 000000000000..eed0c4567422
--- /dev/null
+++ b/enrol/meta/lang/en/enrol_meta.php
@@ -0,0 +1,32 @@
+<?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/>.
+
+/**
+ * Strings for component 'enrol_meta', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package   enrol_meta
+ * @copyright 2010 onwards Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['linkedcourse'] = 'Link course';
+$string['meta:config'] = 'Configure meta link enrol instances';
+$string['meta:selectaslinked'] = 'Select course as meta linked';
+$string['nosyncroleids'] = 'Roles that are not synchronised';
+$string['nosyncroleids_desc'] = 'By default all course level role assignments are synchronised from parent to child courses. Roles that are selected here will not be included in the synchronisation process. The current roles will be updated in the next cron execution.';
+$string['pluginname'] = 'Course meta link';
+$string['pluginname_desc'] = 'Course meta link enrolment plugin synchronises enrolments and roles in two different courses.';
diff --git a/enrol/meta/lib.php b/enrol/meta/lib.php
new file mode 100644
index 000000000000..7323ead6a315
--- /dev/null
+++ b/enrol/meta/lib.php
@@ -0,0 +1,107 @@
+<?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/>.
+
+/**
+ * Meta course enrolment plugin.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Meta course enrolment plugin.
+ * @author Petr Skoda
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class enrol_meta_plugin extends enrol_plugin {
+
+    /**
+     * Returns localised name of enrol instance
+     *
+     * @param object $instance (null is accepted too)
+     * @return string
+     */
+    public function get_instance_name($instance) {
+        global $DB;
+
+        if (empty($instance)) {
+            $enrol = $this->get_name();
+            return get_string('pluginname', 'enrol_'.$enrol);
+        } else if (empty($instance->name)) {
+            $enrol = $this->get_name();
+            return get_string('pluginname', 'enrol_'.$enrol) . ' (' . format_string($DB->get_field('course', 'fullname', array('id'=>$instance->customint1))) . ')';
+        } else {
+            return format_string($instance->name);
+        }
+    }
+
+    /**
+     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * @param int $courseid
+     * @return moodle_url page url
+     */
+    public function get_candidate_link($courseid) {
+        if (!has_capability('moodle/course:enrolconfig', get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST))) {
+            return NULL;
+        }
+        // multiple instances supported - multiple parent courses linked
+        return new moodle_url('/enrol/meta/addinstance.php', array('id'=>$courseid));
+    }
+
+
+    /**
+     * Called after updating/inserting course.
+     *
+     * @param bool $inserted true if course just inserted
+     * @param object $course
+     * @param object $data form data
+     * @return void
+     */
+    public function course_updated($inserted, $course, $data) {
+        global $CFG;
+
+        if (!$inserted) {
+            // sync cohort enrols
+            require_once("$CFG->dirroot/enrol/meta/locallib.php");
+            enrol_meta_sync($course->id);
+        } else {
+            // cohorts are never inserted automatically
+        }
+
+    }
+
+    /**
+     * Called for all enabled enrol plugins that returned true from is_cron_required().
+     * @return void
+     */
+    public function cron() {
+        global $CFG;
+
+        // purge all roles if meta sync disabled, those can be recreated later here in cron
+        if (!enrol_is_enabled('meta')) {
+            role_unassign_all(array('component'=>'meta_enrol'));
+            return;
+        }
+
+        require_once("$CFG->dirroot/enrol/meta/locallib.php");
+        enrol_meta_sync();
+    }
+}
+
diff --git a/enrol/meta/locallib.php b/enrol/meta/locallib.php
new file mode 100644
index 000000000000..bd4d54e67a51
--- /dev/null
+++ b/enrol/meta/locallib.php
@@ -0,0 +1,317 @@
+<?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/>.
+
+/**
+ * Local stuff for meta course enrolment plugin.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+/**
+ * Event handler for meta enrolment plugin.
+ *
+ * We try to keep everything in sync via listening to events,
+ * it may fail sometimes, so we always do a full sync in cron too.
+ */
+class enrol_meta_handler {
+    public function role_assigned($ra) {
+        global $DB;
+
+        if (!enrol_is_enabled('meta')) {
+            return true;
+        }
+
+        // prevent circular dependencies - we can not sync meta roles recursively
+        if ($ra->component === 'enrol_meta') {
+            return true;
+        }
+
+        //only course level roles are interesting
+        $parentcontext = get_context_instance_by_id($ra->contextid);
+        if ($parentcontext->contextlevel != CONTEXT_COURSE) {
+            return true;
+        }
+
+        // does anything want to sync with this parent?
+        if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
+            return true;
+        }
+
+        // make sure the role sync is not prevented
+        $plugin = enrol_get_plugin('meta');
+        if ($disabled = $plugin->get_config('nosyncroleids')) {
+            if (in_array($ra->roleid, explode(',', $disabled))) {
+                continue;
+            }
+        }
+
+        foreach ($enrols as $enrol) {
+            // Is the user enrolled? We want to sync only really enrolled users
+            if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
+                continue;
+            }
+            $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
+
+            // just try to assign role, no problem if role assignment already exists
+            role_assign($ra->roleid, $ra->userid, $context->id, 'enrol_meta', $enrol->id);
+        }
+
+        return true;
+    }
+
+    public function role_unassigned($ra) {
+        global $DB;
+
+        //note: do not test if plugin enabled, we want to keep removing previous roles
+
+        // prevent circular dependencies - we can not sync meta roles recursively
+        if ($ra->component === 'enrol_meta') {
+            return true;
+        }
+
+        // only course level roles are interesting
+        $parentcontext = get_context_instance_by_id($ra->contextid);
+        if ($parentcontext->contextlevel != CONTEXT_COURSE) {
+            return true;
+        }
+
+        // does anything want to sync with this parent?
+        if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
+            return true;
+        }
+
+        //note: do not check 'nosyncroleids', somebody might have just enabled it, we want to get rid of nosync roles gradually
+
+        foreach ($enrols as $enrol) {
+            // Is the user enrolled? We want to sync only really enrolled users
+            if (!$DB->record_exists('user_enrolments', array('userid'=>$ra->userid, 'enrolid'=>$enrol->id))) {
+                continue;
+            }
+            $context = get_context_instance(CONTEXT_COURSE, $enrol->courseid);
+
+            // now make sure the user does not have the role through some other enrol plugin
+            $params = array('contextid'=>$ra->contextid, 'roleid'=>$ra->roleid, 'userid'=>$ra->userid);
+            if ($DB->record_exists_select('role_assignments', "contextid = :contextid AND roleid = :roleid AND userid = :userid AND component <> 'enrol_meta'", $params)) {
+                continue;
+            }
+
+            // unassing role, there is no other role assignment in parent course
+            role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $enrol->id);
+        }
+
+        return true;
+    }
+
+    public function user_enrolled($ue) {
+        global $DB;
+
+        if (!enrol_is_enabled('meta')) {
+            return true;
+        }
+
+        // does anything want to sync with this parent?
+        if (!$enrols = $DB->get_records('enrol', array('customint1'=>$ue->courseid, 'enrol'=>'meta'), 'id ASC')) {
+            return true;
+        }
+
+        $plugin = enrol_get_plugin('meta');
+        foreach ($enrols as $enrol) {
+            // no problem if already enrolled
+            $plugin->enrol_user($enrol, $ue->userid);
+        }
+
+        return true;
+    }
+
+    public function user_unenrolled($ue) {
+        global $DB;
+
+        //note: do not test if plugin enabled, we want to keep removing previously linked courses
+
+        // look for unenrolment candidates - it may be possible that user has multiple enrolments...
+        $sql = "SELECT DISTINCT e.*
+                  FROM {enrol} e
+                  JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
+                  JOIN {enrol} pe ON (pe.courseid = e.customint1 AND pe.enrol <> 'meta' AND pe.courseid = :courseid)
+             LEFT JOIN {user_enrolments} pue ON (pue.enrolid = pe.id AND pue.userid = ue.userid)
+                 WHERE pue.id IS NULL";
+        $params = array('courseid'=>$ue->courseid, 'userid'=>$ue->userid);
+
+        if (!$enrols = $DB->get_records_sql($sql, $params)) {
+            return true;
+        }
+
+        $plugin = enrol_get_plugin('meta');
+        foreach ($enrols as $enrol) {
+            $plugin->unenrol_user($enrol, $ue->userid);
+        }
+
+        return true;
+    }
+
+    public function course_deleted($course) {
+        global $DB;
+
+        //note: do not test if plugin enabled, we want to keep removing previously linked courses
+
+        // does anything want to sync with this parent?
+        if (!$enrols = $DB->get_records('enrol', array('customint1'=>$parentcontext->instanceid, 'enrol'=>'meta'), 'id ASC')) {
+            return true;
+        }
+
+        $plugin = enrol_get_plugin('meta');
+        foreach ($enrols as $enrol) {
+            //unenrol all users
+            $ues = $DB->get_records_sql('user_enrolments', array('courseid'=>$enrol->courseid, 'enrolid'=>$enrol->id));
+            foreach ($ues as $ue) {
+                $plugin->unenrol_user($enrol, $ue->userid);
+            }
+            $ues->close();
+        }
+
+        return true;
+    }
+}
+
+/**
+ * Sync all meta course links.
+ * @param int $courseid one course, empty mean all
+ * @return void
+ */
+function enrol_meta_sync($courseid = NULL) {
+    global $CFG, $DB;
+
+    // unfortunately this may take a loooong time
+    @set_time_limit(0); //if this fails during upgrade we can continue from cron, no big deal
+
+    $meta = enrol_get_plugin('meta');
+
+    $onecourse = $courseid ? "AND e.courseid = :courseid" : "";
+
+    // iterate through all not enrolled yet users
+    if (enrol_is_enabled('meta')) {
+        list($enabled, $params) = $DB->get_in_or_equal(explode(',', $CFG->enrol_plugins_enabled), SQL_PARAMS_NAMED, 'e00');
+        $onecourse = "";
+        if ($courseid) {
+            $params['courseid'] = $courseid;
+            $onecourse = "AND e.courseid = :courseid";
+        }
+        $sql = "SELECT pue.userid, e.id AS enrolid
+                  FROM {user_enrolments} pue
+                  JOIN {enrol} pe ON (pe.id = pue.enrolid AND pe.enrol <> 'meta' AND pe.enrol $enabled )
+                  JOIN {enrol} e ON (e.customint1 = pe.courseid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
+             LEFT JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = pue.userid)
+                 WHERE ue.id IS NULL";
+        $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
+        $params['courseid'] = $courseid;
+
+        $rs = $DB->get_recordset_sql($sql, $params);
+        $instances = array(); //cache
+        foreach($rs as $ue) {
+            if (!isset($instances[$ue->enrolid])) {
+                $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+            }
+            $meta->enrol_user($instances[$ue->enrolid], $ue->userid);
+        }
+        $rs->close();
+        unset($instances);
+    }
+
+    // unenrol as necessary - ignore enabled flag, we want to get rid of all
+    $sql = "SELECT ue.userid, e.id AS enrolid
+              FROM {user_enrolments} ue
+              JOIN {enrol} e ON (e.id = ue.enrolid AND e.enrol = 'meta' $onecourse)
+         LEFT JOIN (SELECT xue.userid, xe.courseid
+                      FROM {enrol} xe
+                      JOIN {user_enrolments} xue ON (xue.enrolid = xe.id)
+                   ) pue ON (pue.courseid = e.customint1 AND pue.userid = ue.userid)
+             WHERE pue.courseid IS NULL";
+    //TODO: this may use a bit of SQL optimisation
+    $rs = $DB->get_recordset_sql($sql, array('courseid'=>$courseid));
+    $instances = array(); //cache
+    foreach($rs as $ue) {
+        if (!isset($instances[$ue->enrolid])) {
+            $instances[$ue->enrolid] = $DB->get_record('enrol', array('id'=>$ue->enrolid));
+        }
+        $meta->unenrol_user($instances[$ue->enrolid], $ue->userid);
+    }
+    $rs->close();
+    unset($instances);
+
+    // now assign all necessary roles
+    if (enrol_is_enabled('meta')) {
+        $enabled = explode(',', $CFG->enrol_plugins_enabled);
+        foreach($enabled as $k=>$v) {
+            if ($v === 'meta') {
+                continue; // no meta sync of meta roles
+            }
+            $enabled[$k] = 'enrol_'.$v;
+        }
+        $enabled[] = $DB->sql_empty(); // manual assignments are replicated too
+
+        list($enabled, $params) = $DB->get_in_or_equal($enabled, SQL_PARAMS_NAMED, 'e00');
+        $sql = "SELECT DISTINCT pra.roleid, pra.userid, c.id AS contextid, e.id AS enrolid
+                  FROM {role_assignments} pra
+                  JOIN {context} pc ON (pc.id = pra.contextid AND pc.contextlevel = :coursecontext AND pra.component $enabled)
+                  JOIN {enrol} e ON (e.customint1 = pc.instanceid AND e.enrol = 'meta' AND e.status = :statusenabled $onecourse)
+                  JOIN {context} c ON (c.contextlevel = pc.contextlevel AND c.instanceid = e.courseid)
+             LEFT JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.userid = pra.userid AND ra.roleid = pra.itemid AND ra.itemid = e.id AND ra.component = 'enrol_meta')
+                 WHERE ra.id IS NULL";
+        $params['statusenabled'] = ENROL_INSTANCE_ENABLED;
+        $params['coursecontext'] = CONTEXT_COURSE;
+        $params['courseid'] = $courseid;
+
+        if ($ignored = $meta->get_config('nosyncroleids')) {
+            list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'i00', false);
+            $params = array_merge($params, $xparams);
+            $sql = "$sql AND pra.roleid $notignored";
+        }
+
+        $rs = $DB->get_recordset_sql($sql, $params);
+        foreach($rs as $ra) {
+            role_assign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->enrolid);
+        }
+        $rs->close();
+    }
+
+    // remove unwanted roles - include ignored roles and disabled plugins too
+    $params = array('coursecontext' => CONTEXT_COURSE, 'courseid' => $courseid);
+    if ($ignored = $meta->get_config('nosyncroleids')) {
+        list($notignored, $xparams) = $DB->get_in_or_equal(explode(',', $ignored), SQL_PARAMS_NAMED, 'i00', false);
+        $params = array_merge($params, $xparams);
+        $notignored = "AND pra.roleid $notignored";
+    } else {
+        $notignored = "";
+    }
+    $sql = "SELECT ra.roleid, ra.userid, ra.contextid, ra.itemid
+              FROM {role_assignments} ra
+              JOIN {enrol} e ON (e.id = ra.itemid AND ra.component = 'enrol_meta' AND e.enrol = 'meta' $onecourse)
+              JOIN {context} pc ON (pc.instanceid = e.customint1 AND pc.contextlevel = :coursecontext)
+         LEFT JOIN {role_assignments} pra ON (pra.contextid = pc.id AND pra.userid = ra.userid AND pra.roleid = ra.roleid AND pra.component <> 'enrol_meta' $notignored)
+             WHERE pra.id IS NULL";
+
+    $rs = $DB->get_recordset_sql($sql, $params);
+    foreach($rs as $ra) {
+        role_unassign($ra->roleid, $ra->userid, $ra->contextid, 'enrol_meta', $ra->itemid);
+    }
+    $rs->close();
+
+}
diff --git a/enrol/meta/settings.php b/enrol/meta/settings.php
new file mode 100644
index 000000000000..3402f582c711
--- /dev/null
+++ b/enrol/meta/settings.php
@@ -0,0 +1,41 @@
+<?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/>.
+
+/**
+ * Meta enrolment plugin settings and presets.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_meta_settings', '', get_string('pluginname_desc', 'enrol_meta')));
+
+    if (!during_initial_install()) {
+        $allroles = array();
+        foreach (get_all_roles() as $role) {
+                $rolename = strip_tags(format_string($role->name)) . ' ('. $role->shortname . ')';
+                $allroles[$role->id] = $rolename;
+        }
+        $settings->add(new admin_setting_configmultiselect('enrol_meta/nosyncroleids', get_string('nosyncroleids', 'enrol_meta'), get_string('nosyncroleids_desc', 'enrol_meta'), array(), $allroles));
+    }
+}
diff --git a/enrol/meta/version.php b/enrol/meta/version.php
new file mode 100644
index 000000000000..7dc736312728
--- /dev/null
+++ b/enrol/meta/version.php
@@ -0,0 +1,27 @@
+<?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/>.
+
+/**
+ * Meta link enrolment plugin version specification.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$plugin->version = 2010061500;
+$plugin->cron    = 60;
\ No newline at end of file
diff --git a/enrol/mnet/enrol.php b/enrol/mnet/enrol.php
index 388c875df18f..67fdf3cfa4d4 100644
--- a/enrol/mnet/enrol.php
+++ b/enrol/mnet/enrol.php
@@ -352,9 +352,7 @@ function unenrol_user($username, $courseid) {
         // Are we a *real* user or the shady MNET Daemon?
         // require_capability('moodle/role:assign', $context, NULL, false);
 
-        if (!role_unassign(0, $userrecord->id, 0, $context->id)) {
-            throw new mnet_exception(5015, 'couldnotunenrol', 'enrol_mnet');
-        }
+        role_unassign_all(array('userid'=>$userrecord->id, 'contextiod'=>$context->id, 'component'=>'enrol_mnet'), true, true);
 
         return true;
     }
diff --git a/enrol/otherusers.php b/enrol/otherusers.php
new file mode 100644
index 000000000000..401e12333e96
--- /dev/null
+++ b/enrol/otherusers.php
@@ -0,0 +1,91 @@
+<?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/>.
+
+/**
+ * List and modify users that are not enrolled but still have a role in course.
+ *
+ * @package    core
+ * @subpackage enrol
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../config.php');
+
+$id      = required_param('id', PARAM_INT); // course id
+$action  = optional_param('action', '', PARAM_ACTION);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+$ifilter = optional_param('ifilter', 0, PARAM_INT); // only one instance
+$page    = optional_param('page', 0, PARAM_INT);
+$perpage = optional_param('perpage', 20, PARAM_INT);
+$sort    = optional_param('sort', 'lastname', PARAM_ALPHA);
+$dir     = optional_param('dir', 'ASC', PARAM_ALPHA);
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('moodle/role:assign', $context);
+
+if ($course->id == SITEID) {
+    redirect("$CFG->wwwroot/");
+}
+
+$instances = enrol_get_instances($course->id, true);
+$plugins   = enrol_get_plugins(true);
+$inames    = array();
+foreach ($instances as $k=>$i) {
+    if (!isset($plugins[$i->enrol])) {
+        // weird, some broken stuff in plugin
+        unset($instances[$k]);
+        continue;
+    }
+    $inames[$k] = $plugins[$i->enrol]->get_instance_name($i);
+}
+
+// validate paging params
+if ($ifilter != 0 and !isset($instances[$ifilter])) {
+    $ifilter = 0;
+}
+if ($perpage < 3) {
+    $perpage = 3;
+}
+if ($page < 0) {
+    $page = 0;
+}
+if (!in_array($dir, array('ASC', 'DESC'))) {
+    $dir = 'ASC';
+}
+if (!in_array($sort, array('firstname', 'lastname', 'email', 'lastseen'))) {
+    $dir = 'lastname';
+}
+
+$PAGE->set_url('/enrol/notenrolled.php', array('id'=>$course->id, 'page'=>$page, 'sort'=>$sort, 'dir'=>$dir, 'perpage'=>$perpage, 'ifilter'=>$ifilter));
+$PAGE->set_pagelayout('admin');
+
+//lalala- nav hack
+navigation_node::override_active_url(new moodle_url('/enrol/otherusers.php', array('id'=>$course->id)));
+
+echo $OUTPUT->header();
+
+//TODO: MDL-22854 add some new role related UI for users that are not enrolled but still got a role somehow in this course context
+
+notify('This page is not implemented yet, sorry. See MDL-21782 in our tracker for more information.');
+
+echo $OUTPUT->single_button(new moodle_url('/admin/roles/assign.php', array('contextid'=>$context->id)), 'Continue to old Assign roles UI');
+
+echo $OUTPUT->footer();
diff --git a/enrol/paypal/enrol.php b/enrol/paypal/enrol.php
index 0be07cbb57a7..67c9f0383fad 100644
--- a/enrol/paypal/enrol.php
+++ b/enrol/paypal/enrol.php
@@ -1,9 +1,6 @@
 <?php
        // Implements all the main code for the PayPal plugin
 
-require_once("$CFG->dirroot/enrol/enrol.class.php");
-
-
 class enrolment_plugin_paypal {
 
 
diff --git a/enrol/paypal/ipn.php b/enrol/paypal/ipn.php
index 33a5153b0fbd..4bab56760208 100644
--- a/enrol/paypal/ipn.php
+++ b/enrol/paypal/ipn.php
@@ -93,7 +93,7 @@
             // and notify admin
 
             if ($data->payment_status != "Completed" and $data->payment_status != "Pending") {
-                role_unassign(0, $data->userid, 0, $context->id);
+                role_unassign_all(array('userid'=>$data->userid, 'contextid'=>$context->id, 'component'=>'enrol_paypal'), true, true);
                 message_paypal_error_to_admin("Status not completed or pending. User unenrolled from course", $data);
                 die;
             }
diff --git a/enrol/paypal/return.php b/enrol/paypal/return.php
index 80c207280f24..6d2f4ce6db3f 100644
--- a/enrol/paypal/return.php
+++ b/enrol/paypal/return.php
@@ -25,7 +25,7 @@
         $destination = "$CFG->wwwroot/course/view.php?id=$course->id";
     }
 
-    if (has_capability('moodle/course:participate', $context)) {
+    if (is_enrolled($context, NULL, '', true)) { // TODO: use real paypal check
         redirect($destination, get_string('paymentthanks', '', $course->fullname));
 
     } else {   /// Somehow they aren't enrolled yet!  :-(
diff --git a/enrol/self/addinstance.php b/enrol/self/addinstance.php
new file mode 100644
index 000000000000..c5ce3fab3a68
--- /dev/null
+++ b/enrol/self/addinstance.php
@@ -0,0 +1,43 @@
+<?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/>.
+
+/**
+ * Adds new instance of enrol_self to specified course.
+ *
+ * @package   enrol_self
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+
+$id = required_param('id', PARAM_INT); // course id
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('moodle/course:enrolconfig', $context);
+require_sesskey();
+
+$enrol = enrol_get_plugin('self');
+
+if ($enrol->get_candidate_link($course->id)) {
+    $enrol->add_default_instance($course);
+}
+
+redirect(new moodle_url('/enrol/instances.php', array('id'=>$course->id)));
diff --git a/enrol/self/db/access.php b/enrol/self/db/access.php
new file mode 100644
index 000000000000..8703f9a9580a
--- /dev/null
+++ b/enrol/self/db/access.php
@@ -0,0 +1,52 @@
+<?php
+//
+// Capability definitions for the enrol_self plugin.
+//
+// The capabilities are loaded into the database table when the module is
+// installed or updated. Whenever the capability definitions are updated,
+// the module version number should be bumped up.
+//
+
+
+$capabilities = array(
+
+    'enrol/self:config' => array(
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+    'enrol/self:manage' => array(
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+    'enrol/self:unenrolself' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'student' => CAP_ALLOW,
+        )
+    ),
+
+    'enrol/self:unenrol' => array(
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'legacy' => array(
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+);
+
+
diff --git a/enrol/self/db/install.php b/enrol/self/db/install.php
new file mode 100644
index 000000000000..99f50676405d
--- /dev/null
+++ b/enrol/self/db/install.php
@@ -0,0 +1,10 @@
+<?php
+
+function xmldb_enrol_self_install() {
+    global $CFG;
+
+    if (isset($CFG->sendcoursewelcomemessage)) {
+        set_config('sendcoursewelcomemessage', $CFG->sendcoursewelcomemessage, 'enrol_self');
+        unset_config('sendcoursewelcomemessage');
+    }
+}
diff --git a/enrol/self/lang/en/enrol_self.php b/enrol/self/lang/en/enrol_self.php
new file mode 100644
index 000000000000..c645aaf2fb7f
--- /dev/null
+++ b/enrol/self/lang/en/enrol_self.php
@@ -0,0 +1,63 @@
+<?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/>.
+
+/**
+ * Strings for component 'enrol_self', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package   enrol_self
+ * @copyright 2010 onwards Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['defaultrole'] = 'Default role assignment';
+$string['defaultrole_desc'] = 'Select role which should be assigned to users during self enrolment';
+$string['enrolenddate'] = 'End date';
+$string['enrolenddaterror'] = 'Enrolment end date cannot be earlier than start date';
+$string['enrolme'] = 'Enrol me';
+$string['enrolperiod'] = 'Enrolment period';
+$string['enrolperiod_desc'] = 'Default length of the enrolment period (in seconds).'; //TODO: fixme
+$string['enrolstartdate'] = 'Start date';
+$string['groupkey'] = 'Use group enrolment keys';
+$string['groupkey_desc'] = 'Use group enrolment keys by default.';
+$string['password'] = 'Enrolment key';
+$string['passwordinvalid'] = 'Incorrect enrolment key, please try again';
+$string['passwordinvalidhint'] = 'That enrolment key was incorrect, please try again<br />
+(Here\'s a hint - it starts with \'{$a}\')';
+$string['pluginname'] = 'Self enrolment';
+$string['pluginname_desc'] = 'The self enrolment plugin allows users to choose which courses they want to participate in. The courses may be protected by an enrolment key. Internally the enrolment is done via the manual enrolmnet plugin which has to be enabled in the same course.';
+$string['requirepassword'] = 'Require enrolment key';
+$string['requirepassword_desc'] = 'Require enrolment key in new courses and prevent prevent removing of enrolment key from existing courses.';
+$string['role'] = 'Assign role';
+$string['self:config'] = 'Configure self enrol instances';
+$string['self:manage'] = 'Manage enrolled users';
+$string['self:unenrol'] = 'Unenrol users from course';
+$string['self:unenrolself'] = 'Unenrol self from the course';
+$string['sendcoursewelcomemessage'] = 'Send course welcome message';
+$string['sendcoursewelcomemessage_desc'] = 'If enabled, users receive a welcome message via email when they self-enrol in a course.';
+$string['showhint'] = 'Show hint';
+$string['showhint_desc'] = 'Show first letter of the guest access key.';
+$string['status'] = 'Allow self enrolments';
+$string['status_desc'] = 'Allow users to self enrol into course by default.';
+$string['unenrolselfconfirm'] = 'Do you really want to unenrol yourself from course "{$a}"?';
+$string['usepasswordpolicy'] = 'Use password policy';
+$string['usepasswordpolicy_desc'] = 'Use standard password policy for enrolment keys.';
+$string['welcometocourse'] = 'Welcome to {$a}';
+$string['welcometocoursetext'] = 'Welcome to {$a->coursename}!
+
+If you have not done so already, you should edit your profile page so that we can learn more about you:
+
+  {$a->profileurl}';
diff --git a/enrol/self/lib.php b/enrol/self/lib.php
new file mode 100644
index 000000000000..4383537fbaf3
--- /dev/null
+++ b/enrol/self/lib.php
@@ -0,0 +1,433 @@
+<?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/>.
+
+/**
+ * Self enrolment plugin.
+ *
+ * @package   enrol_self
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/**
+ * Self enrolment plugin implementation.
+ * @author Petr Skoda
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class enrol_self_plugin extends enrol_plugin {
+
+    /**
+     * Returns localised name of enrol instance
+     *
+     * @param object $instance (null is accepted too)
+     * @return string
+     */
+    public function get_instance_name($instance) {
+        global $DB;
+
+        if (empty($instance->name)) {
+            if (!empty($instance->roleid) and $role = $DB->get_record('role', array('id'=>$instance->roleid))) {
+                $role = ' (' . role_get_name($role, get_context_instance(CONTEXT_COURSE, $instance->courseid)) . ')';
+            } else {
+                $role = '';
+            }
+            $enrol = $this->get_name();
+            return get_string('pluginname', 'enrol_'.$enrol) . $role;
+        } else {
+            return format_string($instance->name);
+        }
+    }
+
+    public function roles_protected() {
+        // users may tweak the roles later
+        return false;
+    }
+
+    public function allow_unenrol(stdClass $instance) {
+        // users with unenrol cap may unenrol other users manually manually
+        return true;
+    }
+
+    public function allow_manage(stdClass $instance) {
+        // users with manage cap may tweak period and status
+        return true;
+    }
+
+    /**
+     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * @param int $courseid
+     * @return moodle_url page url
+     */
+    public function get_candidate_link($courseid) {
+        if (!has_capability('moodle/course:enrolconfig', get_context_instance(CONTEXT_COURSE, $courseid, MUST_EXIST))) {
+            return NULL;
+        }
+        // multiple instances supported - different roles with different password
+        return new moodle_url('/enrol/self/addinstance.php', array('sesskey'=>sesskey(), 'id'=>$courseid));
+    }
+
+    /**
+     * Creates course enrol form, checks if form submitted
+     * and enrols user if necessary. It can also redirect.
+     *
+     * @param stdClass $instance
+     * @return string html text, usually a form in a text box
+     */
+    public function enrol_page_hook(stdClass $instance) {
+        global $CFG, $OUTPUT, $SESSION, $USER, $DB;
+
+        if (isguestuser()) {
+            // can not enrol guest!!
+            return null;
+        }
+        if ($DB->record_exists('user_enrolments', array('userid'=>$USER->id, 'enrolid'=>$instance->id))) {
+            //TODO: maybe we should tell them they are already enrolled, but can not access the course
+            return null;
+        }
+
+        if ($instance->enrolstartdate != 0 and $instance->enrolstartdate < time) {
+            //TODO: inform that we can not enrol yet
+            return null;
+        }
+
+        if ($instance->enrolenddate != 0 and $instance->enrolenddate > time) {
+            //TODO: inform that enrolment is not possible any more
+            return null;
+        }
+
+        require_once("$CFG->dirroot/enrol/self/locallib.php");
+        $form = new enrol_self_enrol_form(NULL, $instance);
+        $instanceid = optional_param('instance', 0, PARAM_INT);
+
+        if ($instance->id == $instanceid) {
+            if ($data = $form->get_data()) {
+                $enrol = enrol_get_plugin('self');
+                if ($instance->enrolperiod) {
+                    $timestart = time();
+                    $tineend   = $timestart + $instance->enrolperiod;
+                } else {
+                    $timestart = 0;
+                    $tineend   = 0;
+                }
+
+                $this->enrol_user($instance, $USER->id, $instance->roleid, $timestart, $tineend);
+                add_to_log($instance->courseid, 'course', 'enrol', '../enrol/users.php?id='.$instance->courseid, $instance->courseid); //there should be userid somewhere!
+                // send welcome
+                if ($this->get_config('sendcoursewelcomemessage')) {
+                    $this->email_welcome_message($instance, $USER);
+                }
+            }
+        }
+
+        ob_start();
+        $form->display();
+        $output = ob_get_clean();
+
+        return $OUTPUT->box($output);
+    }
+
+    /**
+     * Adds enrol instance UI to course edit form
+     *
+     * @param object $instance enrol instance or null if does not exist yet
+     * @param MoodleQuickForm $mform
+     * @param object $data
+     * @param object $context context of existing course or parent category if course does not exist
+     * @return void
+     */
+    public function course_edit_form($instance, MoodleQuickForm $mform, $data, $context) {
+
+        $i = isset($instance->id) ? $instance->id : 0;
+        $plugin = enrol_get_plugin('self');
+        $header = $plugin->get_instance_name($instance);
+        $config = has_capability('enrol/self:config', $context);
+
+        $mform->addElement('header', 'enrol_self_header_'.$i, $header);
+
+
+        $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                         ENROL_INSTANCE_DISABLED => get_string('no'));
+        $mform->addElement('select', 'enrol_self_status_'.$i, get_string('status', 'enrol_self'), $options);
+        $mform->setDefault('enrol_self_status_'.$i, $this->get_config('status'));
+        $mform->setAdvanced('enrol_self_status_'.$i, $this->get_config('status_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_self_status_'.$i);
+        }
+
+
+        $mform->addElement('passwordunmask', 'enrol_self_password_'.$i, get_string('password', 'enrol_self'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_self_password_'.$i);
+        } else {
+            $mform->disabledIf('enrol_self_password_'.$i, 'enrol_self_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        $options = array(1 => get_string('yes'),
+                         0 => get_string('no'));
+        $mform->addElement('select', 'enrol_self_customint1_'.$i, get_string('groupkey', 'enrol_self'), $options);
+        $mform->setDefault('enrol_self_customint1_'.$i, $this->get_config('groupkey'));
+        $mform->setAdvanced('enrol_self_customint1_'.$i, $this->get_config('groupkey_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_self_customint1_'.$i);
+        } else {
+            $mform->disabledIf('enrol_self_customint1_'.$i, 'enrol_self_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        if ($instance) {
+            $roles = get_default_enrol_roles($context, $instance->roleid);
+        } else {
+            $roles = get_default_enrol_roles($context, $this->get_config('roleid'));
+        }
+        $mform->addElement('select', 'enrol_self_roleid_'.$i, get_string('role', 'enrol_self'), $roles);
+        $mform->setDefault('enrol_self_roleid_'.$i, $this->get_config('roleid'));
+        $mform->setAdvanced('enrol_self_roleid_'.$i, $this->get_config('roleid_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_self_roleid_'.$i);
+        } else {
+            $mform->disabledIf('enrol_self_roleid_'.$i, 'enrol_self_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        $mform->addElement('duration', 'enrol_self_enrolperiod_'.$i, get_string('enrolperiod', 'enrol_self'), array('optional' => true, 'defaultunit' => 86400));
+        $mform->setDefault('enrol_self_enrolperiod_'.$i, $this->get_config('enrolperiod'));
+        $mform->setAdvanced('enrol_self_enrolperiod_'.$i, $this->get_config('enrolperiod_adv'));
+        if (!$config) {
+            $mform->hardFreeze('enrol_self_enrolperiod_'.$i);
+        } else {
+            $mform->disabledIf('enrol_self_enrolperiod_'.$i, 'enrol_self_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        $mform->addElement('date_selector', 'enrol_self_enrolstartdate_'.$i, get_string('enrolstartdate', 'enrol_self'), array('optional' => true));
+        $mform->setDefault('enrol_self_enrolstartdate_'.$i, 0);
+        $mform->setAdvanced('enrol_self_enrolstartdate_'.$i, 1);
+        if (!$config) {
+            $mform->hardFreeze('enrol_self_enrolstartdate_'.$i);
+        } else {
+            $mform->disabledIf('enrol_self_enrolstartdate_'.$i, 'enrol_self_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        $mform->addElement('date_selector', 'enrol_self_enrolenddate_'.$i, get_string('enrolenddate', 'enrol_self'), array('optional' => true));
+        $mform->setDefault('enrol_self_enrolenddate_'.$i, 0);
+        $mform->setAdvanced('enrol_self_enrolenddate_'.$i, 1);
+        if (!$config) {
+            $mform->hardFreeze('enrol_self_enrolenddate_'.$i);
+        } else {
+            $mform->disabledIf('enrol_self_enrolenddate_'.$i, 'enrol_self_status_'.$i, 'noteq', ENROL_INSTANCE_ENABLED);
+        }
+
+
+        // now add all values from enrol table
+        if ($instance) {
+            foreach($instance as $key=>$val) {
+                $data->{'enrol_self_'.$key.'_'.$i} = $val;
+            }
+        }
+    }
+
+
+    /**
+     * Validates course edit form data
+     *
+     * @param object $instance enrol instance or null if does not exist yet
+     * @param array $data
+     * @param object $context context of existing course or parent category if course does not exist
+     * @return array errors array
+     */
+    public function course_edit_validation($instance, array $data, $context) {
+        $errors = array();
+
+        if (!has_capability('enrol/self:config', $context)) {
+            // we are going to ignore the data later anyway, they would not be able to fix the form anyway
+            return $errors;
+        }
+
+        $i = isset($instance->id) ? $instance->id : 0;
+
+        $password = empty($data['enrol_self_password_'.$i]) ? '' : $data['enrol_self_password_'.$i];
+        $checkpassword = false;
+
+        if ($instance) {
+            if ($data['enrol_self_status_'.$i] == ENROL_INSTANCE_ENABLED) {
+                if ($instance->password !== $password) {
+                    $checkpassword = true;
+                }
+            }
+        } else {
+            if ($data['enrol_self_status_'.$i] == ENROL_INSTANCE_ENABLED) {
+                $checkpassword = true;
+            }
+        }
+
+        if ($checkpassword) {
+            $require = $this->get_config('requirepassword');
+            $policy  = $this->get_config('usepasswordpolicy');
+            if ($require and empty($password)) {
+                $errors['enrol_self_password_'.$i] = get_string('required');
+            } else if ($policy) {
+                $errmsg = '';//prevent eclipse warning
+                if (!check_password_policy($password, $errmsg)) {
+                    $errors['enrol_self_password_'.$i] = $errmsg;
+                }
+            }
+        }
+
+        if ($data['enrol_self_status_'.$i] == ENROL_INSTANCE_ENABLED) {
+            if (!empty($data['enrol_self_enrolenddate_'.$i]) and $data['enrol_self_enrolenddate_'.$i] < $data['enrol_self_enrolstartdate_'.$i]) {
+                $errors['enrol_self_enrolenddate_'.$i] = get_string('enrolenddaterror', 'enrol_self');
+            }
+        }
+
+        return $errors;
+    }
+
+
+    /**
+     * Called after updating/inserting course.
+     *
+     * @param bool $inserted true if course just inserted
+     * @param object $course
+     * @param object $data form data
+     * @return void
+     */
+    public function course_updated($inserted, $course, $data) {
+        global $DB;
+
+        $context = get_context_instance(CONTEXT_COURSE, $course->id);
+
+        if (has_capability('enrol/self:config', $context)) {
+            if ($inserted) {
+                if (isset($data->enrol_self_status_0)) {
+                    $fields = array('status'=>$data->enrol_self_status_0);
+                    if ($fields['status'] == ENROL_INSTANCE_ENABLED) {
+                        $fields['password']       = $data->enrol_self_password_0;
+                        $fields['customint1']     = $data->enrol_self_customint1_0;
+                        $fields['roleid']         = $data->enrol_self_roleid_0;
+                        $fields['enrolperiod']    = $data->enrol_self_enrolperiod_0;
+                        $fields['enrolstartdate'] = $data->enrol_self_enrolstartdate_0;
+                        $fields['enrolenddate']   = $data->enrol_self_enrolenddate_0;
+                    } else {
+                        if ($this->get_config('requirepassword')) {
+                            // make sure some password is set after enabling this plugin
+                            $fields['password']   = generate_password(20);
+                        }
+                        $fields['customint1']     = $this->get_config('groupkey');
+                        $fields['roleid']         = $this->get_config('roleid');
+                        $fields['enrolperiod']    = $this->get_config('enrolperiod');
+                        $fields['enrolstartdate'] = 0;
+                        $fields['enrolenddate']   = 0;
+                    }
+                    $this->add_instance($course, $fields);
+                }
+
+            } else {
+                $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'enrol'=>'self'));
+                foreach ($instances as $instance) {
+                    $i = $instance->id;
+
+                    if (isset($data->{'enrol_self_status_'.$i})) {
+                        $instance->status       = $data->{'enrol_self_status_'.$i};
+                        $instance->timemodified = time();
+                        if ($instance->status == ENROL_INSTANCE_ENABLED) {
+                            $instance->password       = $data->{'enrol_self_password_'.$i};
+                            $instance->customint1     = $data->{'enrol_self_customint1_'.$i};
+                            $instance->roleid         = $data->{'enrol_self_roleid_'.$i};
+                            $instance->enrolperiod    = $data->{'enrol_self_enrolperiod_'.$i};
+                            $instance->enrolstartdate = $data->{'enrol_self_enrolstartdate_'.$i};
+                            $instance->enrolenddate   = $data->{'enrol_self_enrolenddate_'.$i};
+                        }
+                        $DB->update_record('enrol', $instance);
+                    }
+                }
+            }
+
+        } else {
+            if ($inserted) {
+                if ($this->get_config('defaultenrol')) {
+                    $this->add_default_instance($course);
+                }
+            } else {
+                // bad luck, user can not change anything
+            }
+        }
+    }
+
+    /**
+     * Add new instance of enrol plugin with default settings.
+     * @param object $course
+     * @return int id of new instance
+     */
+    public function add_default_instance($course) {
+        global $DB;
+
+        $exists = $DB->record_exists('enrol', array('courseid'=>$course->id, 'enrol'=>'self'));
+
+        $fields = array('customint1'=>$this->get_config('groupkey'), 'enrolperiod'=>$this->get_config('enrolperiod', 0), 'roleid'=>$this->get_config('roleid', 0));
+
+        $fields['status'] = $exists ? ENROL_INSTANCE_DISABLED : $this->get_config('status');
+
+        if ($this->get_config('requirepassword')) {
+            $fields['password'] = generate_password(20);
+        }
+
+        return $this->add_instance($course, $fields);
+    }
+
+    /**
+     * Send welcome email to specified user
+     *
+     * @param object $instance
+     * @param object $user user record
+     * @return void
+     */
+    protected function email_welcome_message($instance, $user) {
+        global $CFG, $DB;
+
+        $course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST);
+
+        if (!empty($instance->customtext1)) {
+            //note: there is no gui for this yet, do we really need it?
+            $message = formaat_string($instance->customtext1);
+        } else {
+            $a = new object();
+            $a->coursename = format_string($course->fullname);
+            $a->profileurl = "$CFG->wwwroot/user/view.php?id=$user->id&course=$course->id";
+            $message = get_string("welcometocoursetext", 'enrol_self', $a);
+        }
+
+        $subject = get_string('welcometocourse', 'enrol_self', format_string($course->fullname));
+
+        $context = get_context_instance(CONTEXT_COURSE, $course->id);
+        $rusers = null;
+        if (!empty($CFG->coursecontact)) {
+            $croles = explode(',', $CFG->coursecontact);
+            $rusers = get_role_users($croles, $context, true, '', 'r.sortorder ASC, u.lastname ASC');
+        }
+        if ($rusers) {
+            $contact = reset($rusers);
+        } else {
+            $contact = get_admin();
+        }
+
+        email_to_user($user, $contact, $subject, $message);
+    }
+}
+
+
diff --git a/enrol/self/locallib.php b/enrol/self/locallib.php
new file mode 100644
index 000000000000..20a7da26e48a
--- /dev/null
+++ b/enrol/self/locallib.php
@@ -0,0 +1,84 @@
+<?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/>.
+
+/**
+ * Self enrol plugin implementation.
+ *
+ * @package   enrol_self
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/formslib.php");
+
+class enrol_self_enrol_form extends moodleform {
+    protected $instance;
+
+    public function definition() {
+        $mform = $this->_form;
+        $instance = $this->_customdata;
+        $this->instance = $instance;
+        $plugin = enrol_get_plugin('self');
+
+        if ($instance->password) {
+            $heading = $plugin->get_instance_name($instance);
+            $mform->addElement('header', 'selfheader', $heading);
+            $mform->addElement('passwordunmask', 'enrolpassword', get_string('password', 'enrol_self'));
+        } else {
+            // nothing?
+        }
+
+        $this->add_action_buttons(false, get_string('enrolme', 'enrol_self'));
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+        $mform->setDefault('id', $instance->courseid);
+
+        $mform->addElement('hidden', 'instance');
+        $mform->setType('instance', PARAM_INT);
+        $mform->setDefault('instance', $instance->id);
+    }
+
+    public function validation($data, $files) {
+        global $DB, $CFG;
+
+        $errors = parent::validation($data, $files);
+        $instance = $this->instance;
+
+        if ($instance->password) {
+            if ($data['enrolpassword'] !== $instance->password) {
+                if ($instance->customint1) {
+                    //TODO: check groups
+
+                } else {
+                    $plugin = enrol_get_plugin('self');
+                    if ($plugin->get_config('showhint')) {
+                        $textlib = textlib_get_instance();
+                        $hint = $textlib->substr($instance->password, 0, 1);
+                        $errors['enrolpassword'] = get_string('passwordinvalidhint', 'enrol_self', $hint);
+                    } else {
+                        $errors['enrolpassword'] = get_string('passwordinvalid', 'enrol_self');
+                    }
+                }
+            }
+        }
+
+        return $errors;
+    }
+}
\ No newline at end of file
diff --git a/enrol/self/settings.php b/enrol/self/settings.php
new file mode 100644
index 000000000000..050b2141fcd0
--- /dev/null
+++ b/enrol/self/settings.php
@@ -0,0 +1,79 @@
+<?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/>.
+
+/**
+ * Self enrolment plugin settings and presets.
+ *
+ * @package   enrol_self
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+if ($ADMIN->fulltree) {
+
+    //--- general settings -----------------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_self_settings', '', get_string('pluginname_desc', 'enrol_self')));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_self/requirepassword',
+        get_string('requirepassword', 'enrol_self'), get_string('requirepassword_desc', 'enrol_self'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_self/usepasswordpolicy',
+        get_string('usepasswordpolicy', 'enrol_self'), get_string('usepasswordpolicy_desc', 'enrol_self'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_self/showhint',
+        get_string('showhint', 'enrol_self'), get_string('showhint_desc', 'enrol_self'), 0));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_self/sendcoursewelcomemessage',
+        get_string('sendcoursewelcomemessage', 'enrol_self'), get_string('sendcoursewelcomemessage_desc', 'enrol_self'), 1));
+
+
+    //--- enrol instance defaults ----------------------------------------------------------------------------
+    $settings->add(new admin_setting_heading('enrol_self_defaults',
+        get_string('enrolinstancedefaults', 'admin'), get_string('enrolinstancedefaults_desc', 'admin')));
+
+    $settings->add(new admin_setting_configcheckbox('enrol_self/defaultenrol',
+        get_string('defaultenrol', 'enrol'), get_string('defaultenrol_desc', 'enrol'), 1));
+
+    $options = array(ENROL_INSTANCE_ENABLED  => get_string('yes'),
+                     ENROL_INSTANCE_DISABLED => get_string('no'));
+    $settings->add(new admin_setting_configselect_with_advanced('enrol_self/status',
+        get_string('status', 'enrol_self'), get_string('status_desc', 'enrol_self'),
+        array('value'=>ENROL_INSTANCE_DISABLED, 'adv'=>false), $options));
+
+    $options = array(1  => get_string('yes'),
+                     0 => get_string('no'));
+    $settings->add(new admin_setting_configselect_with_advanced('enrol_self/groupkey',
+        get_string('groupkey', 'enrol_self'), get_string('groupkey_desc', 'enrol_self'),
+        array('value'=>0, 'adv'=>true), $options));
+
+    if (!during_initial_install()) {
+        $options = get_default_enrol_roles(get_context_instance(CONTEXT_SYSTEM));
+        $student = get_archetype_roles('student');
+        $student = reset($student);
+        $settings->add(new admin_setting_configselect_with_advanced('enrol_self/roleid',
+            get_string('defaultrole', 'enrol_self'), get_string('defaultrole_desc', 'enrol_self'),
+            array('value'=>$student->id, 'adv'=>false), $options));
+    }
+
+    $settings->add(new admin_setting_configtext_with_advanced('enrol_self/enrolperiod',
+        get_string('enrolperiod', 'enrol_self'), get_string('enrolperiod_desc', 'enrol_self'),
+        array('value'=>0, 'adv'=>true), PARAM_INT));
+
+
+}
diff --git a/enrol/self/unenrolself.php b/enrol/self/unenrolself.php
new file mode 100644
index 000000000000..3e9ea09c3d01
--- /dev/null
+++ b/enrol/self/unenrolself.php
@@ -0,0 +1,58 @@
+<?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/>.
+
+/**
+ * Self enrolment plugin - support for user self unenrolment.
+ *
+ * @package   enrol_self
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../../config.php');
+
+$enrolid = required_param('enrolid', PARAM_INT);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+$instance = $DB->get_record('enrol', array('id'=>$enrolid, 'enrol'=>'self'), '*', MUST_EXIST);
+$course = $DB->get_record('course', array('id'=>$instance->courseid), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+
+$plugin = enrol_get_plugin('self');
+
+// security defined inside following function
+if (!$plugin->get_unenrolself_link($instance)) {
+    redirect(new moodle_url('/course/view.php', array('id'=>$course->id)));
+}
+
+$PAGE->set_url('/enrol/self/unenrolself.php', array('enrolid'=>$instance->id));
+$PAGE->set_title($plugin->get_instance_name($instance));
+
+if ($confirm and confirm_sesskey()) {
+    $plugin->unenrol_user($instance, $USER->id);
+    add_to_log($course->id, 'course', 'unenrol', '../enrol/users.php?id='.$course->id, $course->id); //there should be userid somewhere!
+    redirect(new moodle_url('/index.php'));
+}
+
+echo $OUTPUT->header();
+$yesurl = new moodle_url($PAGE->url, array('confirm'=>1, 'sesskey'=>sesskey()));
+$nourl = new moodle_url('/course/view.php', array('id'=>$course->id));
+$message = get_string('unenrolselfconfirm', 'enrol_self', format_string($course->fullname));
+echo $OUTPUT->confirm($message, $yesurl, $nourl);
+echo $OUTPUT->footer();
diff --git a/enrol/self/version.php b/enrol/self/version.php
new file mode 100644
index 000000000000..b23cf7ba1dc0
--- /dev/null
+++ b/enrol/self/version.php
@@ -0,0 +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/>.
+
+/**
+ * Self enrolment plugin version specification.
+ *
+ * @package   enrol_self
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$plugin->version = 2010061600;
diff --git a/enrol/users.php b/enrol/users.php
new file mode 100644
index 000000000000..4d27d7919588
--- /dev/null
+++ b/enrol/users.php
@@ -0,0 +1,518 @@
+<?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/>.
+
+/**
+ * Main course enrolment management UI, this is not compatible with frontpage course.
+ *
+ * @package    core
+ * @subpackage enrol
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+require('../config.php');
+require_once("$CFG->dirroot/enrol/users_forms.php");
+require_once("$CFG->dirroot/group/lib.php");
+
+$id      = required_param('id', PARAM_INT); // course id
+$action  = optional_param('action', '', PARAM_ACTION);
+$confirm = optional_param('confirm', 0, PARAM_BOOL);
+
+$ifilter = optional_param('ifilter', 0, PARAM_INT); // only one instance
+$page    = optional_param('page', 0, PARAM_INT);
+$perpage = optional_param('perpage', 20, PARAM_INT);
+$sort    = optional_param('sort', 'lastname', PARAM_ALPHA);
+$dir     = optional_param('dir', 'ASC', PARAM_ALPHA);
+
+
+$course = $DB->get_record('course', array('id'=>$id), '*', MUST_EXIST);
+$context = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+
+require_login($course);
+require_capability('moodle/course:enrolreview', $context);
+
+if ($course->id == SITEID) {
+    redirect("$CFG->wwwroot/");
+}
+
+$managegroups = has_capability('moodle/course:managegroups', $context);
+$instances = enrol_get_instances($course->id, true);
+$plugins   = enrol_get_plugins(true);
+$inames    = array();
+foreach ($instances as $k=>$i) {
+    if (!isset($plugins[$i->enrol])) {
+        // weird, some broken stuff in plugin
+        unset($instances[$k]);
+        continue;
+    }
+    $inames[$k] = $plugins[$i->enrol]->get_instance_name($i);
+}
+
+// validate paging params
+if ($ifilter != 0 and !isset($instances[$ifilter])) {
+    $ifilter = 0;
+}
+if ($perpage < 3) {
+    $perpage = 3;
+}
+if ($page < 0) {
+    $page = 0;
+}
+if (!in_array($dir, array('ASC', 'DESC'))) {
+    $dir = 'ASC';
+}
+if (!in_array($sort, array('firstname', 'lastname', 'email', 'lastseen'))) {
+    $dir = 'lastname';
+}
+
+$PAGE->set_url('/enrol/users.php', array('id'=>$course->id, 'page'=>$page, 'sort'=>$sort, 'dir'=>$dir, 'perpage'=>$perpage, 'ifilter'=>$ifilter));
+$PAGE->set_pagelayout('admin');
+
+//lalala- nav hack
+navigation_node::override_active_url(new moodle_url('/enrol/users.php', array('id'=>$course->id)));
+
+
+$allroles   = get_all_roles();
+$allroles   = role_fix_names($allroles, $context);
+$assignable = get_assignable_roles($context, ROLENAME_ALIAS, false); // verifies unassign access control too
+$allgroups  = groups_get_all_groups($course->id);
+foreach ($allgroups as $gid=>$group) {
+    $allgroups[$gid]->name = format_string($group->name);
+}
+
+if ($action) {
+    switch ($action) {
+        case 'unenrol':
+            $ue = required_param('ue', PARAM_INT);
+            if (!$ue = $DB->get_record('user_enrolments', array('id'=>$ue))) {
+                break;
+            }
+            $user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
+            if (!isset($instances[$ue->enrolid])) {
+                break;
+            }
+            $instance = $instances[$ue->enrolid];
+            $plugin = $plugins[$instance->enrol];
+            if (!$plugin->allow_unenrol($instance) or !has_capability("enrol/$instance->enrol:unenrol", $context)) {
+                break;
+            }
+
+            if ($confirm and confirm_sesskey()) {
+                $plugin->unenrol_user($instance, $ue->userid);
+                redirect($PAGE->url);
+
+            } else {
+                $yesurl = new moodle_url($PAGE->url, array('action'=>'unenrol', 'ue'=>$ue->id, 'confirm'=>1, 'sesskey'=>sesskey()));
+                $message = get_string('unenrolconfirm', 'enrol', array('user'=>fullname($user, true), 'course'=>format_string($course->fullname)));
+                $PAGE->set_title(get_string('unenrol', 'enrol'));
+                echo $OUTPUT->header();
+                echo $OUTPUT->heading(get_string('unenrol', 'enrol'));
+                echo $OUTPUT->confirm($message, $yesurl, $PAGE->url);
+                echo $OUTPUT->footer();
+                die;
+            }
+            break;
+
+        case 'unassign':
+            $role = required_param('role', PARAM_INT);
+            $user = required_param('user', PARAM_INT);
+            if (!isset($assignable[$role])) {
+                break;
+            }
+            $role = $allroles[$role];
+            $user = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
+
+            if ($confirm and confirm_sesskey()) {
+                role_unassign($role->id, $user->id, $context->id, '', NULL);
+                redirect($PAGE->url);
+
+            } else {
+                $yesurl = new moodle_url($PAGE->url, array('action'=>'unassign', 'role'=>$role->id, 'user'=>$user->id, 'confirm'=>1, 'sesskey'=>sesskey()));
+                $message = get_string('unassignconfirm', 'role', array('user'=>fullname($user, true), 'role'=>$role->localname));
+                $PAGE->set_title(get_string('unassignarole', 'role', $role->localname));
+                echo $OUTPUT->header();
+                echo $OUTPUT->heading(get_string('unassignarole', 'role', $role->localname));
+                echo $OUTPUT->confirm($message, $yesurl, $PAGE->url);
+                echo $OUTPUT->footer();
+                die;
+            }
+            break;
+
+        case 'assign':
+            $user = required_param('user', PARAM_INT);
+            $user = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
+
+            if (!is_enrolled($context, $user)) {
+                break; // no roles without enrolments here in this script
+            }
+
+            $mform = new enrol_users_assign_form(NULL, array('user'=>$user, 'course'=>$course, 'assignable'=>$assignable));
+
+            if ($mform->is_cancelled()) {
+                redirect($PAGE->url);
+
+            } else if ($data = $mform->get_data()) {
+                if ($data->roleid) {
+                    role_assign($data->roleid, $user->id, $context->id, '', NULL);
+                }
+                redirect($PAGE->url);
+            }
+
+            $PAGE->set_title(get_string('assignroles', 'role'));
+            echo $OUTPUT->header();
+            echo $OUTPUT->heading(get_string('assignroles', 'role'));
+            $mform->display();
+            echo $OUTPUT->footer();
+            die;
+
+        case 'removemember':
+            $group = required_param('group', PARAM_INT);
+            $user  = required_param('user', PARAM_INT);
+            if (!$managegroups) {
+                break;
+            }
+            if (!isset($allgroups[$group])) {
+                break;
+            }
+            $group = $allgroups[$group];
+            $user  = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
+
+            if ($confirm and confirm_sesskey()) {
+                groups_remove_member($group, $user);
+                redirect($PAGE->url);
+
+            } else {
+                $yesurl = new moodle_url($PAGE->url, array('action'=>'removemember', 'group'=>$group->id, 'user'=>$user->id, 'confirm'=>1, 'sesskey'=>sesskey()));
+                $message = get_string('removefromgroupconfirm', 'group', array('user'=>fullname($user, true), 'group'=>$group->name));
+                $PAGE->set_title(get_string('removefromgroup', 'group', $group->name));
+                echo $OUTPUT->header();
+                echo $OUTPUT->heading(get_string('removefromgroup', 'group', $group->name));
+                echo $OUTPUT->confirm($message, $yesurl, $PAGE->url);
+                echo $OUTPUT->footer();
+                die;
+            }
+            break;
+
+        case 'addmember':
+            $user = required_param('user', PARAM_INT);
+            $user = $DB->get_record('user', array('id'=>$user), '*', MUST_EXIST);
+
+            if (!$managegroups) {
+                break;
+            }
+            if (!is_enrolled($context, $user)) {
+                break; // no roles without enrolments here in this script
+            }
+
+            $mform = new enrol_users_addmember_form(NULL, array('user'=>$user, 'course'=>$course, 'allgroups'=>$allgroups));
+
+            if ($mform->is_cancelled()) {
+                redirect($PAGE->url);
+
+            } else if ($data = $mform->get_data()) {
+                if ($data->groupid) {
+                    groups_add_member($data->groupid, $user->id);
+                }
+                redirect($PAGE->url);
+            }
+
+            $PAGE->set_title(get_string('addgroup', 'group'));
+            echo $OUTPUT->header();
+            echo $OUTPUT->heading(get_string('addgroup', 'group'));
+            $mform->display();
+            echo $OUTPUT->footer();
+            die;
+
+        case 'edit':
+            $ue = required_param('ue', PARAM_INT);
+            if (!$ue = $DB->get_record('user_enrolments', array('id'=>$ue))) {
+                break;
+            }
+            $user = $DB->get_record('user', array('id'=>$ue->userid), '*', MUST_EXIST);
+            if (!isset($instances[$ue->enrolid])) {
+                break;
+            }
+            $instance = $instances[$ue->enrolid];
+            $plugin = $plugins[$instance->enrol];
+            if (!$plugin->allow_unenrol($instance) or !has_capability("enrol/$instance->enrol:unenrol", $context)) {
+                break;
+            }
+
+            $mform = new enrol_users_edit_form(NULL, array('user'=>$user, 'course'=>$course, 'ue'=>$ue));
+
+            if ($mform->is_cancelled()) {
+                redirect($PAGE->url);
+
+            } else if ($data = $mform->get_data()) {
+                if (!isset($data->status)) {
+                    $status = $ue->status;
+                }
+                $plugin->update_user_enrol($instance, $ue->userid, $data->status, $data->timestart, $data->timeend);
+                redirect($PAGE->url);
+            }
+
+            $PAGE->set_title(fullname($user));
+            echo $OUTPUT->header();
+            echo $OUTPUT->heading(fullname($user));
+            $mform->display();
+            echo $OUTPUT->footer();
+            die;
+    }
+}
+
+
+echo $OUTPUT->header();
+echo $OUTPUT->heading(get_string('enrolledusers', 'enrol'));
+$PAGE->set_title(get_string('enrolledusers', 'enrol'));
+
+if ($ifilter) {
+    $instancessql = " = :ifilter";
+    $params = array('ifilter'=>$ifilter);
+} else {
+    if ($instances) {
+        list($instancessql, $params) = $DB->get_in_or_equal(array_keys($instances), SQL_PARAMS_NAMED);
+    } else {
+        // no enabled instances, oops, we should probably say something
+        $instancessql = "= :never";
+        $params = array('never'=>-1);
+    }
+}
+
+$sqltotal = "SELECT COUNT(DISTINCT u.id)
+               FROM {user} u
+               JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
+               JOIN {enrol} e ON (e.id = ue.enrolid)";
+$totalusers = $DB->count_records_sql($sqltotal, $params);
+
+$sql = "SELECT DISTINCT u.*, ul.timeaccess AS lastseen
+          FROM {user} u
+          JOIN {user_enrolments} ue ON (ue.userid = u.id  AND ue.enrolid $instancessql)
+          JOIN {enrol} e ON (e.id = ue.enrolid)
+     LEFT JOIN {user_lastaccess} ul ON (ul.courseid = e.courseid AND ul.userid = u.id)";
+if ($sort === 'firstname') {
+    $sql .= " ORDER BY u.firstname $dir, u.lastname $dir";
+} else if ($sort === 'lastname') {
+    $sql .= " ORDER BY u.lastname $dir, u.firstname $dir";
+} else if ($sort === 'email') {
+    $sql .= " ORDER BY u.email $dir, u.lastname $dir, u.firstname $dir";
+} else if ($sort === 'lastseen') {
+    $sql .= " ORDER BY ul.timeaccess $dir, u.lastname $dir, u.firstname $dir";
+}
+
+$pagingbar = new paging_bar($totalusers, $page, $perpage, $PAGE->url, 'page');
+
+$users = $DB->get_records_sql($sql, $params, $page*$perpage, $perpage);
+
+$strfirstname = get_string('firstname');
+$strlastname  = get_string('lastname');
+$stremail     = get_string('email');
+$strlastseen  = get_string('lastaccess');
+
+if ($dir === 'ASC') {
+    $diricon = html_writer::empty_tag('img', array('alt'=>'', 'src'=>$OUTPUT->pix_url('t/down')));
+    $newdir = 'DESC';
+} else {
+    $diricon  = html_writer::empty_tag('img', array('alt'=>'', 'src'=>$OUTPUT->pix_url('t/up')));
+    $newdir = 'ASC';
+}
+
+$table = new html_table();
+$table->head = array();
+if ($sort === 'firstname') {
+    $h = html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $strfirstname);
+    $h .= " $diricon / ";
+    $h .= html_writer::link(new moodle_url($PAGE->url, array('sort'=>'lastname')), $strlastname);
+} else if ($sort === 'lastname') {
+    $newdir = ($dir === 'ASC') ? 'DESC' : 'ASC';
+    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'firstname')), $strfirstname);
+    $h .= " / ";
+    $h .= html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $strlastname);
+    $h .= " $diricon";
+} else {
+    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'firstname')), $strfirstname);
+    $h .= " / ";
+    $h .= html_writer::link(new moodle_url($PAGE->url, array('sort'=>'lastname')), $strlastname);
+}
+$table->head[] = $h;
+if ($sort === 'email') {
+    $h = html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $stremail);
+    $h .= " $diricon";
+} else {
+    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'email')), $stremail);
+}
+$table->head[] = $h;
+if ($sort === 'lastseen') {
+    $h = html_writer::link(new moodle_url($PAGE->url, array('dir'=>$newdir)), $strlastseen);
+    $h .= " $diricon";
+} else {
+    $h = html_writer::link(new moodle_url($PAGE->url, array('sort'=>'lastseen')), $strlastseen);
+}
+$table->head[] = $h;
+$table->head[] = get_string('roles', 'role');
+$table->head[] = get_string('groups', 'group');
+$table->head[] = get_string('enrolmentinstances', 'enrol');
+
+$table->align = array('left', 'left', 'left', 'left', 'left', 'left');
+$table->width = "95%";
+foreach ($users as $user) {
+    $picture = $OUTPUT->user_picture($user, array('courseid'=>$course->id));
+
+    if ($user->lastseen) {
+        $strlastaccess = format_time(time() - $user->lastaccess);
+    } else {
+        $strlastaccess = get_string('never');
+    }
+    $fullname = fullname($user, true);
+
+    // get list of roles
+    $roles = array();
+    $ras = get_user_roles($context, $user->id, true, 'c.contextlevel DESC, r.sortorder ASC');
+    foreach ($ras as $ra) {
+        if ($ra->contextid != $context->id) {
+            if (!isset($roles[$ra->roleid])) {
+                $roles[$ra->roleid] = null;
+            }
+            // higher ras, course always takes precedence
+            continue;
+        }
+        if (isset($roles[$ra->roleid]) and $roles[$ra->roleid] === false) {
+            continue;
+        }
+        $roles[$ra->roleid] = ($ra->itemid == 0 and $ra->component === '');
+    }
+    foreach ($roles as $rid=>$unassignable) {
+        if ($unassignable and isset($assignable[$rid])) {
+            $icon = html_writer::empty_tag('img', array('alt'=>get_string('unassignarole', 'role', $allroles[$rid]->localname), 'src'=>$OUTPUT->pix_url('t/delete')));
+            $url = new moodle_url($PAGE->url, array('action'=>'unassign', 'role'=>$rid, 'user'=>$user->id));
+            $roles[$rid] = $allroles[$rid]->localname . html_writer::link($url, $icon);
+        } else {
+            $roles[$rid] = $allroles[$rid]->localname;
+        }
+    }
+    $addrole = '';
+    if ($assignable) {
+        foreach ($assignable as $rid=>$unused) {
+            if (!isset($roles[$rid])) {
+                //candidate for role assignment
+                $icon = html_writer::empty_tag('img', array('alt'=>get_string('assignroles', 'role'), 'src'=>$OUTPUT->pix_url('t/add')));
+                $url = new moodle_url($PAGE->url, array('action'=>'assign', 'user'=>$user->id));
+                $addrole .= html_writer::link($url, $icon);
+                break;
+            }
+        }
+    }
+    $roles = implode(', ', $roles);
+    if ($addrole) {
+        $roles = $roles . '<div>'.$addrole.'</div>';
+    }
+
+    $groups = array();
+    $usergroups = groups_get_all_groups($course->id, $user->id, 0, 'g.id');
+    foreach($usergroups as $gid=>$unused) {
+        $group = $allgroups[$gid];
+        if ($managegroups) {
+            $icon = html_writer::empty_tag('img', array('alt'=>get_string('removefromgroup', 'group', $group->name), 'src'=>$OUTPUT->pix_url('t/delete')));
+            $url = new moodle_url($PAGE->url, array('action'=>'removemember', 'group'=>$gid, 'user'=>$user->id));
+            $groups[] = $group->name . html_writer::link($url, $icon);
+        } else {
+            $groups[] = $group->name;
+        }
+    }
+    $groups = implode(', ', $groups);
+    if ($managegroups and (count($usergroups) < count($allgroups))) {
+        $icon = html_writer::empty_tag('img', array('alt'=>get_string('addgroup', 'group'), 'src'=>$OUTPUT->pix_url('t/add')));
+        $url = new moodle_url($PAGE->url, array('action'=>'addmember', 'user'=>$user->id));
+        $groups .= '<div>'.html_writer::link($url, $icon).'</div>';
+    }
+
+
+    // get list of enrol instances
+    $now = time();
+    $edits = array();
+    $params['userid'] = $user->id;
+    $ues = $DB->get_records_select('user_enrolments', "enrolid $instancessql AND userid = :userid", $params);
+    foreach ($ues as $ue) {
+        $instance = $instances[$ue->enrolid];
+        $plugin   = $plugins[$instance->enrol];
+
+        $edit = $inames[$instance->id];
+
+        $dimmed = false;
+        if ($ue->timestart and $ue->timeend) {
+            $edit .= '&nbsp;('.get_string('periodstartend', 'enrol', array('start'=>userdate($ue->timestart), 'end'=>userdate($ue->timeend))).')';
+            $dimmed = ($now < $ue->timestart and $now > $ue->timeend);
+        } else if ($ue->timestart) {
+            $edit .= '&nbsp;('.get_string('periodstart', 'enrol', userdate($ue->timestart)).')';
+            $dimmed = ($now < $ue->timestart);
+        } else if ($ue->timeend) {
+            $edit .= '&nbsp;('.get_string('periodend', 'enrol', userdate($ue->timeend)).')';
+            $dimmed = ($now > $ue->timeend);
+        }
+
+        if ($dimmed or $ue->status != ENROL_USER_ACTIVE) {
+            $edit = html_writer::tag('span', $edit, array('class'=>'dimmed_text'));
+        }
+
+        if ($plugin->allow_unenrol($instance) and has_capability("enrol/$instance->enrol:unenrol", $context)) {
+            $icon = html_writer::empty_tag('img', array('alt'=>get_string('unenrol', 'enrol'), 'src'=>$OUTPUT->pix_url('t/delete')));
+            $url = new moodle_url($PAGE->url, array('action'=>'unenrol', 'ue'=>$ue->id));
+            $edit .= html_writer::link($url, $icon);
+        }
+
+        if ($plugin->allow_manage($instance) and has_capability("enrol/$instance->enrol:manage", $context)) {
+            $icon = html_writer::empty_tag('img', array('alt'=>get_string('edit'), 'src'=>$OUTPUT->pix_url('t/edit')));
+            $url = new moodle_url($PAGE->url, array('action'=>'edit', 'ue'=>$ue->id));
+            $edit .= html_writer::link($url, $icon);
+        }
+
+        $edits[] = $edit;
+    }
+    $edits = implode('<br />', $edits);
+
+    $table->data[] = array("$picture <a href=\"$CFG->wwwroot/user/view.php?id=$user->id&amp;course=$course->id\">$fullname</a>", $user->email, $strlastaccess, $roles, $groups, $edits);
+}
+
+$ifilters = new single_select($PAGE->url, 'ifilter', array(0=>get_string('all')) + $inames, $ifilter, array());
+$ifilters->set_label(get_string('enrolmentinstances', 'enrol'));
+
+echo $OUTPUT->render($ifilters);
+echo $OUTPUT->render($pagingbar);
+echo html_writer::table($table);
+echo $OUTPUT->render($pagingbar);
+
+// print enrol link or selection
+$links = array();
+foreach($instances as $instance) {
+    $plugin = $plugins[$instance->enrol];
+    if ($link = $plugin->get_manual_enrol_link($instance)) {
+        $links[$instance->id] = $link;
+    }
+}
+if (count($links) == 1) {
+    $link = reset($links);
+    echo $OUTPUT->single_button($link, get_string('enrolusers', 'enrol_manual'), 'get');
+
+} else if (count($links) > 1) {
+    $options = array();
+    foreach ($links as $i=>$link) {
+        $options[$link->out(false)] = $inames[$i];
+    }
+    echo $OUTPUT->url_select($options, '', array(''=>get_string('enrolusers', 'enrol_manual').'...'));
+}
+
+echo $OUTPUT->footer();
+
+
diff --git a/enrol/users_forms.php b/enrol/users_forms.php
new file mode 100644
index 000000000000..98d82444ce82
--- /dev/null
+++ b/enrol/users_forms.php
@@ -0,0 +1,203 @@
+<?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/>.
+
+/**
+ * Various enrol UI forms
+ *
+ * @package    core
+ * @subpackage enrol
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+defined('MOODLE_INTERNAL') || die;
+
+require_once("$CFG->libdir/formslib.php");
+
+class enrol_users_assign_form extends moodleform {
+    function definition() {
+        global $CFG, $DB, $PAGE;
+
+        $mform = $this->_form;
+
+        $user       = $this->_customdata['user'];
+        $course     = $this->_customdata['course'];
+        $context    = get_context_instance(CONTEXT_COURSE, $course->id);
+        $assignable = $this->_customdata['assignable'];
+        $assignable = array_reverse($assignable, true); // students first
+
+        $ras = get_user_roles($context, $user->id, true);
+        foreach ($ras as $ra) {
+            unset($assignable[$ra->roleid]);
+        }
+
+        $mform->addElement('header','general', fullname($user));
+
+        $mform->addElement('select', 'roleid', get_string('addrole', 'role'), $assignable);
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+
+        $mform->addElement('hidden', 'user');
+        $mform->setType('user', PARAM_INT);
+
+        $mform->addElement('hidden', 'action');
+        $mform->setType('action', PARAM_ACTION);
+
+        $mform->addElement('hidden', 'ifilter');
+        $mform->setType('ifilter', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'page');
+        $mform->setType('page', PARAM_INT);
+
+        $mform->addElement('hidden', 'perpage');
+        $mform->setType('perpage', PARAM_INT);
+
+        $mform->addElement('hidden', 'sort');
+        $mform->setType('sort', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'dir');
+        $mform->setType('dir', PARAM_ALPHA);
+
+        $this->add_action_buttons();
+
+        $this->set_data($PAGE->url->params());
+        $this->set_data(array('action'=>'assign', 'user'=>$user->id));
+    }
+}
+
+class enrol_users_addmember_form extends moodleform {
+    function definition() {
+        global $CFG, $DB, $PAGE;
+
+        $mform = $this->_form;
+
+        $user     = $this->_customdata['user'];
+        $course   = $this->_customdata['course'];
+        $context  = get_context_instance(CONTEXT_COURSE, $course->id);
+        $allgroups = $this->_customdata['allgroups'];
+        $usergroups = groups_get_all_groups($course->id, $user->id, 0, 'g.id');
+
+        $options = array();
+        foreach ($allgroups as $group) {
+            if (isset($usergroups[$group->id])) {
+                continue;
+            }
+            $options[$group->id] = $group->name;
+        }
+
+        $mform->addElement('header','general', fullname($user));
+
+        $mform->addElement('select', 'groupid', get_string('addgroup', 'group'), $options);
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+
+        $mform->addElement('hidden', 'user');
+        $mform->setType('user', PARAM_INT);
+
+        $mform->addElement('hidden', 'action');
+        $mform->setType('action', PARAM_ACTION);
+
+        $mform->addElement('hidden', 'ifilter');
+        $mform->setType('ifilter', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'page');
+        $mform->setType('page', PARAM_INT);
+
+        $mform->addElement('hidden', 'perpage');
+        $mform->setType('perpage', PARAM_INT);
+
+        $mform->addElement('hidden', 'sort');
+        $mform->setType('sort', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'dir');
+        $mform->setType('dir', PARAM_ALPHA);
+
+        $this->add_action_buttons();
+
+        $this->set_data($PAGE->url->params());
+        $this->set_data(array('action'=>'addmember', 'user'=>$user->id));
+    }
+}
+
+class enrol_users_edit_form extends moodleform {
+    function definition() {
+        global $CFG, $DB, $PAGE;
+
+        $mform = $this->_form;
+
+        $user   = $this->_customdata['user'];
+        $course = $this->_customdata['course'];
+        $ue     = $this->_customdata['ue'];
+
+
+        $mform->addElement('header','general', '');
+
+        $options = array(ENROL_USER_ACTIVE    => get_string('participationactive', 'enrol'),
+                         ENROL_USER_SUSPENDED => get_string('participationsuspended', 'enrol'));
+        if (isset($options[$ue->status])) {
+            $mform->addElement('select', 'status', get_string('participationstatus', 'enrol'), $options);
+        }
+
+        $mform->addElement('date_selector', 'timestart', get_string('enroltimestart', 'enrol'), array('optional' => true));
+
+        $mform->addElement('date_selector', 'timeend', get_string('enroltimeend', 'enrol'), array('optional' => true));
+
+        $mform->addElement('hidden', 'id');
+        $mform->setType('id', PARAM_INT);
+
+        $mform->addElement('hidden', 'ue');
+        $mform->setType('ue', PARAM_INT);
+
+        $mform->addElement('hidden', 'action');
+        $mform->setType('action', PARAM_ACTION);
+
+        $mform->addElement('hidden', 'ifilter');
+        $mform->setType('ifilter', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'page');
+        $mform->setType('page', PARAM_INT);
+
+        $mform->addElement('hidden', 'perpage');
+        $mform->setType('perpage', PARAM_INT);
+
+        $mform->addElement('hidden', 'sort');
+        $mform->setType('sort', PARAM_ALPHA);
+
+        $mform->addElement('hidden', 'dir');
+        $mform->setType('dir', PARAM_ALPHA);
+
+        $this->add_action_buttons();
+
+        $this->set_data($PAGE->url->params());
+        $this->set_data(array('action'=>'edit', 'ue'=>$ue->id, 'status'=>$ue->status, 'timestart'=>$ue->timestart, 'timeend'=>$ue->timeend));
+    }
+
+    function validation($data, $files) {
+        $errors = parent::validation($data, $files);
+
+        if (!empty($data['timestart']) and !empty($data['timeend'])) {
+            if ($data['timestart'] >= $data['timeend']) {
+                $errors['timestart'] = get_string('error');
+                $errors['timeend'] = get_string('error');
+            }
+        }
+
+        return $errors;
+    }
+}
diff --git a/grade/querylib.php b/grade/querylib.php
index 2b2dd6e088c8..298b3d66a28c 100644
--- a/grade/querylib.php
+++ b/grade/querylib.php
@@ -128,7 +128,7 @@ function grade_get_course_grade($userid, $courseid_or_ids=null) {
 
     if (!is_array($courseid_or_ids)) {
         if (empty($courseid_or_ids)) {
-            if (!$courses = get_my_courses($userid, $sort='visible DESC,sortorder ASC', 'id')) {
+            if (!$courses = enrol_get_users_courses($userid)) {
                 return false;
             }
             $courseids = array_keys($courses);
diff --git a/grade/report/overview/lib.php b/grade/report/overview/lib.php
index d05b2f0aaeb6..b886d12732c2 100644
--- a/grade/report/overview/lib.php
+++ b/grade/report/overview/lib.php
@@ -111,8 +111,8 @@ public function setup_table() {
     public function fill_table() {
         global $CFG, $DB, $OUTPUT;
 
-        // MDL-11679, only show 'mycourses' instead of all courses
-        if ($courses = get_my_courses($this->user->id, 'c.sortorder ASC', 'id, shortname, showgrades')) {
+        // MDL-11679, only show user's courses instead of all courses
+        if ($courses = enrol_get_users_courses($this->user->id, false, 'id, shortname, showgrades')) {
             $numusers = $this->get_numusers(false);
 
             foreach ($courses as $course) {
diff --git a/group/assign.php b/group/assign.php
index 8cf1249c2f78..fcc07d3b3614 100644
--- a/group/assign.php
+++ b/group/assign.php
@@ -86,9 +86,9 @@
     }
 
     // Get course managers so they can be hilited in the list
-    if ($managerroles = get_config('', 'coursemanager')) {
-        $coursemanagerroles = split(',', $managerroles);
-        foreach ($coursemanagerroles as $roleid) {
+    if ($managerroles = get_config('', 'coursecontact')) {
+        $coursecontactroles = split(',', $managerroles);
+        foreach ($coursecontactroles as $roleid) {
             $role = $DB->get_record('role', array('id'=>$roleid));
             $managers = get_role_users($roleid, $context, true, 'u.id', 'u.id ASC');
         }
diff --git a/group/autogroup_form.php b/group/autogroup_form.php
index 4ba42a0b71cb..0a703fddcff3 100644
--- a/group/autogroup_form.php
+++ b/group/autogroup_form.php
@@ -21,10 +21,12 @@ function definition() {
         $options = array(0=>get_string('all'));
         $options += $this->_customdata['roles'];
         $mform->addElement('select', 'roleid', get_string('selectfromrole', 'group'), $options);
-        if (!empty($COURSE->defaultrole) and array_key_exists($COURSE->defaultrole, $options)) {
-            $mform->setDefault('roleid', $COURSE->defaultrole);
-        } else if (!empty($CFG->defaultcourseroleid) and array_key_exists($CFG->defaultcourseroleid, $options)) {
-            $mform->setDefault('roleid', $CFG->defaultcourseroleid);
+
+        $student = get_archetype_roles('student');
+        $student = reset($student);
+
+        if ($student and array_key_exists($student->id, $options)) {
+            $mform->setDefault('roleid', $student->id);
         }
 
         $context = get_context_instance(CONTEXT_COURSE, $COURSE->id);
diff --git a/group/group_form.php b/group/group_form.php
index 3d979c66f7dc..20cea027d638 100644
--- a/group/group_form.php
+++ b/group/group_form.php
@@ -33,7 +33,7 @@ function definition () {
         $mform->addElement('editor', 'description_editor', get_string('groupdescription', 'group'), null, $editoroptions);
         $mform->setType('description_editor', PARAM_RAW);
 
-        $mform->addElement('passwordunmask', 'enrolmentkey', get_string('enrolmentkey', 'group'), 'maxlength="254" size="24"', get_string('enrolmentkey'));
+        $mform->addElement('passwordunmask', 'enrolmentkey', get_string('enrolmentkey', 'group'), 'maxlength="254" size="24"', get_string('enrolmentkey', 'group'));
         $mform->setHelpButton('enrolmentkey', array('groupenrolmentkey', get_string('enrolmentkey', 'group')), true);
         $mform->setType('enrolmentkey', PARAM_RAW);
 
@@ -71,7 +71,7 @@ function validation($data, $files) {
                 }
             }
 
-            if (!empty($CFG->enrol_manual_usepasswordpolicy) and $data['enrolmentkey'] != '' and $group->enrolmentkey !== $data['enrolmentkey']) {
+            if (!empty($CFG->groupenrolmentkeypolicy) and $data['enrolmentkey'] != '' and $group->enrolmentkey !== $data['enrolmentkey']) {
                 // enforce password policy only if changing password
                 $errmsg = '';
                 if (!check_password_policy($data['enrolmentkey'], $errmsg)) {
diff --git a/group/index.php b/group/index.php
index 91a5b4a66361..28dd58c9d29a 100644
--- a/group/index.php
+++ b/group/index.php
@@ -11,9 +11,6 @@
 require_once('../config.php');
 require_once('lib.php');
 
-$PAGE->requires->yui2_lib('connection');
-$PAGE->requires->js('/group/clientlib.js');
-
 $courseid = required_param('id', PARAM_INT);
 $groupid  = optional_param('group', false, PARAM_INT);
 $userid   = optional_param('user', false, PARAM_INT);
@@ -45,6 +42,9 @@
 // Make sure that the user has permissions to manage groups.
 require_login($course);
 
+$PAGE->requires->yui2_lib('connection');
+$PAGE->requires->js('/group/clientlib.js');
+
 $context = get_context_instance(CONTEXT_COURSE, $course->id);
 if (!has_capability('moodle/course:managegroups', $context)) {
     redirect('/course/view.php', array('id'=>$course->id)); // Not allowed to manage all groups
diff --git a/group/lib.php b/group/lib.php
index 7764c44c57c1..9f6c66720ea0 100644
--- a/group/lib.php
+++ b/group/lib.php
@@ -481,31 +481,13 @@ function groups_delete_groupings($courseid, $showfeedback=false) {
 
 /**
  * Obtains a list of the possible roles that group members might come from,
- * on a course. Generally this includes all the roles who would have
- * course:view on that course, except the doanything roles.
+ * on a course. Generally this includes only profile roles.
  * @param object $context Context of course
  * @return Array of role ID integers, or false if error/none.
  */
 function groups_get_possible_roles($context) {
-    $capability = 'moodle/course:participate';
-
-    // find all possible "student" roles
-    if ($possibleroles = get_roles_with_capability($capability, CAP_ALLOW, $context)) {
-        $validroleids = array();
-        foreach ($possibleroles as $possiblerole) {
-            if ($caps = role_context_capabilities($possiblerole->id, $context, $capability)) { // resolved list
-                if (isset($caps[$capability]) && $caps[$capability] > 0) { // resolved capability > 0
-                    $validroleids[] = $possiblerole->id;
-                }
-            }
-        }
-        if (empty($validroleids)) {
-            return false;
-        }
-        return $validroleids;
-    } else {
-        return false;  // No need to continue, since no roles have this capability set
-    }
+    $roles = get_profile_roles($context);
+    return array_keys($roles);
 }
 
 
diff --git a/index.php b/index.php
index 1c60a4f39d03..0220c99bad50 100644
--- a/index.php
+++ b/index.php
@@ -77,7 +77,7 @@
     if (get_config('local_hub', 'hubenabled') && file_exists($CFG->dirroot.'/local/hub/lib.php')) {
         require_once($CFG->dirroot.'/local/hub/lib.php');
         $hub = new local_hub();
-        $hub->display_homepage();   
+        $hub->display_homepage();
         exit;
     }
 
diff --git a/lang/en/admin.php b/lang/en/admin.php
index 1791e306d3bd..d4fdf850481b 100755
--- a/lang/en/admin.php
+++ b/lang/en/admin.php
@@ -45,7 +45,6 @@
 $string['allowusermailcharset'] = 'Allow user to select character set';
 $string['allowuserswitchrolestheycantassign'] = 'Allow users without the assign roles capability to switch roles';
 $string['allowuserthemes'] = 'Allow user themes';
-$string['allowvisiblecoursesinhiddencategories'] = 'Allow visible courses in hidden categories';
 $string['antivirus'] = 'Anti-Virus';
 $string['appearance'] = 'Appearance';
 $string['aspellpath'] = 'Path to aspell';
@@ -121,7 +120,6 @@
 $string['configallowoverride2'] = 'Select which role(s) can be overridden by each role in the left column.<br />Note that these settings only apply to users who have either the capability moodle/role:override or the capability moodle/role:safeoverride allowed.';
 $string['configallowswitch'] = 'Select which roles a user may switch to, based on which roles they already have. In addition to an entry in this table, a user must also have the moodle/role:switchroles capability to be able to switch.<br />Note that it is only possible to switch to roles that have the moodle/course:view capability, and that do not have the moodle/site:doanything capability, so some columns in this table are disabled.';
 $string['configallowthemechangeonurl'] = 'If enabled, the theme can be changed by adding theme={themename} to any Moodle URL.';
-$string['configallowunenroll'] = 'If this is set \'Yes\', then students are allowed to unenrol themselves from courses whenever they like. Otherwise they are not allowed, and this process will be solely controlled by the teachers and administrators.';
 $string['configallowuserblockhiding'] = 'Do you want to allow users to hide/show side blocks throughout this site?  This feature uses Javascript and cookies to remember the state of each collapsible block, and only affects the user\'s own view.';
 $string['configallowusermailcharset'] = 'Enabling this, every user in the site will be able to specify his own charset for email.';
 $string['configallowuserswitchrolestheycantassign'] = 'By default, moodle/role:assign is required for users to switch roles. Enabling this setting removes this requirement, and results in the roles available in the "Switch role to" dropdown menu being determined by settings in the "Allow role assignments" table only.
@@ -142,7 +140,6 @@
 $string['configcookiehttponly'] = 'Enables new PHP 5.2.0 feature - browsers are instructed to send cookie with real http requests only, cookies should not be accessible by scripting languages. This is not supported in all browsers and it may not be fully compatible with current code. It helps to prevent some types of XSS attacks.';
 $string['configcookiesecure'] = 'If server is accepting only https connections it is recommended to enable sending of secure cookies. If enabled please make sure that web server is not accepting http:// or set up permanent redirection to https:// address. When <em>wwwroot</em> address does not start with https:// this setting is turned off automatically.';
 $string['configcountry'] = 'If you set a country here, then this country will be selected by default on new user accounts.  To force users to choose a country, just leave this unset.';
-$string['configcoursemanager'] = 'This setting allows you to control who appears on the course description. Users need to have at least one of these roles in a course to be shown on the course description for that course.';
 $string['configcourserequestnotify'] = 'Type username of user to be notified when new course requested.';
 $string['configcourserequestnotify2'] = 'Users who will be notified when a course is requested. Only users who can approve course requests are listed here.';
 $string['configcoursesperpage'] = 'Enter the number of courses to be display per page in a course listing.';
@@ -172,7 +169,6 @@
 $string['configdebugstringids'] = 'This option is designed to help translators. It shows the language file and string id beside each string that is output. (Changing this setting will only take effect on the next page load.)';
 $string['configdebugvalidators'] = 'Enable if you want to have links to external validator servers in page footer. You may need to create new user with username <em>w3cvalidator</em>, and enable guest access. These changes may allow unauthorized access to server, do not enable on production sites!';
 $string['configdefaultallowedmodules'] = 'For the courses which fall into the above category, which modules do you want to allow by default <b>when the course is created</b>?';
-$string['configdefaultcourseroleid'] = 'Users who enrol in a course will be automatically assigned this role.';
 $string['configdefaulthomepage'] = 'This determines the home page for logged in users';
 $string['configdefaultrequestcategory'] = 'Courses requested by users will be automatically placed in this category.';
 $string['configdefaultrequestedcategory'] = 'Default category to put courses that were requested into, if they\'re approved.';
@@ -205,7 +201,6 @@
 $string['configenabletrusttext'] = 'By default Moodle will always thoroughly clean text that comes from users to remove any possible bad scripts, media etc that could be a security risk.  The Trusted Content system is a way of giving particular users that you trust the ability to include these advanced features in their content without interference.  To enable this system, you need to first enable this setting, and then grant the Trusted Content permission to a specific Moodle role.  Texts created or uploaded by such users will be marked as trusted and will not be cleaned before display.';
 $string['configenablewebservices'] = 'Web services enable other systems to log in to this Moodle and perform operations.  For extra security this feature should be disabled unless you are really using it.';
 $string['configenablewsdocumentation'] = 'Enable auto-generation of web services documentation. A web service user can access to his own {$a} without login into Moodle. It display the documentation for the enabled protocols only.';
-$string['configenrolmentplugins'] = 'Please choose the enrolment plugins you wish to use. Don\'t forget to configure the settings properly.<br /><br />You have to indicate which plugins are enabled, and <strong>one</strong> plugin can be set as the default plugin for <em>interactive</em> enrolment. To disable interactive enrolment, set "enrollable" to "No" in required courses.';
 $string['configerrorlevel'] = 'Choose the amount of PHP warnings that you want to be displayed.  Normal is usually the best choice.';
 $string['configexcludeoldflashclients'] = 'Some versions of the Adobe Flash plugin are known to be vulnerable to attacks from malicious Flash content.  You can specify a minimum supported version here, and Moodle will not show Flash files to users with lower versions. Instead they will see an alternate Flash file telling them how to upgrade. Leave this empty to disable all checks.';
 $string['configexperimentalsplitrestore'] = 'If enabled, course backup files will be checked for XML errors and split into smaller parts for use in the restore process. This will result in improvements to restore robustness and execution times, particularly for medium to large course backups.';
@@ -271,7 +266,6 @@
 $string['confignavshowallcourses'] = 'Setting this ensures that all courses on the site are shown in the navigation at all times.';
 $string['confignavshowcategories'] = 'Show course categories in the navigation bar and navigation blocks. This does not occur with courses the user is currently enrolled in, they will still be listed under mycourses without categories.';
 $string['confignodefaultuserrolelists'] = 'This setting prevents all users from being returned from the database from deprecated calls of get_course_user, etc., for the site course if the default role provides that access. Check this, if you suffer a performance hit.';
-$string['confignonmetacoursesyncroleids'] = 'By default all role assignments from child courses are synchronised to metacourses. Roles that are selected here will not be included in the synchronisation process.';
 $string['confignoreplyaddress'] = 'Emails are sometimes sent out on behalf of a user (eg forum posts). The email address you specify here will be used as the "From" address in those cases when the recipients should not be able to reply directly to the user (eg when a user chooses to keep their address private).';
 $string['confignotifyloginfailures'] = 'If login failures have been recorded, email notifications can be sent out.  Who should see these notifications?';
 $string['confignotifyloginthreshold'] = 'If notifications about failed logins are active, how many failed login attempts by one user or one IP address is it worth notifying about?';
@@ -317,7 +311,6 @@
 $string['configsectionstats'] = 'Statistics';
 $string['configsectionuser'] = 'User';
 $string['configsecureforms'] = 'Moodle can use an additional level of security when accepting data from web forms. If this is enabled, then the browser\'s HTTP_REFERER variable is checked against the current form address.  In a very few cases this can cause problems if the user is using a firewall (eg Zonealarm) configured to strip HTTP_REFERER from their web traffic.  Symptoms are getting \'stuck\' on a form. If your users are having problems with the login page (for example) you might want to disable this setting, although it might leave your site more open to brute-force password attacks.  If in doubt, leave this set to \'Yes\'.';
-$string['configsendcoursewelcomemessage'] = 'If enabled, users receive a welcome message via email when they self-enrol in a course.';
 $string['configsessioncookie'] = 'This setting customises the name of the cookie used for Moodle sessions.  This is optional, and only useful to avoid cookies being confused when there is more than one copy of Moodle running within the same web site.';
 $string['configsessioncookiedomain'] = 'This allows you to change the domain that the Moodle cookies are available from. This is useful for Moodle customisations (e.g. authentication or enrolment plugins) that need to share Moodle session information with a web application on another subdomain. <strong>WARNING: it is strongly recommended to leave this setting at the default (empty) - an incorrect value will prevent all logins to the site.</strong>';
 $string['configsessioncookiepath'] = 'If you need to change where browsers send the Moodle cookies, you can change this setting to specify a subdirectory of your web site.  Otherwise the default \'/\' should be fine.';
@@ -349,7 +342,6 @@
 $string['configsupportemail'] = 'This email address will be published to users of this site as the one to email when they need general help (for example, when new users create their own accounts).  If this email is left blank then no such helpful email address is supplied.';
 $string['configsupportname'] = 'This is the name of a person or other entity offering general help via the support email or web address.';
 $string['configsupportpage'] = 'This web address will be published to users of this site as the one to go to when they need general help (for example, when new users create their own accounts).  If this address is left blank then no link will be supplied.';
-$string['configteacherassignteachers'] = 'Should ordinary teachers be allowed to assign other teachers within courses they teach?  If \'No\', then only course creators and admins can assign teachers.';
 $string['configthemedesignermode'] = 'Normally all theme images and style sheets are cached in browsers and on the server for a very long time, for performance. If you are designing themes or developing code then you probably want to turn this mode on so that you are not served cached versions.  Warning: this will make your site slower for all users!  Alternatively, you can also reset the theme caches manually from the Theme selection page.';
 $string['configthemelist'] = 'Leave this blank to allow any valid theme to be used.  If you want to shorten the theme menu, you can specify a comma-separated list of names here (Don\'t use spaces!).
 For example:  standard,orangewhite.';
@@ -373,7 +365,8 @@
 $string['cookiehttponly'] = 'Only http cookies';
 $string['cookiesecure'] = 'Secure cookies only';
 $string['country'] = 'Default country';
-$string['coursemanager'] = 'Course managers';
+$string['coursecontact'] = 'Course contacts';
+$string['coursecontact_desc'] = 'This setting allows you to control who appears on the course description. Users need to have at least one of these roles in a course to be shown on the course description for that course.';
 $string['coursemgmt'] = 'Add/edit courses';
 $string['courseoverview'] = 'Course overview';
 $string['courserequestnotify'] = 'Course request notification';
@@ -425,7 +418,6 @@
 $string['debugstringids'] = 'Show origin of languages strings';
 $string['debugvalidators'] = 'Show validator links';
 $string['defaultallowedmodules'] = 'Default allowed modules';
-$string['defaultcourseroleid'] = 'Default role for users in a course';
 $string['defaulthomepage'] = 'Default home page for users';
 $string['defaulthtmleditor'] = 'Default HTML editor';
 $string['defaultrequestcategory'] = 'Default category for course requests';
@@ -486,6 +478,8 @@
 $string['enablewebservices'] = 'Enable web services';
 $string['enablewsdocumentation'] = 'Web services documentation';
 $string['encoding'] = 'Encoding';
+$string['enrolinstancedefaults'] = 'Enrolment instance defaults';
+$string['enrolinstancedefaults_desc'] = 'Default enrolment settings in new courses.';
 $string['enrolmultipleusers'] = 'Enrol the users';
 $string['environment'] = 'Environment';
 $string['environmenterrortodo'] = 'You must solve all the environmental problems (errors) found above before proceeding to install this Moodle version!';
@@ -548,6 +542,8 @@
 $string['getremoteaddrconf'] = 'Logged IP address source';
 $string['globalsquoteswarning'] = '<p><strong>Security Warning</strong>: to operate properly, Moodle requires <br />that you make certain changes to your current PHP settings.<p/><p>You <em>must</em> set <code>register_globals=off</code> and/or <code>magic_quotes_gpc=on</code>. <br />If possible, you should set <code>register_globals=off</code> to improve general <br /> server security, setting <code>magic_quotes_gpc=on</code> is also recommended.<p/><p>These settings are controlled by editing your <code>php.ini</code>, Apache/IIS <br />configuration or <code>.htaccess</code> file.</p>';
 $string['globalswarning'] = '<p><strong>SECURITY WARNING!</strong></p><p> To operate properly, Moodle requires <br />that you make certain changes to your current PHP settings.</p><p>You <em>must</em> set <code>register_globals=off</code>.</p><p>This setting is controlled by editing your <code>php.ini</code>, Apache/IIS <br />configuration or <code>.htaccess</code> file.</p>';
+$string['groupenrolmentkeypolicy'] = 'Group enrolment key policy';
+$string['groupenrolmentkeypolicy_desc'] = 'Turning this on will make Moodle check group enrolment keys against a valid password policy.';
 $string['googlemapkey'] = 'Google Maps API key';
 $string['gotofirst'] = 'Go to first missing string';
 $string['gradebook'] = 'Gradebook';
@@ -588,7 +584,7 @@
 $string['incompatibleblocks'] = 'Incompatible blocks';
 $string['install'] = 'Install selected language pack';
 $string['installedlangs'] = 'Installed language packs';
-$string['installhijacked'] = 'Installation must be finished from the origianl IP address, sorry.';
+$string['installhijacked'] = 'Installation must be finished from the original IP address, sorry.';
 $string['installsessionerror'] = 'Can not initialise PHP session, please verify that your browser accepts cookies.';
 $string['intcachemax'] = 'Int. cache max';
 $string['invalidlangpack'] = 'to edit this language pack, you need to put it in language folder.';
@@ -712,7 +708,6 @@
 $string['nomissingstrings'] = 'No missing strings';
 $string['nonewsettings'] = 'No new settings were added during this upgrade.';
 $string['nonexistentbookmark'] = 'The bookmark you requested does not exist.';
-$string['nonmetacoursesyncroleids'] = 'Roles that are not synchronised to metacourses';
 $string['noreplyaddress'] = 'No-reply address';
 $string['noresults'] = 'No results found.';
 $string['noroles'] = 'No roles';
@@ -864,7 +859,6 @@
 $string['sectionerror'] = 'Section Error!';
 $string['secureforms'] = 'Use additional form security';
 $string['security'] = 'Security';
-$string['sendcoursewelcomemessage'] = 'Send course welcome message';
 $string['server'] = 'Server';
 $string['serverchecks'] = 'Server Checks';
 $string['serverlimit'] = 'Server Limit';
@@ -1016,7 +1010,6 @@
 $string['uubulkall'] = 'All users';
 $string['uubulknew'] = 'New users';
 $string['uubulkupdated'] = 'Updated users';
-$string['uucoursedefaultrole'] = 'Default course role';
 $string['uucsvline'] = 'CSV line';
 $string['uulegacy1role'] = '(Original Student) typeN=1';
 $string['uulegacy2role'] = '(Original Teacher) typeN=2';
diff --git a/lang/en/enrol.php b/lang/en/enrol.php
new file mode 100644
index 000000000000..8e253817e2ee
--- /dev/null
+++ b/lang/en/enrol.php
@@ -0,0 +1,63 @@
+<?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/>.
+
+/**
+ * Strings for component 'core_enrol', language 'en', branch 'MOODLE_20_STABLE'
+ *
+ * @package    core
+ * @subpackage enrol
+ * @copyright  2010 Petr Skoda {@link http://skodak.org}
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+$string['actenrolshhdr'] = 'Active course enrolment plugins';
+$string['addinstance'] = 'Add method';
+$string['configenrolplugins'] = 'Please select all required plugins and arrange then in appropriate order.';
+$string['defaultenrol'] = 'Add instance to new courses';
+$string['defaultenrol_desc'] = 'It is possible to add this plugin to all new courses by default.';
+$string['deleteinstanceconfirm'] = 'Do you really want to delete enrol plugin instance "{$a->name}" with {$a->users} enrolled users?';
+$string['enrolcandidates'] = 'Not enrolled users';
+$string['enrolcandidatesmatching'] = 'Matching not enrolled users';
+$string['enrolledusers'] = 'Enrolled users';
+$string['enrolledusersmatching'] = 'Matching enrolled users';
+$string['enrolme'] = 'Enrol me in this course';
+$string['enrolmentinstances'] = 'Enrolment methods';
+$string['enrolmentnew'] = 'New enrolment in {$a}';
+$string['enrolmentnewuser'] = '{$a->user} has enrolled in course "{$a->course}"';
+$string['enrolments'] = 'Enrolments';
+$string['enrolperiod'] = 'Enrolment duration';
+$string['enrolusage'] = 'Instances / enrolments';
+$string['enroltimeend'] = 'Enrolment ends';
+$string['enroltimestart'] = 'Enrolment starts';
+$string['manageenrols'] = 'Manage enrol plugins';
+$string['manageinstance'] = 'Manage';
+$string['noexistingparticipants'] = 'No existing participants';
+$string['noguestaccess'] = 'Guests can not access this course, please try to log in.';
+$string['notenrollable'] = 'This course is not enrollable at the moment.';
+$string['notenrolledusers'] = 'Other users';
+$string['participationactive'] = 'Active';
+$string['participationstatus'] = 'Status';
+$string['participationsuspended'] = 'Suspended';
+$string['periodend'] = 'until {$a}';
+$string['periodstart'] = 'from {$a}';
+$string['periodstartend'] = 'from {$a->start} until {$a->end}';
+$string['unenrol'] = 'Unenrol';
+$string['unenrolconfirm'] = 'Do you really want to unenrol user "{$a->user}" from course "{$a->course}"?';
+$string['unenrolme'] = 'Unenrol me from {$a}';
+$string['unenrolroleusers'] = 'Unenrol users';
+$string['uninstallconfirm'] = 'You are about to completely delete the enrol plugin \'{$a}\'.  This will completely delete everything in the database associated with this enrolment type.  Are you SURE you want to continue?';
+$string['uninstalldeletefiles'] = 'All data associated with the enrol plugin \'{$a->plugin}\' has been deleted from the database.  To complete the deletion (and prevent the plugin re-installing itself), you should now delete this directory from your server: {$a->directory}';
diff --git a/lang/en/error.php b/lang/en/error.php
index 529e6f5e48a4..a24832e19f8e 100755
--- a/lang/en/error.php
+++ b/lang/en/error.php
@@ -25,7 +25,6 @@
 
 $string['adminprimarynoedit'] = 'The primary admin cannot be edited by others';
 $string['authnotexisting'] = 'The autorization plugin doesn\'t exist';
-$string['authorizeerror'] = 'Authorize error';
 $string['backupcontainexternal'] = 'This backup file contains external Moodle Network Hosts that are not configured locally';
 $string['backuptablefail'] = 'Backup tables could NOT be set up successfully!';
 $string['blockcannotconfig'] = 'This block does not support global configuration';
@@ -104,7 +103,6 @@
 $string['cannotmanualctrack'] = 'Activity does not provide manual completion tracking';
 $string['cannotmapfield'] = 'Mapping collision detected - two fields maps to the same grade item {$a}';
 $string['cannotmarktopic'] = 'Could not mark that topic for this course';
-$string['cannotmetacourse'] = 'Cannot not add the selected course to this meta course!';
 $string['cannotmigratedatacomments'] = 'Cannot migrate data module comments';
 $string['cannotmodulename'] = 'Cannot get the module name in build navigation';
 $string['cannotmoduletype'] = 'Cannot get the module type in build navigation';
@@ -160,8 +158,6 @@
 $string['cannotusepage2'] = 'Sorry, you may not use this page';
 $string['cannotviewprofile'] = 'You cannot view the profile of this user';
 $string['cannotwritefile'] = 'Cannot write to file ({$a})';
-$string['cantunenrollfrommetacourse'] = 'You cannot unenrol from this meta course';
-$string['cantunenrollinthisrole'] = 'You cannot unenrol from this course while you are in your current role';
 $string['commentmisconf'] = 'Comment ID is misconfigured';
 $string['componentisuptodate'] = 'Component is up-to-date';
 $string['confirmsesskeybad'] = 'Sorry, but your session key could not be confirmed to carry out this action.  This security feature prevents against accidental or malicious execution of important functions in your name.  Please make sure you really wanted to execute this function.';
@@ -220,7 +216,6 @@
 $string['errorsavingrequest'] = 'An error occurred when trying to save your request.';
 $string['errorsettinguserpref'] = 'Error setting user preference';
 $string['errorunzippingfiles'] = 'Error unzipping files';
-$string['errorupdatingcoursevisibility'] = 'Error updating the course visibility';
 $string['expiredkey'] = 'Expired key';
 $string['failtoloadblocks'] = 'One or more blocks are registered in the database, but they all failed to load!';
 $string['fieldrequired'] = '"{$a}" is a required field';
@@ -270,7 +265,6 @@
 $string['invalidcoursenameshort'] = 'Invalid short course name';
 $string['invaliddata'] = 'Data submitted is invalid';
 $string['invalidelementid'] = 'Incorrect element id!';
-$string['invalidenrol'] = 'Illegal enrolment attempted';
 $string['invalidentry'] = 'This is not valid entry!';
 $string['invalidevent'] = 'Invalid event';
 $string['invalidfieldname'] = '"{$a}" is not a valid field name';
@@ -409,6 +403,7 @@
 $string['remotedownloaderror'] = 'Download of component to your server failed, please verify proxy settings, PHP cURL extension is highly recommended.<br /><br />You must download the <a href="{$a->url}">{$a->url}</a> file manually, copy it to "{$a->dest}" in your server and unzip it there.';
 $string['remotedownloadnotallowed'] = 'Download of components to your server isn\'t allowed (allow_url_fopen is disabled).<br /><br />You must download the <a href="{$a->url}">{$a->url}</a> file manually, copy it to "{$a->dest}" in your server and unzip it there.';
 $string['reportnotavailable'] = 'This type of report is only available for the site course';
+$string['requireloginerror'] = 'Cource or activity not accessible.';
 $string['restorechecksumfailed'] = 'Some problem happened with the restore information stored in your session. Please check your PHP memory/DB package size limits. Restore stopped.';
 $string['restrictedcontextexception'] = 'Sorry, execution of external function violates context restriction.';
 $string['restricteduser'] = 'Sorry, but your current account "{$a}" is restricted from doing that';
diff --git a/lang/en/group.php b/lang/en/group.php
index 152423cb13f7..166a116db532 100644
--- a/lang/en/group.php
+++ b/lang/en/group.php
@@ -24,6 +24,7 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
+$string['addgroup'] = 'Add user into group';
 $string['addgroupstogrouping'] = 'Add group to grouping';
 $string['addgroupstogroupings'] = 'Add/remove groups';
 $string['adduserstogroup'] = 'Add/remove users';
@@ -138,6 +139,8 @@
 $string['printerfriendly'] = 'Printer-friendly display';
 $string['random'] = 'Randomly';
 $string['removegroupfromselectedgrouping'] = 'Remove group from grouping';
+$string['removefromgroup'] = 'Remove user from group {$a}';
+$string['removefromgroupconfirm'] = 'Do you really want to remove user "{$a->user}" from group "{$a->group}"?';
 $string['removegroupingsmembers'] = 'Remove all groups from groupings';
 $string['removegroupsmembers'] = 'Remove all group members';
 $string['removeselectedusers'] = 'Remove selected users';
diff --git a/lang/en/moodle.php b/lang/en/moodle.php
index 9cc91dec9760..12c7f092b8c5 100644
--- a/lang/en/moodle.php
+++ b/lang/en/moodle.php
@@ -72,7 +72,6 @@
 $string['adminhelpassigncreators'] = 'Course creators can create new courses';
 $string['adminhelpassignsiteroles'] = 'Apply defined site roles to specific users';
 $string['adminhelpassignstudents'] = 'Go into a course and add students from the admin menu';
-$string['adminhelpassignteachers'] = 'Find a course then use the icon to add teachers';
 $string['adminhelpauthentication'] = 'You can use internal user accounts or external databases';
 $string['adminhelpbackup'] = 'Configure automated backups and their schedule';
 $string['adminhelpconfiguration'] = 'Configure how the site looks and works';
@@ -80,7 +79,6 @@
 $string['adminhelpcourses'] = 'Define courses and categories and assign people to them, edit pending courses';
 $string['adminhelpeditorsettings'] = 'Define basic settings for HTML editor';
 $string['adminhelpedituser'] = 'Browse the list of user accounts and edit any of them';
-$string['adminhelpenrolments'] = 'Choose internal or external ways to control enrolments';
 $string['adminhelpenvironment'] = 'Check how your server suits current and future installation requirements';
 $string['adminhelpfailurelogs'] = 'Browse logs of failed logins';
 $string['adminhelphealthcenter'] = 'Automatic detection of site problems';
@@ -147,10 +145,6 @@
 $string['assignadmins'] = 'Assign admins';
 $string['assigncreators'] = 'Assign creators';
 $string['assignsiteroles'] = 'Assign site-wide roles';
-$string['assignstudents'] = 'Enrol students';
-$string['assignstudentsnote'] = 'Note: it may not be necessary to use this page, since it is possible for students to enrol themselves in this course.';
-$string['assignstudentspass'] = 'All you may need to do is notify your students of the enrolment key for this course, which is currently set to: \'{$a}\'';
-$string['assignteachers'] = 'Assign teachers';
 $string['authenticateduser'] = 'Authenticated user';
 $string['authenticateduserdescription'] = 'All logged in users.';
 $string['authentication'] = 'Authentication';
@@ -187,7 +181,6 @@
 $string['backuplogdetailed'] = 'Detailed execution log';
 $string['backuploglaststatus'] = 'Last execution log';
 $string['backuplogshelp'] = 'If enabled, then course logs will be included in automated backups';
-$string['backupmetacoursehelp'] = 'If enabled, then metacourse info (inherited enrolments) will be included in automated backups';
 $string['backupmissinguserinfoperms'] = 'Note: This backup contains no user data. Exercise and Workshop activities will not be included in the backup, since these modules are not compatible with this type of backup.';
 $string['backupnext'] = 'Next backup';
 $string['backupnoneusersinfo'] = 'Note: This backup contains no users and so all activities have been switched to "without user data" mode. Exercise and Workshop activities will not be included in the backup, since these modules are not compatible with this type of backup.';
@@ -301,27 +294,16 @@
 $string['coursecreators'] = 'Course creator';
 $string['coursecreatorsdescription'] = 'Course creators can create new courses.';
 $string['coursedeleted'] = 'Deleted course {$a}';
-$string['courseenrolend'] = 'Course enrolment end';
-$string['courseenrolenddate'] = 'Course enrolment end date';
-$string['courseenrolstart'] = 'Course enrolment start';
-$string['courseenrolstartdate'] = 'Course enrolment start';
 $string['coursefiles'] = 'Course files';
 $string['courseformatdata'] = 'Course format data';
 $string['courseformats'] = 'Course formats';
 $string['coursegrades'] = 'Course grades';
 $string['coursehelpcategory'] = 'Position the course on the course listing and may make it easier for students to find it.';
-$string['coursehelpenrollable'] = 'Are students able to enrol themselves using the default interactive enrolment plugin';
-$string['coursehelpenrolmentkey'] = 'If set, users will need this key to be enrolled into the course.';
-$string['coursehelpenrolmentplugins'] = 'Default interactive enrolment plugin.';
-$string['coursehelpexpirynotifystudents'] = 'If an enrolment duration has been specified, then this setting determines whether students receive email notification when they are about to be unenrolled from the course.';
-$string['coursehelpexpirythreshold'] = 'If an enrolment duration has been specified, then this setting determines the number of days notice given before students are unenrolled from the course.';
 $string['coursehelpforce'] = 'Force the course group mode to every activity in the course.';
 $string['coursehelpformat'] = 'The course main page will be displayed in this format.';
 $string['coursehelphiddensections'] = 'How the hidden sections in the course are displayed to students.';
 $string['coursehelpmaximumupload'] = 'Define the largest size of file that can be uploaded in this course, limited by the site-wide setting.';
-$string['coursehelpmetacourse'] = 'Set the course a metacourse. A meta course takes enrolments (and other role assignments) from a "child" course or courses.';
 $string['coursehelpnewsitemsnumber'] = 'Number of recent items appearing on the course home page, in a news box down the right-hand side <br/>(0 means the news box won\'t appear).';
-$string['coursehelpnotify'] = 'If an enrolment duration has been specified, then this setting determines whether teachers receive email notification when a student is about to be unenrolled from the course.';
 $string['coursehelpnumberweeks'] = 'Number of weeks/topics displayed on the course main page.';
 $string['coursehelpshowgrades'] = 'Enable the display of the gradebook. It does not prevent grades from being displayed within the individual activities.';
 $string['coursehidden'] = 'This course is currently unavailable to students';
@@ -384,7 +366,6 @@
 $string['creatinggroups'] = 'Creating groups';
 $string['creatinglogentries'] = 'Creating log entries';
 $string['creatingmessagesinfo'] = 'Creating messages info';
-$string['creatingmetacoursedata'] = 'Creating metacourse info';
 $string['creatingmodroles'] = 'Creating module level role assignments and overrides';
 $string['creatingnewcourse'] = 'Creating new course';
 $string['creatingrolesdefinitions'] = 'Creating roles definitions';
@@ -624,39 +605,6 @@
 $string['enable'] = 'Enable';
 $string['encryptedcode'] = 'Encrypted code';
 $string['english'] = 'English';
-$string['enroldate'] = 'Date range';
-$string['enroldetails'] = 'Enrolment details';
-$string['enrolenddate'] = 'End date';
-$string['enrolenddaterror'] = 'Enrolment end date cannot be earlier than start date';
-$string['enrollable'] = 'Course enrollable';
-$string['enrollable_help'] = 'This setting determines whether students are able to enrol themselves using the default interactive enrolment plugin (e.g. internal enrolment) or whether they obtain the message "This course is not enrollable at the moment."
-
-This setting has no effect on non-interactive enrolment plugins. Also, users can always be assigned the role of student via the assign roles link in the course administration block.';
-$string['enrolledincourse'] = 'Enrolled in course "{$a}"';
-$string['enrolledincoursenot'] = 'Not enrolled in course "{$a}"';
-$string['enrolledincoursenotrole'] = 'Error enrolling into "{$a->course}" as "{$a->role}"';
-$string['enrolledincourserole'] = 'Enrolled in "{$a->course}" as "{$a->role}"';
-$string['enrollfirst'] = 'You have to enrol in one of the courses before you can use the site activities';
-$string['enrolme'] = 'Enrol me in this course';
-$string['enrolmentconfirmation'] = 'You are about to enrol yourself as a member of this course.<br />Are you sure you wish to do this?';
-$string['enrolmentend'] = 'Enrolment ends';
-$string['enrolmentkey'] = 'Enrolment key';
-$string['enrolmentkeyfrom'] = 'This course requires an \'enrolment key\' - a one-time<br />
-password that you should have received from {$a}';
-$string['enrolmentkeyfromguest'] = 'This course requires an \'enrolment key\' - as a guest<br />
-you must enter it each time you enter the course. You should have received it from {$a}';
-$string['enrolmentkeyhint'] = 'That enrolment key was incorrect, please try again<br />
-(Here\'s a hint - it starts with \'{$a}\')';
-$string['enrolmentnew'] = 'New enrolment in {$a}';
-$string['enrolmentnewuser'] = '{$a->user} has enrolled in course "{$a->course}"';
-$string['enrolmentnointernal'] = 'Manual enrolments are currently not enabled';
-$string['enrolmentnotyet'] = 'Sorry, you can not access this course until <br /> {$a}';
-$string['enrolmentplugins'] = 'Enrolment plugins';
-$string['enrolmentplugins_help'] = 'This setting determines whether an interactive enrolment plugin is used. Interactive enrolment is when a user has to do something interactively in order to be enrolled, such as clicking "Yes, I do" (internal enrolment) or paying some money. If a non-interactive enrolment plugin is used, the setting may be left as site default.';
-$string['enrolments'] = 'Enrolments';
-$string['enrolmentstart'] = 'Enrolment started';
-$string['enrolperiod'] = 'Enrolment duration';
-$string['enrolstartdate'] = 'Start date';
 $string['entercourse'] = 'Click to enter this course';
 $string['enteremail'] = 'Enter your email address';
 $string['enteremailaddress'] = 'Enter in your email address to reset your
@@ -905,7 +853,6 @@
 $string['choose'] = 'Choose';
 $string['choosecourse'] = 'Choose a course';
 $string['choosedots'] = 'Choose...';
-$string['chooseenrolmethod'] = 'Choose enrolment plugin';
 $string['chooselivelogs'] = 'Or watch current activity';
 $string['chooselogs'] = 'Choose which logs you want to see';
 $string['choosereportfilter'] = 'Choose a filter for the report';
@@ -928,7 +875,6 @@
 $string['importdatafinished'] = 'Import complete! Continue to your course';
 $string['importdatafrom'] = 'Find a course to import data from:';
 $string['importgroups'] = 'Import groups';
-$string['importmetacoursenote'] = 'Use this form to add courses to your meta course (this will import the enrolments)';
 $string['inactive'] = 'Inactive';
 $string['include'] = 'Include';
 $string['includeallusers'] = 'Include all users';
@@ -1036,10 +982,6 @@
 $string['managedatabase'] = 'Database';
 $string['manageeditorfiles'] = 'Manage files used by editor';
 $string['managefilters'] = 'Filters';
-$string['managemeta'] = 'Is this a meta course?';
-$string['managemeta_help'] = 'A meta course takes enrolments (and other role assignments) from a "child" course or courses. In other words, for every child course added to the meta course, all students (and users in other roles) are also enrolled in the meta course. Individual students cannot be added to a meta course, only child courses via the "Child courses" link in the course administration bock.';
-$string['managemetadisabled'] = 'This is disabled because this course is already in a meta course';
-$string['managemetaexplan'] = '(This means that enrolments are inherited from other courses)';
 $string['managemodules'] = 'Modules';
 $string['manageroles'] = 'Roles and permissions';
 $string['markedthistopic'] = 'This topic is highlighted as the current topic';
@@ -1064,17 +1006,6 @@
 $string['messageprovider:instantmessage_help'] = 'This section configures what happens to messages that are sent to you directly from other users on this site.';
 $string['messageselect'] = 'Select this user as an email recipient';
 $string['messageselectadd'] = 'Add / send message';
-$string['metaaddcourse'] = 'Add this course';
-$string['metaalreadycourses'] = 'Courses already assigned';
-$string['metaalreadyhascourses'] = 'This meta course already has child courses.';
-$string['metaalreadyhasenrolments'] = 'This course already has normal enrolments.';
-$string['metaalreadyinmeta'] = 'This course is already part of a meta course.';
-$string['metaassigncourses'] = 'Assign courses';
-$string['metacourse'] = 'Metacourse';
-$string['metanoalreadycourses'] = 'No courses already assigned';
-$string['metanopotentialcourses'] = 'No courses available';
-$string['metapotentialcourses'] = 'Courses available';
-$string['metaremovecourse'] = 'Remove this course';
 $string['migratinggrades'] = 'Migrating grades';
 $string['min'] = 'min';
 $string['mins'] = 'mins';
@@ -1211,10 +1142,6 @@
 $string['nocoursesfound'] = 'No courses were found with the words \'{$a}\'';
 $string['nocoursesyet'] = 'No courses in this category';
 $string['nodstpresets'] = 'The administrator has not enabled Daylight Savings Time support.';
-$string['noexistingadmins'] = 'No existing admins, this is a serious error and you should never have seen this message.';
-$string['noexistingcreators'] = 'No existing creators';
-$string['noexistingstudents'] = 'No existing students';
-$string['noexistingteachers'] = 'No existing teachers';
 $string['nofilesselected'] = 'No files have been selected to restore';
 $string['nofilesyet'] = 'No files have been uploaded to your course yet';
 $string['nograde'] = 'No grade';
@@ -1249,7 +1176,6 @@
 $string['nosuchemail'] = 'No such email address';
 $string['notavailable'] = 'Not available';
 $string['noteachersyet'] = 'No teachers in this course yet';
-$string['notenrollable'] = 'This course is not enrollable at the moment.';
 $string['notenrolled'] = '{$a} is not enrolled in this course.';
 $string['notenrolledprofile'] = 'This profile is not available because this user is not enrolled in this course.';
 $string['noteusercannotrolldatesoncontext'] = '<strong>Note:</strong> The ability to roll dates when restoring this backup has been disabled because you lack the required permissions.';
@@ -1259,8 +1185,6 @@
 $string['notice'] = 'Notice';
 $string['noticenewerbackup'] = 'This backup file has been created with Moodle {$a->backuprelease} ({$a->backupversion}) and it\'s newer than your currently installed Moodle {$a->serverrelease} ({$a->serverversion}). This could cause some inconsistencies because backwards compatibility of backup files cannot be guaranteed.';
 $string['notifications'] = 'Notifications';
-$string['notify'] = 'Notify teachers';
-$string['notify_help'] = 'If an enrolment duration has been specified, then this setting determines whether teachers receive email notification when a student is about to be unenrolled from the course.';
 $string['notifyloginfailuresmessage'] = '{$a->time}, IP: {$a->ip}, User: {$a->info}';
 $string['notifyloginfailuresmessageend'] = 'You can view these logs at {$a}/course/report/log/index.php?id=1&amp;chooselog=1&amp;modid=site_errors.';
 $string['notifyloginfailuresmessagestart'] = 'Here is a list of failed login attempts at {$a} since you were last notified';
@@ -1299,7 +1223,6 @@
 $string['pageshouldredirect'] = 'This page should automatically redirect. If nothing is happening please use the continue link below.';
 $string['parentcategory'] = 'Parent category';
 $string['parentcoursenotfound'] = 'Parent course not found!';
-$string['parentcoursenotmetacourse'] = 'Parent course not metacourse!';
 $string['parentfolder'] = 'Parent folder';
 $string['participants'] = 'Participants';
 $string['participantslist'] = 'Participants list';
@@ -1723,12 +1646,6 @@
 $string['turneditingoff'] = 'Turn editing off';
 $string['turneditingon'] = 'Turn editing on';
 $string['undecided'] = 'Undecided';
-$string['unenrol'] = 'Unenrol';
-$string['unenrolallstudents'] = 'Unenrol all students';
-$string['unenrolallstudentssure'] = 'Are you sure you want to completely unenrol all students from this course?';
-$string['unenrolme'] = 'Unenrol me from {$a}';
-$string['unenrolroleusers'] = 'Unenrol users';
-$string['unenrolsure'] = 'Are you sure you want to unenrol {$a} from this course?';
 $string['unfinished'] = 'Unfinished';
 $string['unknowncategory'] = 'Unknown category';
 $string['unlimited'] = 'Unlimited';
@@ -1900,4 +1817,4 @@
 $string['yourself'] = 'yourself';
 $string['yourteacher'] = 'your {$a}';
 $string['yourwordforx'] = 'Your word for \'{$a}\'';
-$string['zippingbackup'] = 'Zipping backup';
\ No newline at end of file
+$string['zippingbackup'] = 'Zipping backup';
diff --git a/lang/en/role.php b/lang/en/role.php
index b7e9bb1bd31f..b2b1b499e966 100644
--- a/lang/en/role.php
+++ b/lang/en/role.php
@@ -48,6 +48,7 @@
 $string['assignanotherrole'] = 'Assign another role';
 $string['assignerror'] = 'Error while assigning the role {$a->role} to user {$a->user}.';
 $string['assignglobalroles'] = 'Assign system roles';
+$string['assignedroles'] = 'Assigned roles';
 $string['assignmentcontext'] = 'Assignment context';
 $string['assignmentoptions'] = 'Assignment options';
 $string['assignrolenameincontext'] = 'Assign role \'{$a->role}\' in {$a->context}';
@@ -111,14 +112,14 @@
 $string['course:changeidnumber'] = 'Change course ID number';
 $string['course:changeshortname'] = 'Change course short name';
 $string['course:changesummary'] = 'Change course summary';
+$string['course:enrolconfig'] = 'Configure enrol instances in courses';
+$string['course:enrolreview'] = 'Review course enrolments';
 $string['course:manageactivities'] = 'Manage activities';
 $string['course:managefiles'] = 'Manage files';
 $string['course:managegrades'] = 'Manage grades';
 $string['course:managegroups'] = 'Manage groups';
-$string['course:managemetacourse'] = 'Manage metacourse';
 $string['course:managescales'] = 'Manage scales';
 $string['course:markcomplete'] = 'Mark users as complete in course completion';
-$string['course:participate'] = 'Participate in courses';
 $string['course:publish'] = 'Publish a course into hub';
 $string['course:request'] = 'Request new courses';
 $string['course:reset'] = 'Reset course';
@@ -152,7 +153,6 @@
 $string['editingrolex'] = 'Editing role \'{$a}\'';
 $string['editrole'] = 'Edit role';
 $string['editxrole'] = 'Edit {$a} role';
-$string['enrolmentoptions'] = 'Enrolment options';
 $string['errorbadrolename'] = 'Incorrect role name';
 $string['errorbadroleshortname'] = 'Incorrect role short name';
 $string['errorexistsrolename'] = 'Role name already exists';
@@ -211,8 +211,6 @@
 $string['managerdescription'] = 'Managers can access course and modify them, they usually do not participate in courses.';
 $string['manageroles'] = 'Manage roles';
 $string['maybeassignedin'] = 'Context types where this role may be assigned';
-$string['metaassignerror'] = 'Can not assign this role to user "{$a}" because Manage metacourse capability is needed.';
-$string['metaunassignerror'] = 'Role of user "{$a}" was automatically reassigned, please unassign the role in child courses instead.';
 $string['morethan'] = 'More than {$a}';
 $string['multipleroles'] = 'Multiple roles';
 $string['my:manageblocks'] = 'Manage myMoodle page blocks';
@@ -304,7 +302,6 @@
 $string['role:switchroles'] = 'Switch to other roles';
 $string['roletoassign'] = 'Role to assign';
 $string['roletooverride'] = 'Role to override';
-$string['role:unassignself'] = 'Unassign own roles';
 $string['role:viewhiddenassigns'] = 'View hidden role assignments';
 $string['safeoverridenotice'] = 'Note: Capabilities with higher risks are locked because you are only allowed to override safe capabilities.';
 $string['selectanotheruser'] = 'Select another user';
@@ -338,7 +335,9 @@
 $string['tag:editblocks'] = 'Edit blocks in tags pages';
 $string['tag:manage'] = 'Manage all tags';
 $string['thisusersroles'] = 'This user\'s role assignments';
+$string['unassignarole'] = 'Unassign role {$a}';
 $string['unassignerror'] = 'Error while unassigning the role {$a->role} from user {$a->user}.';
+$string['unassignconfirm'] = 'Do you really want to unassign "{$a->role}" role from user "{$a->user}"?';
 $string['user:changeownpassword'] = 'Change own password';
 $string['user:create'] = 'Create users';
 $string['user:delete'] = 'Delete users';
diff --git a/lib/accesslib.php b/lib/accesslib.php
index 22a908f0dad2..d47704853eb6 100755
--- a/lib/accesslib.php
+++ b/lib/accesslib.php
@@ -168,6 +168,8 @@
 define('ROLENAME_ORIGINALANDSHORT', 3);
 /** rolename displays - the name as defined by a role alias, in raw form suitable for editing*/
 define('ROLENAME_ALIAS_RAW', 4);
+/** rolename displays - the name is simply short role name*/
+define('ROLENAME_SHORT', 5);
 
 /** size limit for context cache */
 if (!defined('MAX_CONTEXT_CACHE_SIZE')) {
@@ -700,19 +702,19 @@ function is_siteadmin($user_or_id = NULL) {
 
 /**
  * Returns true if user has at least one role assign
- * of 'coursemanager' role (is potentially listed in some course descriptions).
+ * of 'coursecontact' role (is potentially listed in some course descriptions).
  * @param $userid
  * @return unknown_type
  */
-function has_coursemanager_role($userid) {
+function has_coursecontact_role($userid) {
     global $DB;
 
-    if (empty($CFG->coursemanager)) {
+    if (empty($CFG->coursecontact)) {
         return false;
     }
     $sql = "SELECT 1
               FROM {role_assignments}
-             WHERE userid = :userid AND roleid IN ($CFG->coursemanager)";
+             WHERE userid = :userid AND roleid IN ($CFG->coursecontact)";
     return $DB->record_exists($sql, array('userid'=>$userid));
 }
 
@@ -920,211 +922,26 @@ function require_capability($capability, $context, $userid = NULL, $doanything =
 
 /**
  * Get an array of courses where cap requested is available
+ * and user is enrolled, this can be relatively slow.
  *
- * Get an array of courses (with magic extra bits)
- * where the accessdata and in DB enrolments show
- * that the cap requested is available.
- *
- * The main use is for get_my_courses().
- *
- * Notes
- *
- * - $fields is an array of fieldnames to ADD
- *   so name the fields you really need, which will
- *   be added and uniq'd
- *
- * - the course records have $c->context which is a fully
- *   valid context object. Saves you a query per course!
- *
- * - the course records have $c->categorypath to make
- *   category lookups cheap
- *
- * - current implementation is split in -
- *
- *   - if the user has the cap systemwide, stupidly
- *     grab *every* course for a capcheck. This eats
- *     a TON of bandwidth, specially on large sites
- *     with separate DBs...
- *
- *   - otherwise, fetch "likely" courses with a wide net
- *     that should get us _cheaply_ at least the courses we need, and some
- *     we won't - we get courses that...
- *      - are in a category where user has the cap
- *      - or where use has a role-assignment (any kind)
- *      - or where the course has an override on for this cap
- *
- *   - walk the courses recordset checking the caps on each one
- *     the checks are all in memory and quite fast
- *     (though we could implement a specialised variant of the
- *     has_capability_in_accessdata() code to speed it up)
- *
- * @global object
- * @global object
  * @param string $capability - name of the capability
- * @param array  $accessdata - accessdata session array
- * @param bool   $doanything_ignored - admin roles are completely ignored here
+ * @param array  $accessdata_ignored
+ * @param bool   $doanything_ignored
  * @param string $sort - sorting fields - prefix each fieldname with "c."
  * @param array  $fields - additional fields you are interested in...
- * @param int    $limit  - set if you want to limit the number of courses
+ * @param int    $limit_ignored
  * @return array $courses - ordered array of course objects - see notes above
  */
-function get_user_courses_bycap($userid, $cap, $accessdata, $doanything_ignored, $sort='c.sortorder ASC', $fields=NULL, $limit=0) {
-
-    global $CFG, $DB;
+function get_user_courses_bycap($userid, $cap, $accessdata_ignored, $doanything_ignored, $sort='c.sortorder ASC', $fields=NULL, $limit_ignored=0) {
 
-    // Slim base fields, let callers ask for what they need...
-    $basefields = array('id', 'sortorder', 'shortname', 'idnumber');
+    //TODO: this should be most probably deprecated
 
-    if (!is_null($fields)) {
-        $fields = array_merge($basefields, $fields);
-        $fields = array_unique($fields);
-    } else {
-        $fields = $basefields;
-    }
-    // If any of the fields is '*', leave it alone, discarding the rest
-    // to avoid ambiguous columns under some silly DBs. See MDL-18746 :-D
-    if (in_array('*', $fields)) {
-        $fields = array('*');
-    }
-    $coursefields = 'c.' .implode(',c.', $fields);
-
-    $sort = trim($sort);
-    if ($sort !== '') {
-        $sort = "ORDER BY $sort";
-    }
-
-    $sysctx = get_context_instance(CONTEXT_SYSTEM);
-    if (has_capability_in_accessdata($cap, $sysctx, $accessdata)) {
-        //
-        // Apparently the user has the cap sitewide, so walk *every* course
-        // (the cap checks are moderately fast, but this moves massive bandwidth w the db)
-        // Yuck.
-        //
-        $sql = "SELECT $coursefields,
-                       ctx.id AS ctxid, ctx.path AS ctxpath,
-                       ctx.depth AS ctxdepth, ctx.contextlevel AS ctxlevel,
-                       cc.path AS categorypath
-                  FROM {course} c
-                  JOIN {course_categories} cc
-                       ON c.category=cc.id
-                  JOIN {context} ctx
-                       ON (c.id=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSE.")
-                 $sort ";
-        $rs = $DB->get_recordset_sql($sql);
-    } else {
-        //
-        // narrow down where we have the caps to a few contexts
-        // this will be a combination of
-        // - courses    where user has an explicit enrolment
-        // - courses    that have an override (any status) on that capability
-        // - categories where user has the rights (granted status) on that capability
-        //
-        $sql = "SELECT ctx.*
-                  FROM {context} ctx
-                 WHERE ctx.contextlevel=".CONTEXT_COURSECAT."
-              ORDER BY ctx.depth";
-        $rs = $DB->get_recordset_sql($sql);
-        $catpaths = array();
-        foreach ($rs as $catctx) {
-            if ($catctx->path != ''
-                && has_capability_in_accessdata($cap, $catctx, $accessdata)) {
-                $catpaths[] = $catctx->path;
-            }
+    $courses = enrol_get_users_courses($userid, true, $fields, $sort);
+    foreach ($courses as $id=>$course) {
+        $context = get_context_instance(CONTEXT_COURSE, $id);
+        if (!has_capability($cap, $context, $userid)) {
+            unset($courses[$id]);
         }
-        $rs->close();
-        $catclause = '';
-        $params = array();
-        if (count($catpaths)) {
-            $cc = count($catpaths);
-            for ($n=0;$n<$cc;$n++) {
-                $catpaths[$n] = "ctx.path LIKE '{$catpaths[$n]}/%'";
-            }
-            $catclause = 'WHERE (' . implode(' OR ', $catpaths) .')';
-        }
-        unset($catpaths);
-
-        /// UNION 3 queries:
-        /// - user role assignments in courses
-        /// - user capability (override - any status) in courses
-        /// - user right (granted status) in categories (optionally executed)
-        /// Enclosing the 3-UNION into an inline_view to avoid column names conflict and making the ORDER BY cross-db
-        /// and to allow selection of TEXT columns in the query (MSSQL and Oracle limitation). MDL-16209
-        $sql = "
-            SELECT $coursefields, ctxid, ctxpath, ctxdepth, ctxlevel, ctxinstance, categorypath
-              FROM (
-                    SELECT c.id,
-                           ctx.id AS ctxid, ctx.path AS ctxpath,
-                           ctx.depth AS ctxdepth, ctx.contextlevel AS ctxlevel, ctx.instanceid AS ctxinstance,
-                           cc.path AS categorypath
-                    FROM {course} c
-                    JOIN {course_categories} cc
-                      ON c.category=cc.id
-                    JOIN {context} ctx
-                      ON (c.id=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSE.")
-                    JOIN {role_assignments} ra
-                      ON (ra.contextid=ctx.id AND ra.userid=:userid)
-                    UNION
-                    SELECT c.id,
-                           ctx.id AS ctxid, ctx.path AS ctxpath,
-                           ctx.depth AS ctxdepth, ctx.contextlevel AS ctxlevel, ctx.instanceid AS ctxinstance,
-                           cc.path AS categorypath
-                    FROM {course} c
-                    JOIN {course_categories} cc
-                      ON c.category=cc.id
-                    JOIN {context} ctx
-                      ON (c.id=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSE.")
-                    JOIN {role_capabilities} rc
-                      ON (rc.contextid=ctx.id AND (rc.capability=:cap)) ";
-
-        if (!empty($catclause)) { /// If we have found the right in categories, add child courses here too
-            $sql .= "
-                    UNION
-                    SELECT c.id,
-                           ctx.id AS ctxid, ctx.path AS ctxpath,
-                           ctx.depth AS ctxdepth, ctx.contextlevel AS ctxlevel, ctx.instanceid AS ctxinstance,
-                           cc.path AS categorypath
-                    FROM {course} c
-                    JOIN {course_categories} cc
-                      ON c.category=cc.id
-                    JOIN {context} ctx
-                      ON (c.id=ctx.instanceid AND ctx.contextlevel=".CONTEXT_COURSE.")
-                    $catclause";
-        }
-
-    /// Close the inline_view and join with courses table to get requested $coursefields
-        $sql .= "
-                ) inline_view
-                INNER JOIN {course} c
-                    ON inline_view.id = c.id";
-
-    /// To keep cross-db we need to strip any prefix in the ORDER BY clause for queries using UNION
-        $sql .= "
-                " . preg_replace('/[a-z]+\./i', '', $sort); /// Add ORDER BY clause
-
-        $params['userid'] = $userid;
-        $params['cap']    = $cap;
-        $rs = $DB->get_recordset_sql($sql, $params);
-    }
-
-/// Confirm rights (granted capability) for each course returned
-    $courses = array();
-    $cc = 0; // keep count
-    if ($rs) {
-        foreach ($rs as $c) {
-            // build the context obj
-            context_instance_preload($c);
-            $context = get_context_instance(CONTEXT_COURSE, $c->id);
-
-            if (has_capability_in_accessdata($cap, $context, $accessdata)) {
-                if ($limit > 0 && $cc >= $limit) {
-                    break;
-                }
-
-                $courses[] = $c;
-                $cc++;
-            }
-        }
-        $rs->close();
     }
 
     return $courses;
@@ -1134,7 +951,7 @@ function get_user_courses_bycap($userid, $cap, $accessdata, $doanything_ignored,
 /**
  * Return a nested array showing role assignments
  * all relevant role capabilities for the user at
- * site/metacourse/course_category/course levels
+ * site/course_category/course levels
  *
  * We do _not_ delve deeper than courses because the number of
  * overrides at the module/block levels is HUGE.
@@ -1679,15 +1496,12 @@ function reload_all_capabilities() {
  *
  * Note - assumes a course context!
  *
- * @global object
- * @global object
  * @param object $content
  * @param int $roleid
  * @param array $accessdata
  * @return array Returns access data
  */
-function load_temp_role($context, $roleid, $accessdata) {
-
+function load_temp_role($context, $roleid, array $accessdata) {
     global $CFG, $DB;
 
     //
@@ -1738,57 +1552,21 @@ function load_temp_role($context, $roleid, $accessdata) {
     return $accessdata;
 }
 
-
 /**
- * Check all the login enrolment information for the given user object
- * by querying the enrolment plugins
- *
- * @global object
- * @param object $user
- * @return void
+ * Removes any extra guest roels from accessdata
+ * @param object $context
+ * @param array $accessdata
+ * @return array access data
  */
-function check_enrolment_plugins(&$user) {
-    global $CFG;
-
-    if (empty($user->id) or isguestuser($user)) {
-        // shortcut - there is no enrolment work for guests and not-logged-in users
-        return;
-    }
-
-    static $inprogress = array();  // To prevent this function being called more than once in an invocation
-
-    if (!empty($inprogress[$user->id])) {
-        return;
-    }
-
-    $inprogress[$user->id] = true;  // Set the flag
-
-    require_once($CFG->dirroot .'/enrol/enrol.class.php');
-
-    if (!($plugins = explode(',', $CFG->enrol_plugins_enabled))) {
-        $plugins = array($CFG->enrol);
-    }
-
-    foreach ($plugins as $plugin) {
-        $enrol = enrolment_factory::factory($plugin);
-        if (method_exists($enrol, 'setup_enrolments')) {  /// Plugin supports Roles (Moodle 1.7 and later)
-            $enrol->setup_enrolments($user);
-        } else {                                          /// Run legacy enrolment methods
-            if (method_exists($enrol, 'get_student_courses')) {
-                $enrol->get_student_courses($user);
-            }
-            if (method_exists($enrol, 'get_teacher_courses')) {
-                $enrol->get_teacher_courses($user);
-            }
-
-        /// deal with $user->students and $user->teachers stuff
-            unset($user->student);
-            unset($user->teacher);
-        }
-        unset($enrol);
-    }
+function remove_temp_roles($context, array $accessdata) {
+    global $DB, $USER;
+    $sql = "SELECT DISTINCT ra.roleid AS id
+              FROM {role_assignments} ra
+             WHERE ra.contextid = :contextid AND ra.userid = :userid";
+    $ras = $DB->get_records_sql($sql, array('contextid'=>$context->id, 'userid'=>$USER->id));
 
-    unset($inprogress[$user->id]);  // Unset the flag
+    $accessdata['ra'][$context->path] = array_keys($ras);
+    return $accessdata;
 }
 
 /**
@@ -2318,15 +2096,6 @@ function get_context_instance($contextlevel, $instance=0, $strictness=IGNORE_MIS
     global $DB, $ACCESSLIB_PRIVATE;
     static $allowed_contexts = array(CONTEXT_SYSTEM, CONTEXT_USER, CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_MODULE, CONTEXT_BLOCK);
 
-    if ($contextlevel === 'clearcache') {
-        // TODO: Remove for v2.0
-        // No longer needed, but we'll catch it to avoid erroring out on custom code.
-        // This used to be a fix for MDL-9016
-        // "Restoring into existing course, deleting first
-        //  deletes context and doesn't recreate it"
-        return false;
-    }
-
 /// System context has special cache
     if ($contextlevel == CONTEXT_SYSTEM) {
         return get_system_context();
@@ -2535,7 +2304,7 @@ function delete_role($roleid) {
     global $CFG, $DB;
 
     // first unssign all users
-    role_unassign($roleid);
+    role_unassign_all(array('roleid'=>$roleid));
 
     // cleanup all references to this role, ignore errors
     $DB->delete_records('role_capabilities',   array('roleid'=>$roleid));
@@ -2671,110 +2440,92 @@ function get_roles_with_capability($capability, $permission=NULL, $context=NULL)
 
 
 /**
- * This function makes a role-assignment (a role for a user or group in a particular context)
+ * This function makes a role-assignment (a role for a user in a particular context)
  *
  * @param int $roleid the role of the id
  * @param int $userid userid
- * @param int $groupid_ignored - never implemented!
  * @param int $contextid id of the context
- * @param int $timestart time this assignment becomes effective defaults to 0
- * @param int $timeend time this assignment ceases to be effective defaults to 0
- * @param int $hidden_ignored - use roels with moodle/course:view capability or enrolment instead
- * @param string $enrol defaults to 'manual'
- * @param string $timemodified defaults to ''
- * @return int new id of the assignment
- */
-function role_assign($roleid, $userid, $groupid_ignored, $contextid, $timestart=0, $timeend=0, $hidden_ignored=0, $enrol='manual',$timemodified='') {
+ * @param string $component example 'enrol_ldap', defaults to '' which means manual assignment,
+ * @prama int $itemid id of enrolment/auth plugin
+ * @param string $timemodified defaults to current time
+ * @return int new/existing id of the assignment
+ */
+function role_assign($roleid, $userid, $contextid, $component = '', $itemid = 0, $timemodified = '') {
     global $USER, $CFG, $DB;
 
-/// Do some data validation
+    // first of all detect if somebody is using old style parameters
+    if ($contextid === 0 or is_numeric($component)) {
+        throw new coding_exception('Invalid call to role_assign(), code needs to be updated to use new order of parameters');
+    }
 
+    // now validate all parameters
     if (empty($roleid)) {
-        debugging('Role ID not provided');
-        return false;
+        throw new coding_exception('Invalid call to role_assign(), roleid can not be empty');
     }
 
     if (empty($userid)) {
-        debugging('Userid or groupid must be provided');
-        return false;
+        throw new coding_exception('Invalid call to role_assign(), userid can not be empty');
     }
 
-    if ($userid && !$DB->record_exists('user', array('id'=>$userid))) {
-        debugging('User ID '.intval($userid).' does not exist!');
-        return false;
+    if ($itemid) {
+        if (strpos($component, '_') === false) {
+            throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as"enrol_" when itemid specified', 'component:'.$component);
+        }
+    } else {
+        $itemid = 0;
+        if ($component !== '' and strpos($component, '_') === false) {
+            throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component);
+        }
     }
 
-    if (!$context = get_context_instance_by_id($contextid)) {
-        debugging('Context ID '.intval($contextid).' does not exist!');
+    if (!$DB->record_exists('user', array('id'=>$userid, 'deleted'=>0))) {
+        throw new coding_exception('User ID does not exist or is deleted!', 'userid:'.$userid);
         return false;
     }
 
-    if (($timestart and $timeend) and ($timestart > $timeend)) {
-        debugging('The end time can not be earlier than the start time');
-        return false;
-    }
+    $context = get_context_instance_by_id($contextid, MUST_EXIST);
 
     if (!$timemodified) {
         $timemodified = time();
     }
 
 /// Check for existing entry
-    $ra = $DB->get_record('role_assignments', array('roleid'=>$roleid, 'contextid'=>$context->id, 'userid'=>$userid));
-
-    if (empty($ra)) {             // Create a new entry
-        $ra = new object();
-        $ra->roleid = $roleid;
-        $ra->contextid = $context->id;
-        $ra->userid = $userid;
-        $ra->enrol = $enrol;
-    /// Always round timestart downto 100 secs to help DBs to use their own caching algorithms
-    /// by repeating queries with the same exact parameters in a 100 secs time window
-        $ra->timestart = round($timestart, -2);
-        $ra->timeend = $timeend;
-        $ra->timemodified = $timemodified;
-        $ra->modifierid = empty($USER->id) ? 0 : $USER->id;
-
-        $ra->id = $DB->insert_record('role_assignments', $ra);
-
-    } else {                      // We already have one, just update it
-        $ra->id = $ra->id;
-        $ra->enrol = $enrol;
-    /// Always round timestart downto 100 secs to help DBs to use their own caching algorithms
-    /// by repeating queries with the same exact parameters in a 100 secs time window
-        $ra->timestart = round($timestart, -2);
-        $ra->timeend = $timeend;
-        $ra->timemodified = $timemodified;
-        $ra->modifierid = empty($USER->id) ? 0 : $USER->id;
-
-        $DB->update_record('role_assignments', $ra);
-    }
-
-/// mark context as dirty - modules might use has_capability() in xxx_role_assing()
-/// again expensive, but needed
-    mark_context_dirty($context->path);
+    $ras = $DB->get_records('role_assignments', array('roleid'=>$roleid, 'contextid'=>$context->id, 'userid'=>$userid, 'component'=>$component, 'itemid'=>$itemid), 'id');
+
+    if ($ras) {
+        // role already assigned - this should not happen
+        if (count($ras) > 1) {
+            //very weird - remove all duplicates!
+            $ra = array_shift($ras);
+            foreach ($ras as $r) {
+                $DB->delete_records('role_assignments', array('id'=>$r->id));
+            }
+        } else {
+            $ra = reset($ras);
+        }
 
-    if (!empty($USER->id) && $USER->id == $userid) {
-/// If the user is the current user, then do full reload of capabilities too.
-        load_all_capabilities();
+        // actually there is no need to update, reset anything or trigger any event, so just return
+        return $ra->id;
     }
 
-/// Ask all the modules if anything needs to be done for this user
-    $mods = get_plugin_list('mod');
-    foreach ($mods as $mod => $moddir) {
-        include_once($moddir.'/lib.php');
-        $functionname = $mod.'_role_assign';
-        if (function_exists($functionname)) {
-            $functionname($userid, $context, $roleid);
-        }
-    }
+    // Create a new entry
+    $ra = new object();
+    $ra->roleid       = $roleid;
+    $ra->contextid    = $context->id;
+    $ra->userid       = $userid;
+    $ra->component    = $component;
+    $ra->itemid       = $itemid;
+    $ra->timemodified = $timemodified;
+    $ra->modifierid   = empty($USER->id) ? 0 : $USER->id;
 
-    /// now handle metacourse role assignments if in course context
-    if ($context->contextlevel == CONTEXT_COURSE) {
-        if ($parents = $DB->get_records('course_meta', array('child_course'=>$context->instanceid))) {
-            foreach ($parents as $parent) {
-                sync_metacourse($parent->parent_course);
-            }
-        }
+    $ra->id = $DB->insert_record('role_assignments', $ra);
+
+    // mark context as dirty - again expensive, but needed
+    mark_context_dirty($context->path);
+
+    if (!empty($USER->id) && $USER->id == $userid) {
+        // If the user is the current user, then do full reload of capabilities too.
+        load_all_capabilities();
     }
 
     events_trigger('role_assigned', $ra);
@@ -2782,149 +2533,120 @@ function role_assign($roleid, $userid, $groupid_ignored, $contextid, $timestart=
     return $ra->id;
 }
 
-
 /**
- * Deletes one or more role assignments.   You must specify at least one parameter.
+ * Removes one role assignment
  *
- * @global object
- * @global object
- * @global object
- * @param int $roleid defaults to 0
- * @param int $userid defaults to 0
- * @param int $groupid_ignored never implemented
- * @param int $contextid defaults to 0
- * @param mixed $enrol unassign only if enrolment type matches, NULL means anything. Defaults to NULL
- * @return boolean success or failure
+ * @param int $roleid
+ * @param int  $userid
+ * @param int  $contextid
+ * @param string $component
+ * @param int  $itemid
+ * @return void
  */
-function role_unassign($roleid=0, $userid=0, $groupid_ignored=0, $contextid=0, $enrol=NULL) {
-
+function role_unassign($roleid, $userid, $contextid, $component = '', $itemid = 0) {
     global $USER, $CFG, $DB;
-    require_once($CFG->dirroot.'/group/lib.php');
 
-    $args = array('roleid', 'userid', 'contextid');
-    $select = array();
-    $params = array();
-
-    foreach ($args as $arg) {
-        if ($$arg) {
-            $select[] = "$arg = ?";
-            $params[] = $$arg;
-        }
+    // first make sure the params make sense
+    if ($roleid == 0 or $userid == 0 or $contextid == 0) {
+        throw new coding_exception('Invalid call to role_unassign(), please use role_unassign_all() when removing multiple role assignments');
     }
-    if (!empty($enrol)) {
-        $select[] = "enrol=?";
-        $params[] = $enrol;
-    }
-
-    if ($select) {
-        if ($ras = $DB->get_records_select('role_assignments', implode(' AND ', $select), $params)) {
-            $mods = get_plugin_list('mod');
-            foreach($ras as $ra) {
-                /// infinite loop protection when deleting recursively
-                if (!$ra = $DB->get_record('role_assignments', array('id'=>$ra->id))) {
-                    continue;
-                }
-                $DB->delete_records('role_assignments', array('id'=>$ra->id));
-
-                if (!$context = get_context_instance_by_id($ra->contextid)) {
-                    // strange error, not much to do
-                    continue;
-                }
-
-                /* mark contexts as dirty here, because we need the refreshed
-                 * caps bellow to delete group membership and user_lastaccess!
-                 * and yes, this is very expensive for bulk operations :-(
-                 */
-                mark_context_dirty($context->path);
-
-                /// If the user is the current user, then do full reload of capabilities too.
-                if (!empty($USER->id) && $USER->id == $ra->userid) {
-                    load_all_capabilities();
-                }
-
-                /// Ask all the modules if anything needs to be done for this user
-                foreach ($mods as $mod=>$moddir) {
-                    include_once($moddir.'/lib.php');
-                    $functionname = $mod.'_role_unassign';
-                    if (function_exists($functionname)) {
-                        $functionname($ra->userid, $context); // watch out, $context might be NULL if something goes wrong
-                    }
-                }
-
-                /// now handle metacourse role unassigment and removing from groups if in course context
-                if ($context->contextlevel == CONTEXT_COURSE) {
 
-                    // cleanup leftover course groups/subscriptions etc when user has
-                    // no capability to view course
-                    // this may be slow, but this is the proper way of doing it
-                    if (!has_capability('moodle/course:participate', $context, $ra->userid)) {
-                        // remove from groups
-                        groups_delete_group_members($context->instanceid, $ra->userid);
-
-                        // delete lastaccess records
-                        $DB->delete_records('user_lastaccess', array('userid'=>$ra->userid, 'courseid'=>$context->instanceid));
-                    }
-
-                    //unassign roles in metacourses if needed
-                    if ($parents = $DB->get_records('course_meta', array('child_course'=>$context->instanceid))) {
-                        foreach ($parents as $parent) {
-                            sync_metacourse($parent->parent_course);
-                        }
-                    }
-                }
-
-                events_trigger('role_unassigned', $ra);
-            }
+    if ($itemid) {
+        if (strpos($component, '_') === false) {
+            throw new coding_exception('Invalid call to role_assign(), component must start with plugin type such as "enrol_" when itemid specified', 'component:'.$component);
+        }
+    } else {
+        $itemid = 0;
+        if ($component !== '' and strpos($component, '_') === false) {
+            throw new coding_exception('Invalid call to role_assign(), invalid component string', 'component:'.$component);
         }
     }
 
-    return true;
+    role_unassign_all(array('roleid'=>$roleid, 'userid'=>$userid, 'contextid'=>$contextid, 'component'=>$component, 'itemid'=>$itemid), false, false);
 }
 
 /**
- * Enrol someone without using the default role in a course
- *
- * A convenience function to take care of the common case where you
- * just want to enrol someone using the default role into a course
+ * Removes multiple role assignments, parameters may contain:
+ *   'roleid', 'userid', 'contextid', 'component', 'enrolid'.
  *
- * @param object $course
- * @param object $user
- * @param string $enrol the plugin used to do this enrolment
- * @return bool
+ * @param array $params role assignment parameters
+ * @param bool $subcontexts unassign in subcontexts too
+ * @param bool $includmanual include manual role assignments too
+ * @return void
  */
-function enrol_into_course($course, $user, $enrol) {
+function role_unassign_all(array $params, $subcontexts = false, $includemanual=false) {
+    global $USER, $CFG, $DB;
 
-    $timestart = time();
-    // remove time part from the timestamp and keep only the date part
-    $timestart = make_timestamp(date('Y', $timestart), date('m', $timestart), date('d', $timestart), 0, 0, 0);
-    if ($course->enrolperiod) {
-        $timeend = $timestart + $course->enrolperiod;
-    } else {
-        $timeend = 0;
+    if (!$params) {
+        throw new coding_exception('Missing parameters in role_unsassign_all() call');
     }
 
-    if ($role = get_default_course_role($course)) {
+    $allowed = array('roleid', 'userid', 'contextid', 'component', 'itemid');
+    foreach ($params as $key=>$value) {
+        if (!in_array($key, $allowed)) {
+            throw new coding_exception('Unknown role_unsassign_all() parameter key', 'key:'.$key);
+        }
+    }
 
-        $context = get_context_instance(CONTEXT_COURSE, $course->id);
+    if (isset($params['component']) and $params['component'] !== '' and strpos($params['component'], '_') === false) {
+        throw new coding_exception('Invalid component paramter in role_unsassign_all() call', 'component:'.$params['component']);
+    }
 
-        if (!role_assign($role->id, $user->id, 0, $context->id, $timestart, $timeend, 0, $enrol)) {
-            return false;
+    if ($includemanual) {
+        if (!isset($params['component']) or $params['component'] === '') {
+            throw new coding_exception('include manual parameter requires component parameter in role_unsassign_all() call');
         }
+    }
 
-        // force accessdata refresh for users visiting this context...
-        mark_context_dirty($context->path);
-
-        email_welcome_message_to_user($course, $user);
+    if ($subcontexts) {
+        if (empty($params['contextid'])) {
+            throw new coding_exception('subcontexts paramtere requires component parameter in role_unsassign_all() call');
+        }
+    }
 
-        add_to_log($course->id, 'course', 'enrol',
-                'view.php?id='.$course->id, $course->id);
+    $ras = $DB->get_records('role_assignments', $params);
+    foreach($ras as $ra) {
+        $DB->delete_records('role_assignments', array('id'=>$ra->id));
+        if ($context = get_context_instance_by_id($ra->contextid)) {
+            // this is a bit expensive but necessary
+            mark_context_dirty($context->path);
+            /// If the user is the current user, then do full reload of capabilities too.
+            if (!empty($USER->id) && $USER->id == $ra->userid) {
+                load_all_capabilities();
+            }
+        }
+        events_trigger('role_unassigned', $ra);
+    }
+    unset($ras);
 
-        return true;
+    // process subcontexts
+    if ($subcontexts and $context = get_context_instance_by_id($params['contextid'])) {
+        $contexts = get_child_contexts($context);
+        $mparams = $params;
+        foreach($contexts as $context) {
+            $mparams['contextid'] = $context->id;
+            $ras = $DB->get_records('role_assignments', $mparams);
+            foreach($ras as $ra) {
+                $DB->delete_records('role_assignments', array('id'=>$ra->id));
+                // this is a bit expensive but necessary
+                mark_context_dirty($context->path);
+                /// If the user is the current user, then do full reload of capabilities too.
+                if (!empty($USER->id) && $USER->id == $ra->userid) {
+                    load_all_capabilities();
+                }
+                events_trigger('role_unassigned', $ra);
+            }
+        }
     }
 
-    return false;
+    // do this once more for all manual role assignments
+    if ($includemanual) {
+        $params['component'] = '';
+        role_unassign_all($params, $subcontexts, false);
+    }
 }
 
+
 /**
  * Determines if a user is currently logged in
  *
@@ -2939,7 +2661,7 @@ function isloggedin() {
 /**
  * Determines if a user is logged in as real guest user with username 'guest'.
  *
- * @param int $user mixed user object or id, $USER if not specified
+ * @param int|object $user mixed user object or id, $USER if not specified
  * @return bool true if user is the real guest user, false if not logged in or other user
  */
 function isguestuser($user = NULL) {
@@ -3004,7 +2726,8 @@ function is_guest($context, $user = NULL) {
         return false;
     }
 
-    if (has_capability('moodle/course:participate', $coursecontext, $userid, false)) {
+    // consider only real active enrolments here
+    if (is_enrolled($coursecontext, $user, '', true)) {
         return false;
     }
 
@@ -3052,10 +2775,11 @@ function is_viewing($context, $user = NULL, $withcapability = '') {
  * @param object $context
  * @param int|object $user, if NULL $USER is used, otherwise user object or id expected
  * @param string $withcapability extra capability name
+ * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
  * @return bool
  */
-function is_enrolled($context, $user = NULL, $withcapability = '') {
-    global $USER;
+function is_enrolled($context, $user = NULL, $withcapability = '', $onlyactive = false) {
+    global $USER, $DB;
 
     // first find the course context
     $coursecontext = get_course_context($context);
@@ -3075,9 +2799,47 @@ function is_enrolled($context, $user = NULL, $withcapability = '') {
         return false;
     }
 
-    if ($coursecontext->instanceid != SITEID and !has_capability('moodle/course:participate', $coursecontext, $userid, false)) {
-        // admins are not enrolled, everybody is "enrolled" in the frontpage course
-        return false;
+    if ($coursecontext->instanceid == SITEID) {
+        // everybody participates on frontpage
+    } else {
+        if ($onlyactive) {
+            $sql = "SELECT ue.*
+                      FROM {user_enrolments} ue
+                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
+                      JOIN {user} u ON u.id = ue.userid
+                     WHERE ue.userid = :userid AND ue.status = :active AND e.status = :enabled AND u.deleted = 0";
+            $params = array('enabled'=>ENROL_INSTANCE_ENABLED, 'active'=>ENROL_USER_ACTIVE, 'userid'=>$userid, 'courseid'=>$coursecontext->instanceid);
+            // this result should be very small, better not do the complex time checks in sql for now ;-)
+            $enrolments = $DB->get_records_sql($sql, $params);
+            $now = time();
+            // make sure the enrol period is ok
+            $result = false;
+            foreach ($enrolments as $e) {
+                if ($e->timestart > $now) {
+                    continue;
+                }
+                if ($e->timeend and $e->timeend < $now) {
+                    continue;
+                }
+                $result = true;
+                break;
+            }
+            if (!$result) {
+                return false;
+            }
+
+        } else {
+            // any enrolment is good for us here, even outdated, disabled or inactive
+            $sql = "SELECT 'x'
+                      FROM {user_enrolments} ue
+                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
+                      JOIN {user} u ON u.id = ue.userid
+                     WHERE ue.userid = :userid AND u.deleted = 0";
+            $params = array('userid'=>$userid, 'courseid'=>$coursecontext->instanceid);
+            if (!$DB->record_exists_sql($sql, $params)) {
+                return false;
+            }
+        }
     }
 
     if ($withcapability and !has_capability($withcapability, $context, $userid)) {
@@ -3090,144 +2852,88 @@ function is_enrolled($context, $user = NULL, $withcapability = '') {
 /**
  * Returns array with sql code and parameters returning all ids
  * of users enrolled into course.
+ *
+ * This function is using 'eu[0-9]+_' prefix for table names and parameters.
+ *
  * @param object $context
  * @param string $withcapability
  * @param int $groupid 0 means ignore groups, any other value limits the result by group id
- * @param string $prefix used for alias of user table, parameter names and in aliases of other used tables
+ * @param bool $onlyactive consider only active enrolments in enabled plugins and time restrictions
  * @return array list($sql, $params)
  */
-function get_enrolled_sql($context, $withcapability = '', $groupid = 0, $prefix = 'eu') {
+function get_enrolled_sql($context, $withcapability = '', $groupid = 0, $onlyactive = false) {
     global $DB;
 
-    if ($context->contextlevel < CONTEXT_COURSE) {
-        throw new coding_exception('get_enrolled_sql() expects course context and bellow!');
-    }
+    // use unique prefix just in case somebody makes some SQL magic with the result
+    static $i = 0;
+    $i++;
+    $prefix = 'eu'.$i.'_';
 
     // first find the course context
-    if ($context->contextlevel == CONTEXT_COURSE) {
-        $coursecontext = $context;
-
-    } else if ($context->contextlevel == CONTEXT_MODULE) {
-        $coursecontext = get_context_instance_by_id(get_parent_contextid($context, MUST_EXIST));
+    $coursecontext = get_course_context($context);
 
-    } else if ($context->contextlevel == CONTEXT_BLOCK) {
-        $parentcontext = get_context_instance_by_id(get_parent_contextid($context, MUST_EXIST));
-        if ($parentcontext->contextlevel == CONTEXT_COURSE) {
-            $coursecontext = $parentcontext;
-        } else if ($parentcontext->contextlevel == CONTEXT_MODULE) {
-            $coursecontext = get_context_instance_by_id(get_parent_contextid($parentcontext, MUST_EXIST));
-        } else {
-            throw new coding_exception('Invalid context supplied to get_enrolled_sql()!');
-        }
+    $isfrontpage = ($coursecontext->instanceid == SITEID);
 
-    } else {
-        throw new coding_exception('Invalid context supplied to get_enrolled_sql()!');
-    }
+    $joins  = array();
+    $wheres = array();
+    $params = array();
 
     list($contextids, $contextpaths) = get_context_info_list($context);
-    list($coursecontextids, $coursecontextpaths) = get_context_info_list($coursecontext);
 
     // get all relevant capability info for all roles
     if ($withcapability) {
-        list($incontexts, $params) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'con00');
-        $incaps = "IN (:participate, :withcap)";
-        $params['participate'] = 'moodle/course:participate';
-        $params['withcap']     = $withcapability;
-    } else {
-        list($incontexts, $params) = $DB->get_in_or_equal($coursecontextids, SQL_PARAMS_NAMED, 'con00');
-        $incaps = "= :participate";
-        $params['participate'] = 'moodle/course:participate';
-    }
-    $defs = array();
-    $sql = "SELECT rc.id, rc.roleid, rc.permission, rc.capability, ctx.path
-              FROM {role_capabilities} rc
-              JOIN {context} ctx on rc.contextid = ctx.id
-             WHERE rc.contextid $incontexts AND rc.capability $incaps";
-    $rcs = $DB->get_records_sql($sql, $params);
-    foreach ($rcs as $rc) {
-        $defs[$rc->capability][$rc->path][$rc->roleid] = $rc->permission;
-    }
-
-    $courseaccess = array();
-    if (!empty($defs['moodle/course:participate'])) {
-        foreach ($coursecontextpaths as $path) {
-            if (empty($defs['moodle/course:participate'][$path])) {
-                continue;
-            }
-
-            foreach($defs['moodle/course:participate'][$path] as $roleid => $perm) {
-                if ($perm == CAP_PROHIBIT) {
-                    $courseaccess[$roleid] = CAP_PROHIBIT;
+        list($incontexts, $cparams) = $DB->get_in_or_equal($contextids, SQL_PARAMS_NAMED, 'ctx00');
+        $cparams['cap'] = $withcapability;
+
+        $defs = array();
+        $sql = "SELECT rc.id, rc.roleid, rc.permission, ctx.path
+                  FROM {role_capabilities} rc
+                  JOIN {context} ctx on rc.contextid = ctx.id
+                 WHERE rc.contextid $incontexts AND rc.capability = :cap";
+        $rcs = $DB->get_records_sql($sql, $cparams);
+        foreach ($rcs as $rc) {
+            $defs[$rc->path][$rc->roleid] = $rc->permission;
+        }
+
+        $access = array();
+        if (!empty($defs)) {
+            foreach ($contextpaths as $path) {
+                if (empty($defs[$path])) {
                     continue;
                 }
-                if (!isset($courseaccess[$roleid])) {
-                    $courseaccess[$roleid] = (int)$perm;
+                foreach($defs[$path] as $roleid => $perm) {
+                    if ($perm == CAP_PROHIBIT) {
+                        $access[$roleid] = CAP_PROHIBIT;
+                        continue;
+                    }
+                    if (!isset($access[$roleid])) {
+                        $access[$roleid] = (int)$perm;
+                    }
                 }
             }
         }
-    }
 
-    $access = array();
-    if (!empty($defs[$withcapability])) {
-        foreach ($contextpaths as $path) {
-            if (empty($defs[$withcapability][$path])) {
-                continue;
-            }
-            foreach($defs[$withcapability][$path] as $roleid => $perm) {
+        unset($defs);
+
+        // make lists of roles that are needed and prohibited
+        $needed     = array(); // one of these is enough
+        $prohibited = array(); // must not have any of these
+        if ($withcapability) {
+            foreach ($access as $roleid => $perm) {
                 if ($perm == CAP_PROHIBIT) {
-                    $access[$roleid] = CAP_PROHIBIT;
-                    continue;
-                }
-                if (!isset($access[$roleid])) {
-                    $access[$roleid] = (int)$perm;
+                    unset($needed[$roleid]);
+                    $prohibited[$roleid] = true;
+                } else if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) {
+                    $needed[$roleid] = true;
                 }
             }
         }
-    }
-
-    unset($defs);
-
-    // make lists of roles that are needed and prohibited
-    $courseneeded     = array(); // one of these is enough
-    $courseprohibited = array(); // must not have any of these
-    foreach ($courseaccess as $roleid => $perm) {
-        if ($perm == CAP_PROHIBIT) {
-            unset($courseneeded[$roleid]);
-            $courseprohibited[$roleid] = true;
-        } else if ($perm == CAP_ALLOW and empty($courseprohibited[$roleid])) {
-            $courseneeded[$roleid] = true;
-        }
-    }
-    $needed     = array(); // one of these is enough
-    $prohibited = array(); // must not have any of these
-    if ($withcapability) {
-        foreach ($access as $roleid => $perm) {
-            if ($perm == CAP_PROHIBIT) {
-                unset($needed[$roleid]);
-                $prohibited[$roleid] = true;
-            } else if ($perm == CAP_ALLOW and empty($prohibited[$roleid])) {
-                $needed[$roleid] = true;
-            }
-        }
-    }
-
-    $isfrontpage = ($coursecontext->instanceid == SITEID);
-
-    $defaultuserroleid      = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : NULL;
-    $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : NULL;
 
-    $nobody = false;
+        $defaultuserroleid      = isset($CFG->defaultuserroleid) ? $CFG->defaultuserroleid : NULL;
+        $defaultfrontpageroleid = isset($CFG->defaultfrontpageroleid) ? $CFG->defaultfrontpageroleid : NULL;
 
-    if ($isfrontpage) {
-        // on the frontpage all users are kind of enrolled, we have to respect only the prohibits
-        $courseneeded = array();
-    } else {
-        if (empty($courseneeded)) {
-            $nobody = true;
-        }
-    }
+        $nobody = false;
 
-    if ($withcapability and !$nobody) {
         if ($isfrontpage) {
             if (!empty($prohibited[$defaultuserroleid]) or !empty($prohibited[$defaultfrontpageroleid])) {
                 $nobody = true;
@@ -3247,53 +2953,58 @@ function get_enrolled_sql($context, $withcapability = '', $groupid = 0, $prefix
                 $nobody = true;
             }
         }
-    }
 
-    if ($nobody) {
-        // nobody can match so return some SQL that does not return any results
-        return array("SELECT {$prefix}.id FROM {user} {$prefix} WHERE 1=2", array());
-    }
+        if ($nobody) {
+            // nobody can match so return some SQL that does not return any results
+            $wheres[] = "1 = 2";
 
-    $joins  = array();
-    $params = array();
-    $wheres = array("{$prefix}.deleted = 0 AND {$prefix}.username <> 'guest'");
+        } else {
 
-    if ($courseneeded) {
-        $ctxids = implode(',', $coursecontextids);
-        $roleids = implode(',', array_keys($courseneeded));
-        $joins[] = "JOIN {role_assignments} {$prefix}_ra1 ON ({$prefix}_ra1.userid = {$prefix}.id AND {$prefix}_ra1.roleid IN ($roleids) AND {$prefix}_ra1.contextid IN ($ctxids))";
-    }
+            if ($needed) {
+                $ctxids = implode(',', $contextids);
+                $roleids = implode(',', array_keys($needed));
+                $joins[] = "JOIN {role_assignments} {$prefix}ra3 ON ({$prefix}ra3.userid = {$prefix}u.id AND {$prefix}ra3.roleid IN ($roleids) AND {$prefix}ra3.contextid IN ($ctxids))";
+            }
 
-    if ($courseprohibited) {
-        $ctxids = implode(',', $coursecontextids);
-        $roleids = implode(',', array_keys($courseprohibited));
-        $joins[] = "LEFT JOIN {role_assignments} {$prefix}_ra2 ON ({$prefix}_ra2.userid = {$prefix}.id AND {$prefix}_ra2.roleid IN ($roleids) AND {$prefix}_ra2.contextid IN ($ctxids))";
-        $wheres[] = "{$prefix}_ra2 IS NULL";
-    }
+            if ($prohibited) {
+                $ctxids = implode(',', $contextids);
+                $roleids = implode(',', array_keys($prohibited));
+                $joins[] = "LEFT JOIN {role_assignments} {$prefix}ra4 ON ({$prefix}ra4.userid = {$prefix}u.id AND {$prefix}ra4.roleid IN ($roleids) AND {$prefix}ra4.contextid IN ($ctxids))";
+                $wheres[] = "{$prefix}ra4 IS NULL";
+            }
 
-    if ($needed) {
-        $ctxids = implode(',', $contextids);
-        $roleids = implode(',', array_keys($needed));
-        $joins[] = "JOIN {role_assignments} {$prefix}_ra3 ON ({$prefix}_ra3.userid = {$prefix}.id AND {$prefix}_ra3.roleid IN ($roleids) AND {$prefix}_ra3.contextid IN ($ctxids))";
-    }
+            if ($groupid) {
+                $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}u.id AND {$prefix}gm.id = :{$prefix}gmid)";
+                $params["{$prefix}gmid"] = $groupid;
+            }
+        }
 
-    if ($prohibited) {
-        $ctxids = implode(',', $contextids);
-        $roleids = implode(',', array_keys($prohibited));
-        $joins[] = "LEFT JOIN {role_assignments} {$prefix}_ra4 ON ({$prefix}_ra4.userid = {$prefix}.id AND {$prefix}_ra4.roleid IN ($roleids) AND {$prefix}_ra4.contextid IN ($ctxids))";
-        $wheres[] = "{$prefix}_ra4 IS NULL";
     }
 
-    if ($groupid) {
-        $joins[] = "JOIN {groups_members} {$prefix}gm ON ({$prefix}gm.userid = {$prefix}.id AND {$prefix}gm.id = :{$prefix}gmid)";
-        $params["{$prefix}gmid"] = $groupid;
+    $wheres[] = "{$prefix}u.deleted = 0 AND {$prefix}u.username <> 'guest'";
+
+    if ($isfrontpage) {
+        // all users are "enrolled" on the frontpage
+    } else {
+        $joins[] = "JOIN {user_enrolments} {$prefix}ue ON {$prefix}ue.userid = {$prefix}u.id";
+        $joins[] = "JOIN {enrol} {$prefix}e ON ({$prefix}e.id = {$prefix}ue.enrolid AND {$prefix}e.courseid = :{$prefix}courseid)";
+        $params[$prefix.'courseid'] = $coursecontext->instanceid;
+
+        if ($onlyactive) {
+            $wheres[] = "{$prefix}ue.status = :{$prefix}active AND {$prefix}e.status = :{$prefix}enabled";
+            $wheres[] = "{$prefix}ue.timestart < :{$prefix}now1 AND ({$prefix}ue.timeend = 0 OR {$prefix}ue.timeend > :{$prefix}now2)";
+            $now = round(time(), -2); // rounding helps caching in DB
+            $params = array_merge($params, array($prefix.'enabled'=>ENROL_INSTANCE_ENABLED,
+                                                 $prefix.'active'=>ENROL_USER_ACTIVE,
+                                                 $prefix.'now1'=>$now, $prefix.'now2'=>$now));
+        }
     }
 
     $joins = implode("\n", $joins);
     $wheres = "WHERE ".implode(" AND ", $wheres);
 
-    $sql = "SELECT DISTINCT {$prefix}.id
-               FROM {user} {$prefix}
+    $sql = "SELECT DISTINCT {$prefix}u.id
+               FROM {user} {$prefix}u
              $joins
             $wheres";
 
@@ -4011,7 +3722,7 @@ function get_context_info_list($context) {
  */
 function get_course_context($context) {
     if (empty($context->contextlevel)) {
-        throw coding_exception('Invalid context parameter.');
+        throw new coding_exception('Invalid context parameter.');
 
     } if ($context->contextlevel == CONTEXT_COURSE) {
         return $context;
@@ -4334,7 +4045,7 @@ function get_component_string($component, $contextlevel) {
         case CONTEXT_SYSTEM:
             if (preg_match('|^enrol|', $component)) {
                 $langname = str_replace('/', '_', $component);
-                $string = get_string('enrolname', $langname);
+                $string = get_string('pluginname', $langname);
             } else if (preg_match('|^block|', $component)) {
                 $langname = str_replace('/', '_', $component);
                 $string = get_string('pluginname', $langname);
@@ -4355,11 +4066,19 @@ function get_component_string($component, $contextlevel) {
         break;
 
         case CONTEXT_COURSECAT:
-            $string = get_string('categories');
+            if (preg_match('|^enrol|', $component)) {
+                $langname = str_replace('/', '_', $component);
+                $string = get_string('pluginname', $langname);
+            } else {
+                $string = get_string('categories');
+            }
         break;
 
         case CONTEXT_COURSE:
-            if (preg_match('|^gradeimport|', $component)) {
+            if (preg_match('|^enrol|', $component)) {
+                $langname = str_replace('/', '_', $component);
+                $string = get_string('pluginname', $langname);
+            } else if (preg_match('|^gradeimport|', $component)) {
                 $string = get_string('gradeimport', 'grades').': '.get_string('modulename', $component);
             } else if (preg_match('|^gradeexport|', $component)) {
                 $string = get_string('gradeexport', 'grades').': '.get_string('modulename', $component);
@@ -4555,7 +4274,7 @@ function get_all_roles() {
  */
 function get_archetype_roles($archetype) {
     global $DB;
-    return $DB->get_records('role', array('archetype'=>$archetype));
+    return $DB->get_records('role', array('archetype'=>$archetype), 'sortorder ASC');
 }
 
 /**
@@ -4656,8 +4375,6 @@ function allow_switch($fromroleid, $targetroleid) {
 /**
  * Gets a list of roles that this user can assign in this context
  *
- * @global object
- * @global object
  * @param object $context the context.
  * @param int $rolenamedisplay the type of role name to display. One of the
  *      ROLENAME_X constants. Default ROLENAME_ALIAS.
@@ -4682,7 +4399,7 @@ function get_assignable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $with
 
     $params = array();
     $extrafields = '';
-    if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) {
+    if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT or $rolenamedisplay == ROLENAME_SHORT) {
         $extrafields .= ', r.shortname';
     }
 
@@ -4716,12 +4433,16 @@ function get_assignable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $with
 
     $rolenames = array();
     foreach ($roles as $role) {
+        if ($rolenamedisplay == ROLENAME_SHORT) {
+            $rolenames[$role->id] = $role->shortname;
+            continue;
+        }
         $rolenames[$role->id] = $role->name;
         if ($rolenamedisplay == ROLENAME_ORIGINALANDSHORT) {
             $rolenames[$role->id] .= ' (' . $role->shortname . ')';
         }
     }
-    if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT) {
+    if ($rolenamedisplay != ROLENAME_ORIGINALANDSHORT and $rolenamedisplay != ROLENAME_SHORT) {
         $rolenames = role_fix_names($rolenames, $context, $rolenamedisplay);
     }
 
@@ -4759,8 +4480,7 @@ function get_switchable_roles($context) {
     $extrajoins = '';
     $extrawhere = '';
     if (!is_siteadmin()) {
-        // Admins are allowed to switch to any role with 'moodle/course:participate' in the
-        // role definition.
+        // Admins are allowed to switch to any role with.
         // Others are subject to the additional constraint that the switch-to role must be allowed by
         // 'role_allow_switch' for some role they have assigned in this context or any parent.
         $parents = get_parent_contexts($context);
@@ -4769,7 +4489,7 @@ function get_switchable_roles($context) {
 
         $extrajoins = "JOIN {role_allow_switch} ras ON ras.allowswitch = rc.roleid
         JOIN {role_assignments} ra ON ra.roleid = ras.roleid";
-        $extrawhere = "AND ra.userid = :userid AND ra.contextid IN ($contexts)";
+        $extrawhere = "WHERE ra.userid = :userid AND ra.contextid IN ($contexts)";
         $params['userid'] = $USER->id;
     }
 
@@ -4779,46 +4499,15 @@ function get_switchable_roles($context) {
             SELECT DISTINCT rc.roleid
             FROM {role_capabilities} rc
             $extrajoins
-            WHERE rc.capability = :viewcap
-              AND rc.permission = " . CAP_ALLOW . "
-              AND rc.contextid = :syscontextid
-              $extrawhere
+            $extrawhere
         ) idlist
         JOIN {role} r ON r.id = idlist.roleid
         ORDER BY r.sortorder";
-    $params['syscontextid'] = $systemcontext->id;
-    $params['viewcap'] = 'moodle/course:participate';
 
     $rolenames = $DB->get_records_sql_menu($query, $params);
     return role_fix_names($rolenames, $context, ROLENAME_ALIAS);
 }
 
-/**
- * Get an array of role ids that might possibly be the target of a switchrole.
- * Our policy is that you cannot switch to admin role
- * and you can only switch to a role with moodle/course:participate. This method returns
- * a list of those role ids.
- *
- * @global object
- * @return array an array whose keys are the allowed role ids.
- */
-function get_allowed_switchable_roles() {
-    global $CFG, $DB;
-
-    $systemcontext = get_context_instance(CONTEXT_SYSTEM);
-
-    $query = "
-        SELECT DISTINCT rc.roleid, 1
-        FROM {role_capabilities} rc
-        JOIN {role} r ON r.id = rc.roleid
-        WHERE rc.capability = :participate
-          AND rc.permission = " . CAP_ALLOW . "
-          AND rc.contextid = :syscontextid";
-    $params = array('syscontextid' => $systemcontext->id, 'participate' => 'moodle/course:participate');
-
-    return $DB->get_records_sql_menu($query, $params);
-}
-
 /**
  * Gets a list of roles that this user can override in this context.
  *
@@ -4904,6 +4593,34 @@ function get_overridable_roles($context, $rolenamedisplay = ROLENAME_ALIAS, $wit
     return array($rolenames, $rolecounts, $nameswithcounts);
 }
 
+/**
+ * Create a role menu suitable for default role selection in enrol plugins.
+ * @param object $context
+ * @param int $addroleid current or default role - always added to list
+ * @return array roleid=>localised role name
+ */
+function get_default_enrol_roles($context, $addroleid = null) {
+    global $DB;
+
+    $params = array('level'=>CONTEXT_COURSE);
+    if ($addroleid) {
+        $addrole = "OR r.id = :addroleid";
+        $params['addroleid'] = $addroleid;
+    } else {
+        $addrole = "";
+    }
+    $sql = "SELECT r.id, r.name
+              FROM {role} r
+         LEFT JOIN {role_context_levels} rcl ON (rcl.roleid = r.id AND rcl.contextlevel = :level)
+             WHERE rcl.id IS NOT NULL $addrole
+          ORDER BY sortorder DESC";
+
+    $roles = $DB->get_records_sql_menu($sql, $params);
+    $roles = role_fix_names($roles, $context, ROLENAME_BOTH);
+
+    return $roles;
+}
+
 /**
  * @global object
  * @param integer $roleid the id of a role.
@@ -4933,15 +4650,15 @@ function get_roles_for_contextlevels($contextlevel) {
  */
 function get_default_contextlevels($rolearchetype) {
     static $defaults = array(
-        'manager' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE),
-        'coursecreator' => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT),
-        'editingteacher' => array(CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_MODULE),
-        'teacher' => array(CONTEXT_COURSECAT, CONTEXT_COURSE, CONTEXT_MODULE),
-        'student' => array(CONTEXT_COURSE, CONTEXT_MODULE),
-        'guest' => array(),
-        'user' => array(),
-        'frontpage' => array()
-    );
+        'manager'        => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE),
+        'coursecreator'  => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT),
+        'editingteacher' => array(CONTEXT_COURSE, CONTEXT_MODULE),
+        'teacher'        => array(CONTEXT_COURSE, CONTEXT_MODULE),
+        'student'        => array(CONTEXT_COURSE, CONTEXT_MODULE),
+        'guest'          => array(),
+        'user'           => array(),
+        'frontpage'      => array());
+
     if (isset($defaults[$rolearchetype])) {
         return $defaults[$rolearchetype];
     } else {
@@ -4955,54 +4672,22 @@ function get_default_contextlevels($rolearchetype) {
  *
  * @global object
  * @param integer $roleid the id of a role.
- * @param array $contextlevels the context levels at which this role should be assignable.
+ * @param array $contextlevels the context levels at which this role should be assignable,
+ *      duplicate levels are removed.
+ * @return void
  */
 function set_role_contextlevels($roleid, array $contextlevels) {
     global $DB;
     $DB->delete_records('role_context_levels', array('roleid' => $roleid));
     $rcl = new stdClass;
     $rcl->roleid = $roleid;
+    $contextlevels = array_unique($contextlevels);
     foreach ($contextlevels as $level) {
         $rcl->contextlevel = $level;
         $DB->insert_record('role_context_levels', $rcl, false, true);
     }
 }
 
-/**
- *  Returns a role object that is the default role for new enrolments
- *  in a given course
- *
- * @global object
- * @global object
- *  @param object $course
- *  @return object returns a role or NULL if none set
- */
-function get_default_course_role($course) {
-    global $DB, $CFG;
-
-/// First let's take the default role the course may have
-    if (!empty($course->defaultrole)) {
-        if ($role = $DB->get_record('role', array('id'=>$course->defaultrole))) {
-            return $role;
-        }
-    }
-
-/// Otherwise the site setting should tell us
-    if ($CFG->defaultcourseroleid) {
-        if ($role = $DB->get_record('role', array('id'=>$CFG->defaultcourseroleid))) {
-            return $role;
-        }
-    }
-
-/// It's unlikely we'll get here, but just in case, try and find a student role
-    if ($studentroles = $DB->get_records('role', array('archetype'=>'student'))) {
-        return array_shift($studentroles);   /// Take the first one
-    }
-
-    return NULL;
-}
-
-
 /**
  * Who has this capability in this context?
  *
@@ -5302,7 +4987,7 @@ function get_users_by_capability($context, $capability, $fields='', $sort='', $l
  * a good idea to see what roles have the capabilities you want
  * (array_diff() them against roiles that have 'can-do-anything'
  * to weed out admin-ish roles. Or fetch a list of roles from
- * variables like $CFG->coursemanager .
+ * variables like $CFG->coursecontact .
  *
  * @global object
  * @param array $users Users array, keyed on userid
@@ -5387,7 +5072,7 @@ function get_role_users($roleid, $context, $parent=false, $fields='',
         $fields = 'u.id, u.confirmed, u.username, u.firstname, u.lastname, '.
                   'u.maildisplay, u.mailformat, u.maildigest, u.email, u.city, '.
                   'u.country, u.picture, u.idnumber, u.department, u.institution, '.
-                  'u.emailstop, u.lang, u.timezone, u.lastaccess, u.mnethostid, r.name as rolename';
+                  'u.emailstop, u.lang, u.timezone, u.lastaccess, u.mnethostid, r.name AS rolename, r.sortorder';
     }
 
     $parentcontexts = '';
@@ -5423,7 +5108,7 @@ function get_role_users($roleid, $context, $parent=false, $fields='',
         $params = array_merge($params, $whereparams);
     }
 
-    $sql = "SELECT $fields, ra.roleid
+    $sql = "SELECT DISTINCT $fields, ra.roleid
               FROM {role_assignments} ra
               JOIN {user} u ON u.id = ra.userid
               JOIN {role} r ON ra.roleid = r.id
@@ -5819,10 +5504,6 @@ function role_fix_names($roleoptions, $context, $rolenamedisplay=ROLENAME_ALIAS)
  */
 function component_level_changed($cap, $comp, $contextlevel) {
 
-    if ($cap->component == 'enrol/authorize' && $comp =='enrol/authorize') {
-        return false;
-    }
-
     if (strstr($cap->component, '/') && strstr($comp, '/')) {
         $compsa = explode('/', $cap->component);
         $compsb = explode('/', $comp);
diff --git a/lib/adminlib.php b/lib/adminlib.php
index b526150db4b1..90edf9b1696b 100644
--- a/lib/adminlib.php
+++ b/lib/adminlib.php
@@ -139,12 +139,12 @@ function uninstall_plugin($type, $name) {
 
     if ($type === 'mod') {
         $pluginname = $name;  // eg. 'forum'
-        $strpluginname = get_string('modulename', $pluginname);
         if (get_string_manager()->string_exists('modulename', $component)) {
             $strpluginname = get_string('modulename', $component);
         } else {
             $strpluginname = $component;
         }
+
     } else {
         $pluginname = $component;
         if (get_string_manager()->string_exists('pluginname', $component)) {
@@ -153,6 +153,7 @@ function uninstall_plugin($type, $name) {
             $strpluginname = $component;
         }
     }
+
     echo $OUTPUT->heading($pluginname);
 
     $plugindirectory = get_plugin_directory($type, $name);
@@ -167,7 +168,7 @@ function uninstall_plugin($type, $name) {
         }
     }
 
-    if ('mod' === $type) {
+    if ($type === 'mod') {
     // perform cleanup tasks specific for activity modules
 
         if (!$module = $DB->get_record('modules', array('name' => $name))) {
@@ -221,6 +222,26 @@ function uninstall_plugin($type, $name) {
                 }
             }
         }
+
+    } else if ($type === 'enrol') {
+        // NOTE: this is a bit brute force way - it will not trigger events and hooks properly
+        // nuke all role assignments
+        role_unassign_all(array('component'=>$component));
+        // purge participants
+        $DB->delete_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($name));
+        // purge enrol instances
+        $DB->delete_records('enrol', array('enrol'=>$name));
+        // tweak enrol settings
+        if (!empty($CFG->enrol_plugins_enabled)) {
+            $enabledenrols = explode(',', $CFG->enrol_plugins_enabled);
+            $enabledenrols = array_unique($enabledenrols);
+            $enabledenrols = array_flip($enabledenrols);
+            unset($enabledenrols[$name]);
+            $enabledenrols = array_flip($enabledenrols);
+            if (is_array($enabledenrols)) {
+                set_config('enrol_plugins_enabled', implode(',', $enabledenrols));
+            }
+        }
     }
 
     // perform clean-up task common for all the plugin/subplugin types
@@ -4238,13 +4259,13 @@ public function write_setting($data) {
  *
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class admin_setting_special_coursemanager extends admin_setting_pickroles {
+class admin_setting_special_coursecontact extends admin_setting_pickroles {
 /**
  * Calls parent::__construct with specific arguments
  */
     public function __construct() {
-        parent::__construct('coursemanager', get_string('coursemanager', 'admin'),
-            get_string('configcoursemanager', 'admin'),
+        parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
+            get_string('coursecontact_desc', 'admin'),
             array('editingteacher'));
     }
 }
@@ -4613,66 +4634,204 @@ public function search($query) {
     }
 }
 
+
 /**
- * Enrolment manage page
+ * Special class for enrol plugins management.
  *
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
  * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class admin_enrolment_page extends admin_externalpage {
+class admin_setting_manageenrols extends admin_setting {
 /**
  * Calls parent::__construct with specific arguments
  */
     public function __construct() {
-        global $CFG;
-        parent::__construct('enrolment', get_string('enrolments'), $CFG->wwwroot . '/'.$CFG->admin.'/enrol.php');
+        $this->nosave = true;
+        parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
     }
 
     /**
-     * @param string The string to search for
-     * @return array
+     * Always returns true, does nothing
+     *
+     * @return true
      */
-    public function search($query) {
-        if ($result = parent::search($query)) {
-            return $result;
+    public function get_setting() {
+        return true;
+    }
+
+    /**
+     * Always returns true, does nothing
+     *
+     * @return true
+     */
+    public function get_defaultsetting() {
+        return true;
+    }
+
+    /**
+     * Always returns '', does not write anything
+     *
+     * @return string Always returns ''
+     */
+    public function write_setting($data) {
+    // do not write any setting
+        return '';
+    }
+
+    /**
+     * Checks if $query is one of the available enrol plugins
+     *
+     * @param string $query The string to search for
+     * @return bool Returns true if found, false if not
+     */
+    public function is_related($query) {
+        if (parent::is_related($query)) {
+            return true;
         }
 
-        $found = false;
+        $textlib = textlib_get_instance();
+        $query = $textlib->strtolower($query);
+        $enrols = enrol_get_plugins(false);
+        foreach ($enrols as $name=>$enrol) {
+            $localised = get_string('pluginname', 'enrol_'.$name);
+            if (strpos($textlib->strtolower($name), $query) !== false) {
+                return true;
+            }
+            if (strpos($textlib->strtolower($localised), $query) !== false) {
+                return true;
+            }
+        }
+        return false;
+    }
 
-        if ($modules = get_plugin_list('enrol')) {
-            $textlib = textlib_get_instance();
-            foreach ($modules as $plugin => $dir) {
-                if (strpos($plugin, $query) !== false) {
-                    $found = true;
-                    break;
+    /**
+     * Builds the XHTML to display the control
+     *
+     * @param string $data Unused
+     * @param string $query
+     * @return string
+     */
+    public function output_html($data, $query='') {
+        global $CFG, $OUTPUT, $DB;
+
+        // display strings
+        $strup        = get_string('up');
+        $strdown      = get_string('down');
+        $strsettings  = get_string('settings');
+        $strenable    = get_string('enable');
+        $strdisable   = get_string('disable');
+        $struninstall = get_string('uninstallplugin', 'admin');
+        $strusage     = get_string('enrolusage', 'enrol');
+
+        $enrols_available = enrol_get_plugins(false);
+        $active_enrols    = enrol_get_plugins(true);
+
+        $allenrols = array();
+        foreach ($active_enrols as $key=>$enrol) {
+            $allenrols[$key] = true;
+        }
+        foreach ($enrols_available as $key=>$enrol) {
+            $allenrols[$key] = true;
+        }
+        // now find all borked plugins and at least allow then to uninstall
+        $borked = array();
+        $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
+        foreach ($condidates as $candidate) {
+            if (empty($allenrols[$candidate])) {
+                $allenrols[$candidate] = true;
+            }
+        }
+
+        $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
+        $return .= $OUTPUT->box_start('generalbox enrolsui');
+
+        $table = new html_table();
+        $table->head  = array(get_string('name'), $strusage, $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
+        $table->align = array('left', 'center', 'center', 'center', 'center', 'center');
+        $table->width = '90%';
+        $table->data  = array();
+
+        // iterate through enrol plugins and add to the display table
+        $updowncount = 1;
+        $enrolcount = count($active_enrols);
+        $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
+        $printed = array();
+        foreach($allenrols as $enrol => $unused) {
+            if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
+                $name = get_string('pluginname', 'enrol_'.$enrol);
+            } else {
+                $name = $enrol;
+            }
+            //usage
+            $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
+            $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
+            $usage = "$ci / $cp";
+
+            // hide/show link
+            if (isset($active_enrols[$enrol])) {
+                $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
+                $hideshow = "<a href=\"$aurl\">";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/hide') . "\" class=\"icon\" alt=\"$strdisable\" /></a>";
+                $enabled = true;
+                $displayname = "<span>$name</span>";
+            } else if (isset($enrols_available[$enrol])) {
+                $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
+                $hideshow = "<a href=\"$aurl\">";
+                $hideshow .= "<img src=\"" . $OUTPUT->pix_url('i/show') . "\" class=\"icon\" alt=\"$strenable\" /></a>";
+                $enabled = false;
+                $displayname = "<span class=\"dimmed_text\">$name</span>";
+            } else {
+                $hideshow = '';
+                $enabled = false;
+                $displayname = '<span class="notifyproblem">'.$name.'</span>';
+            }
+
+            // up/down link (only if enrol is enabled)
+            $updown = '';
+            if ($enabled) {
+                if ($updowncount > 1) {
+                    $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
+                    $updown .= "<a href=\"$aurl\">";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"$strup\" /></a>&nbsp;";
+                } else {
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />&nbsp;";
                 }
-                $strmodulename = get_string('enrolname', "enrol_$plugin");
-                if (strpos($textlib->strtolower($strmodulename), $query) !== false) {
-                    $found = true;
-                    break;
+                if ($updowncount < $enrolcount) {
+                    $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
+                    $updown .= "<a href=\"$aurl\">";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"$strdown\" /></a>";
+                } else {
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />";
                 }
+                ++$updowncount;
             }
+
+            // settings link
+            if (isset($active_enrols[$enrol]) or file_exists($CFG->dirroot.'/enrol/'.$enrol.'/settings.php')) {
+                $surl = new moodle_url('/admin/settings.php', array('section'=>'enrolsettings'.$enrol));
+                $settings = "<a href=\"$surl\">$strsettings</a>";
+            } else {
+                $settings = '';
+            }
+
+            // uninstall
+            $aurl = new moodle_url($url, array('action'=>'uninstall', 'enrol'=>$enrol));
+            $uninstall = "<a href=\"$aurl\">$struninstall</a>";
+
+            // add a row to the table
+            $table->data[] = array($displayname, $usage, $hideshow, $updown, $settings, $uninstall);
+
+            $printed[$enrol] = true;
         }
-        //ugly harcoded hacks
-        if (strpos('sendcoursewelcomemessage', $query) !== false) {
-            $found = true;
-        } else if (strpos($textlib->strtolower(get_string('sendcoursewelcomemessage', 'admin')), $query) !== false) {
-                $found = true;
-            } else if (strpos($textlib->strtolower(get_string('configsendcoursewelcomemessage', 'admin')), $query) !== false) {
-                    $found = true;
-                } else if (strpos($textlib->strtolower(get_string('configenrolmentplugins', 'admin')), $query) !== false) {
-                        $found = true;
-                    }
-        if ($found) {
-            $result = new object();
-            $result->page     = $this;
-            $result->settings = array();
-            return array($this->name => $result);
-        } else {
-            return array();
-        }
+
+        $return .= html_writer::table($table);
+        $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
+        $return .= $OUTPUT->box_end();
+        return highlight($query, $return);
     }
 }
 
+
 /**
  * Blocks manage page
  *
@@ -4948,14 +5107,14 @@ public function output_html($data, $query='') {
                     $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" /></a>&nbsp;";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer.gif') . "\" class=\"icon\" alt=\"\" />&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />&nbsp;";
                 }
                 if ($updowncount < $authcount) {
                     $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
                     $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" /></a>";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer.gif') . "\" class=\"icon\" alt=\"\" />";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />";
                 }
                 ++ $updowncount;
             }
@@ -5113,14 +5272,14 @@ public function output_html($data, $query='') {
                     $updown .= "<img src=\"" . $OUTPUT->pix_url('t/up') . "\" alt=\"up\" /></a>&nbsp;";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer.gif') . "\" class=\"icon\" alt=\"\" />&nbsp;";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />&nbsp;";
                 }
                 if ($updowncount < $editorcount) {
                     $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
                     $updown .= "<img src=\"" . $OUTPUT->pix_url('t/down') . "\" alt=\"down\" /></a>";
                 }
                 else {
-                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer.gif') . "\" class=\"icon\" alt=\"\" />";
+                    $updown .= "<img src=\"" . $OUTPUT->pix_url('spacer') . "\" class=\"icon\" alt=\"\" />";
                 }
                 ++ $updowncount;
             }
@@ -5561,7 +5720,7 @@ function admin_get_root($reload=false, $requirefulltree=true) {
 
         $ADMIN->loaded = true;
     }
-    
+
     return $ADMIN;
 }
 
@@ -6217,7 +6376,7 @@ public function is_related($query) {
      * Helper function that generates a moodle_url object
      * relevant to the repository
      */
-    
+
     function repository_action_url($repository) {
         return new moodle_url('/admin/repository.php', array('sesskey'=>sesskey(), 'repos'=>$repository));
     }
@@ -6250,7 +6409,7 @@ public function output_html($data, $query='') {
         // Set strings that are used multiple times
         $settingsstr = get_string('settings');
         $disablestr = get_string('disable');
-        
+
         // Table to list plug-ins
         $table = new html_table();
         $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
@@ -6296,7 +6455,7 @@ public function output_html($data, $query='') {
                 } else {
                     $currentaction = 'hide';
                 }
- 
+
                 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
 
                 // Display up/down link
@@ -7133,4 +7292,4 @@ public function output_html($data, $query = '') {
         return format_admin_setting($this, $this->visiblename, $content, $this->description, false, '', $this->get_defaultsetting(), $query);
     }
 
-}
\ No newline at end of file
+}
diff --git a/lib/completion/completion_criteria_duration.php b/lib/completion/completion_criteria_duration.php
index 43ee857c2783..6ad1ba2fb1dc 100644
--- a/lib/completion/completion_criteria_duration.php
+++ b/lib/completion/completion_criteria_duration.php
@@ -171,6 +171,8 @@ public function get_status($completion) {
     public function cron() {
         global $DB;
 
+//TODO: MDL-22797 completion needs to be updated to use new enrolment framework
+
         // Get all users who match meet this criteria
         $sql = '
             SELECT DISTINCT
diff --git a/lib/completion/cron.php b/lib/completion/cron.php
index 29167853f575..9c8b2e8d7d6a 100644
--- a/lib/completion/cron.php
+++ b/lib/completion/cron.php
@@ -53,6 +53,8 @@ function completion_cron() {
 function completion_cron_mark_started() {
     global $CFG, $DB;
 
+//TODO: MDL-22797 completion needs to be updated to use new enrolment framework
+
     if (debugging()) {
         mtrace('Marking users as started');
     }
diff --git a/lib/datalib.php b/lib/datalib.php
index 18ddf5005c76..e8197caee615 100644
--- a/lib/datalib.php
+++ b/lib/datalib.php
@@ -85,82 +85,6 @@ function get_admins() {
     return $DB->get_records_sql($sql);
 }
 
-/**
- * Get all of the courses in a given meta course
- *
- * @global object
- * @param int $metacourseid The metacourse id
- * @return array
- */
-function get_courses_in_metacourse($metacourseid) {
-    global $DB;
-
-    $sql = "SELECT c.id, c.shortname, c.fullname
-              FROM {course} c, {course_meta} mc
-             WHERE mc.parent_course = ? AND mc.child_course = c.id
-          ORDER BY c.shortname";
-    $params = array($metacourseid);
-
-    return $DB->get_records_sql($sql, $params);
-}
-
-/**
- * @todo Document this function
- *
- * @global object
- * @uses SITEID
- * @param int $metacourseid
- * @return array
- */
-function get_courses_notin_metacourse($metacourseid) {
-    global $DB;
-
-    if ($alreadycourses = get_courses_in_metacourse($metacourseid)) {
-        $alreadycourses = implode(',',array_keys($alreadycourses));
-        $alreadycourses = "AND c.id NOT IN ($alreadycourses)";
-    } else {
-        $alreadycourses = "";
-    }
-
-    $sql = "SELECT c.id,c.shortname,c.fullname
-              FROM {course} c
-             WHERE c.id != ? and c.id != ".SITEID." and c.metacourse != 1
-                   $alreadycourses
-          ORDER BY c.shortname";
-    $params = array($metacourseid);
-
-    return $DB->get_records_sql($sql, $params);
-}
-
-/**
- * @todo Document this function
- *
- * This function is nearly identical to {@link get_courses_notin_metacourse()}
- *
- * @global object
- * @uses SITEID
- * @param int $metacourseid
- * @return int The count
- */
-function count_courses_notin_metacourse($metacourseid) {
-    global $DB;
-
-    if ($alreadycourses = get_courses_in_metacourse($metacourseid)) {
-        $alreadycourses = implode(',',array_keys($alreadycourses));
-        $alreadycourses = "AND c.id NOT IN ($alreadycourses)";
-    } else {
-        $alreadycourses = "";
-    }
-
-    $sql = "SELECT COUNT(c.id)
-              FROM {course} c
-             WHERE c.id != ? and c.id != ".SITEID." and c.metacourse != 1
-                   $alreadycourses";
-    $params = array($metacourseid);
-
-    return $DB->count_records_sql($sql, $params);
-}
-
 /**
  * Search through course users
  *
@@ -606,9 +530,8 @@ function get_courses_wmanagers($categoryid=0, $sort="c.sortorder ASC", $fields=a
 
     $basefields = array('id', 'category', 'sortorder',
                         'shortname', 'fullname', 'idnumber',
-                        'guest', 'startdate', 'visible',
-                        'newsitems',  'cost', 'enrol',
-                        'groupmode', 'groupmodeforce');
+                        'startdate', 'visible',
+                        'newsitems', 'groupmode', 'groupmodeforce');
 
     if (!is_null($fields) && is_string($fields)) {
         if (empty($fields)) {
@@ -670,12 +593,12 @@ function get_courses_wmanagers($categoryid=0, $sort="c.sortorder ASC", $fields=a
         return array(); // no courses!
     }
 
-    $CFG->coursemanager = trim($CFG->coursemanager);
-    if (empty($CFG->coursemanager)) {
+    $CFG->coursecontact = trim($CFG->coursecontact);
+    if (empty($CFG->coursecontact)) {
         return $courses;
     }
 
-    $managerroles = split(',', $CFG->coursemanager);
+    $managerroles = split(',', $CFG->coursecontact);
     $catctxids = '';
     if (count($managerroles)) {
         if ($allcats === true) {
@@ -724,7 +647,7 @@ function get_courses_wmanagers($categoryid=0, $sort="c.sortorder ASC", $fields=a
             $sql .= " OR ra.contextid  IN ($catctxids) )";
         }
 
-        $sql .= "AND ra.roleid IN ({$CFG->coursemanager})
+        $sql .= "AND ra.roleid IN ({$CFG->coursecontact})
                       $categoryclause
                 ORDER BY r.sortorder ASC, ctx.contextlevel ASC, ra.sortorder ASC";
         $rs = $DB->get_recordset_sql($sql, $params);
@@ -771,293 +694,6 @@ function get_courses_wmanagers($categoryid=0, $sort="c.sortorder ASC", $fields=a
     return $courses;
 }
 
-/**
- * Convenience function - lists courses that a user has access to view.
- *
- * For admins and others with access to "every" course in the system, we should
- * try to get courses with explicit RAs.
- *
- * NOTE: this function is heavily geared towards the perspective of the user
- *       passed in $userid. So it will hide courses that the user cannot see
- *       (for any reason) even if called from cron or from another $USER's
- *       perspective.
- *
- *       If you really want to know what courses are assigned to the user,
- *       without any hiding or scheming, call the lower-level
- *       get_user_courses_bycap().
- *
- *
- * Notes inherited from get_user_courses_bycap():
- *
- * - $fields is an array of fieldnames to ADD
- *   so name the fields you really need, which will
- *   be added and uniq'd
- *
- * - the course records have $c->context which is a fully
- *   valid context object. Saves you a query per course!
- *
- * @global object
- * @global object
- * @global object
- * @uses CONTEXT_SYSTEM
- * @uses CONTEXT_COURSE
- * @uses CONTEXT_COURSECAT
- * @param int $userid The user of interest
- * @param string $sort the sortorder in the course table
- * @param array $fields names of _additional_ fields to return (also accepts a string)
- * @param bool $doanything True if using the doanything flag
- * @param int $limit Maximum number of records to return, or 0 for unlimited
- * @return array Array of {@link $COURSE} of course objects
- */
-function get_my_courses($userid, $sort='visible DESC,sortorder ASC', $fields=NULL, $doanything=false,$limit=0) {
-    global $CFG, $USER, $DB;
-
-    // Guest account does not have any courses
-    if (isguestuser()) {
-        return(array());
-    }
-
-    $basefields = array('id', 'category', 'sortorder',
-                        'shortname', 'fullname', 'idnumber',
-                        'guest', 'startdate', 'visible',
-                        'newsitems',  'cost', 'enrol',
-                        'groupmode', 'groupmodeforce');
-
-    if (!is_null($fields) && is_string($fields)) {
-        if (empty($fields)) {
-            $fields = $basefields;
-        } else {
-            // turn the fields from a string to an array that
-            // get_user_courses_bycap() will like...
-            $fields = explode(',',$fields);
-            $fields = array_map('trim', $fields);
-            $fields = array_unique(array_merge($basefields, $fields));
-        }
-    } elseif (is_array($fields)) {
-        $fields = array_unique(array_merge($basefields, $fields));
-    } else {
-        $fields = $basefields;
-    }
-
-    $orderby = '';
-    $sort    = trim($sort);
-    if (!empty($sort)) {
-        $rawsorts = explode(',', $sort);
-        $sorts = array();
-        foreach ($rawsorts as $rawsort) {
-            $rawsort = trim($rawsort);
-            if (strpos($rawsort, 'c.') === 0) {
-                $rawsort = substr($rawsort, 2);
-            }
-            $sorts[] = trim($rawsort);
-        }
-        $sort = 'c.'.implode(',c.', $sorts);
-        $orderby = "ORDER BY $sort";
-    }
-
-    //
-    // Logged-in user - Check cached courses
-    //
-    // NOTE! it's a _string_ because
-    // - it's all we'll ever use
-    // - it serialises much more compact than an array
-    //   this a big concern here - cost of serialise
-    //   and unserialise gets huge as the session grows
-    //
-    // If the courses are too many - it won't be set
-    // for large numbers of courses, caching in the session
-    // has marginal benefits (costs too much, not
-    // worthwhile...) and we may hit SQL parser limits
-    // because we use IN()
-    //
-    if ($userid === $USER->id) {
-        if (isset($USER->loginascontext)
-            && $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
-            // list _only_ this course
-            // anything else is asking for trouble...
-            $courseids = $USER->loginascontext->instanceid;
-        } elseif (isset($USER->mycourses)
-                  && is_string($USER->mycourses)) {
-            if ($USER->mycourses === '') {
-                // empty str means: user has no courses
-                // ... so do the easy thing...
-                return array();
-            } else {
-                $courseids = $USER->mycourses;
-            }
-        }
-        if (isset($courseids)) {
-            // The data massaging here MUST be kept in sync with
-            // get_user_courses_bycap() so we return
-            // the same...
-            // (but here we don't need to check has_cap)
-            $coursefields = 'c.' .join(',c.', $fields);
-            list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
-            $sql = "SELECT $coursefields $ccselect, cc.path AS categorypath
-                      FROM {course} c
-                      JOIN {course_categories} cc ON c.category=cc.id
-                   $ccjoin
-                     WHERE c.id IN ($courseids)
-                  $orderby";
-            $rs = $DB->get_recordset_sql($sql);
-            $courses = array();
-            $cc = 0; // keep count
-            foreach ($rs as $c) {
-                // build the context obj
-                context_instance_preload($c);
-
-                if ($limit > 0 && $cc >= $limit) {
-                    break;
-                }
-
-                $courses[$c->id] = $c;
-                $cc++;
-            }
-            $rs->close();
-            return $courses;
-        }
-    }
-
-    // Non-cached - get accessinfo
-    if ($userid === $USER->id && isset($USER->access)) {
-        $accessinfo = $USER->access;
-    } else {
-        $accessinfo = get_user_access_sitewide($userid);
-    }
-
-
-    $courses = get_user_courses_bycap($userid, 'moodle/course:participate', $accessinfo,
-                                      $doanything, $sort, $fields,
-                                      $limit);
-
-    $cats = NULL;
-    // If we have to walk category visibility
-    // to eval course visibility, get the categories
-    if (empty($CFG->allowvisiblecoursesinhiddencategories)) {
-        list($ccselect, $ccjoin) = context_instance_preload_sql('cc.id', CONTEXT_COURSECAT, 'ctx');
-        $sql = "SELECT cc.id, cc.path, cc.visible $ccselect
-                  FROM {course_categories} cc
-               $ccjoin
-              ORDER BY cc.id";
-        $rs = $DB->get_recordset_sql($sql);
-
-        // Using a temporary array instead of $cats here, to avoid a "true" result when isnull($cats) further down
-        $categories = array();
-        foreach($rs as $course_cat) {
-            // build the context obj
-            context_instance_preload($course_cat);
-            $categories[$course_cat->id] = $course_cat;
-        }
-        $rs->close();
-
-        if (!empty($categories)) {
-            $cats = $categories;
-        }
-
-        unset($course_cat);
-    }
-    //
-    // Strangely, get_my_courses() is expected to return the
-    // array keyed on id, which messes up the sorting
-    // So do that, and also cache the ids in the session if appropriate
-    //
-    $kcourses = array();
-    $courses_count = count($courses);
-    $cacheids = NULL;
-    $vcatpaths = array();
-    if ($userid === $USER->id && $courses_count < 500) {
-        $cacheids = array();
-    }
-    for ($n=0; $n<$courses_count; $n++) {
-
-        //
-        // Check whether $USER (not $userid) can _actually_ see them
-        // Easy if $CFG->allowvisiblecoursesinhiddencategories
-        // is set, and we don't have to care about categories.
-        // Lots of work otherwise... (all in mem though!)
-        //
-        $cansee = false;
-        if (is_null($cats)) { // easy rules!
-            if ($courses[$n]->visible == true) {
-                $cansee = true;
-            } elseif (has_capability('moodle/course:viewhiddencourses',
-                                     $courses[$n]->context, $USER->id)) {
-                $cansee = true;
-            }
-        } else {
-            //
-            // Is the cat visible?
-            // we have to assume it _is_ visible
-            // so we can shortcut when we find a hidden one
-            //
-            $viscat = true;
-            $cpath = $courses[$n]->categorypath;
-            if (isset($vcatpaths[$cpath])) {
-                $viscat = $vcatpaths[$cpath];
-            } else {
-                $cpath = substr($cpath,1); // kill leading slash
-                $cpath = explode('/',$cpath);
-                $ccct  = count($cpath);
-                for ($m=0;$m<$ccct;$m++) {
-                    $ccid = $cpath[$m];
-                    if ($cats[$ccid]->visible==false) {
-                        $viscat = false;
-                        break;
-                    }
-                }
-                $vcatpaths[$courses[$n]->categorypath] = $viscat;
-            }
-
-            //
-            // Perhaps it's actually visible to $USER
-            // check moodle/category:viewhiddencategories
-            //
-            // The name isn't obvious, but the description says
-            // "See hidden categories" so the user shall see...
-            // But also check if the allowvisiblecoursesinhiddencategories setting is true, and check for course visibility
-            if ($viscat === false) {
-                $catctx = $cats[$courses[$n]->category]->context;
-                if (has_capability('moodle/category:viewhiddencategories', $catctx, $USER->id)) {
-                    $vcatpaths[$courses[$n]->categorypath] = true;
-                    $viscat = true;
-                } elseif ($CFG->allowvisiblecoursesinhiddencategories && $courses[$n]->visible == true) {
-                    $viscat = true;
-                }
-            }
-
-            //
-            // Decision matrix
-            //
-            if ($viscat === true) {
-                if ($courses[$n]->visible == true) {
-                    $cansee = true;
-                } elseif (has_capability('moodle/course:viewhiddencourses',
-                                        $courses[$n]->context, $USER->id)) {
-                    $cansee = true;
-                }
-            }
-        }
-        if ($cansee === true) {
-            $kcourses[$courses[$n]->id] = $courses[$n];
-            if (is_array($cacheids)) {
-                $cacheids[] = $courses[$n]->id;
-            }
-        }
-    }
-    if (is_array($cacheids)) {
-        // Only happens
-        // - for the logged in user
-        // - below the threshold (500)
-        // empty string is _valid_
-        $USER->mycourses = join(',',$cacheids);
-    } elseif ($userid === $USER->id && isset($USER->mycourses)) {
-        // cheap sanity check
-        unset($USER->mycourses);
-    }
-
-    return $kcourses;
-}
-
 /**
  * A list of courses that match a search
  *
@@ -2285,75 +1921,6 @@ function print_object($object) {
     echo '</pre>';
 }
 
-/**
- * Check whether a course is visible through its parents
- * path.
- *
- * Notes:
- *
- * - All we need from the course is ->category. _However_
- *   if the course object has a categorypath property,
- *   we'll save a dbquery
- *
- * - If we return false, you'll still need to check if
- *   the user can has the 'moodle/category:viewhiddencategories'
- *   capability...
- *
- * - Will generate 2 DB calls.
- *
- * - It does have a small local cache, however...
- *
- * - Do NOT call this over many courses as it'll generate
- *   DB traffic. Instead, see what get_my_courses() does.
- *
- * @global object
- * @global object
- * @staticvar array $mycache
- * @param object $course A course object
- * @return bool
- */
-function course_parent_visible($course = null) {
-    global $CFG, $DB;
-    //return true;
-    static $mycache;
-
-    if (!is_object($course)) {
-        return true;
-    }
-    if (!empty($CFG->allowvisiblecoursesinhiddencategories)) {
-        return true;
-    }
-
-    if (!isset($mycache)) {
-        $mycache = array();
-    } else {
-        // cast to force assoc array
-        $k = (string)$course->category;
-        if (isset($mycache[$k])) {
-            return $mycache[$k];
-        }
-    }
-
-    if (isset($course->categorypath)) {
-        $path = $course->categorypath;
-    } else {
-        $path = $DB->get_field('course_categories', 'path', array('id'=>$course->category));
-    }
-    $catids = substr($path,1); // strip leading slash
-    $catids = str_replace('/',',',$catids);
-
-    $sql = "SELECT MIN(visible)
-              FROM {course_categories}
-             WHERE id IN ($catids)";
-    $vis = $DB->get_field_sql($sql);
-
-    // cast to force assoc array
-    $k = (string)$course->category;
-    $mycache[$k] = $vis;
-
-    return $vis;
-}
-
 /**
  * This function is the official hook inside XMLDB stuff to delegate its debug to one
  * external function.
diff --git a/lib/db/access.php b/lib/db/access.php
index e1f520aab28c..8ca61bb328ec 100644
--- a/lib/db/access.php
+++ b/lib/db/access.php
@@ -596,19 +596,6 @@
         )
     ),
 
-    'moodle/role:unassignself' => array(
-
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_COURSE,
-        'archetypes' => array(
-            'student' => (empty($CFG->allowunenrol)) ? CAP_INHERIT : CAP_ALLOW,
-            'teacher' => CAP_ALLOW,
-            'editingteacher' => CAP_ALLOW,
-            'coursecreator' => CAP_ALLOW,
-            'manager' => CAP_ALLOW
-        )
-    ),
-
     'moodle/role:switchroles' => array(
 
         'riskbitmask' => RISK_XSS | RISK_PERSONAL,
@@ -725,25 +712,37 @@
         )
     ),
 
-    /* originally this capability was called moodle/course:view,
-     * but since 2.0 it is used for access to course without the enrolment
-     */
-    'moodle/course:participate' => array(
+    'moodle/course:view' => array(
 
         'captype' => 'read',
         'contextlevel' => CONTEXT_COURSE,
         'archetypes' => array(
-            'student' => CAP_ALLOW,
-            'teacher' => CAP_ALLOW,
-            'editingteacher' => CAP_ALLOW
+            'manager' => CAP_ALLOW,
         )
     ),
 
-    'moodle/course:view' => array(
+    /* review course enrolments - no group restrictions, it is really full access to all participants info*/
+    'moodle/course:enrolreview' => array(
+
+        'riskbitmask' => RISK_PERSONAL,
 
         'captype' => 'read',
         'contextlevel' => CONTEXT_COURSE,
         'archetypes' => array(
+            'editingteacher' => CAP_ALLOW,
+            'manager' => CAP_ALLOW,
+        )
+    ),
+
+    /* add, remove, hide enrol instances in courses */
+    'moodle/course:enrolconfig' => array(
+
+        'riskbitmask' => RISK_PERSONAL,
+
+        'captype' => 'write',
+        'contextlevel' => CONTEXT_COURSE,
+        'archetypes' => array(
+            'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW,
         )
     ),
@@ -791,6 +790,7 @@
         'captype' => 'write',
         'contextlevel' => CONTEXT_COURSE,
         'archetypes' => array(
+            'editingteacher' => CAP_ALLOW,
             'manager' => CAP_ALLOW
         )
     ),
@@ -819,18 +819,6 @@
         )
     ),
 
-    'moodle/course:managemetacourse' => array(
-
-        'riskbitmask' => RISK_XSS | RISK_PERSONAL,
-
-        'captype' => 'write',
-        'contextlevel' => CONTEXT_COURSE,
-        'archetypes' => array(
-            'editingteacher' => CAP_ALLOW,
-            'manager' => CAP_ALLOW
-        )
-    ),
-
     'moodle/course:activityvisibility' => array(
 
         'captype' => 'write',
diff --git a/lib/db/events.php b/lib/db/events.php
index 390797749742..6ba651876ed2 100644
--- a/lib/db/events.php
+++ b/lib/db/events.php
@@ -13,7 +13,7 @@
 //                                                                       //
 // This program 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 2 of the License, or     //
+// the Free Software Foundation; either version 3 of the License, or     //
 // (at your option) any later version.                                   //
 //                                                                       //
 // This program is distributed in the hope that it will be useful,       //
@@ -29,13 +29,15 @@
 
 /* List of handlers */
 
-$handlers = array (
+$handlers = array(
 
 /*
  * portfolio queued event - for non interactive file transfers
- * NOTE: this is a hack, please do not add any more things like this here
- *       (it is just abusing cron to do very time consuming things which is wrong)
-*/
+ * NOTE: this is a HACK, please do not add any more things like this here
+ *       (it is just abusing cron to do very time consuming things which is wrong any way)
+ *
+ * TODO: this has to be moved into separate queueing framework....
+ */
     'portfolio_send' => array (
         'handlerfile'      => '/lib/portfolio.php',
         'handlerfunction'  => 'portfolio_handle_event',    // argument to call_user_func(), could be an array
@@ -43,7 +45,7 @@
     ),
 
 
-/* more go here */
+/* no more here please, core should not consume any events!!!!!!! */
 );
 
 
@@ -68,6 +70,20 @@
 course_content_removed - object course table record
 course_deleted - object course table record
 
+user_enrolled - object record from user_enrolments table + courseid,enrol
+user_unenrol_modified - object record from user_enrolments table + courseid,enrol
+user_unenrolled - object record from user_enrolments table + courseid,enrol,lastenrol
+
+==== cohort related events ===
+
+
+cohort_added - object cohort table record
+cohort_updated - object cohort table record
+cohort_deleted - object cohort table record
+
+cohort_member_added - object cohortid, userid properties
+cohort_member_removed - object cohortid, userid properties
+
 ==== group related events ====
 
 groups_group_created - object groups_group table record
@@ -97,8 +113,4 @@
 modulename_mod_created - int courseid, int cmid - happens when a module is created -eg quiz_mod_created
 modulename_mod_updated - int courseid, int cmid - happens when a module is updated -eg quiz_mod_updated
 
-==== Assignment Related events ====
-
-assignment_finalize_sent - object course, object user, object cm, object assignment, fileareaname
-assignment_file_sent - object course, object user, object cm, object assignment, object file
 */
diff --git a/lib/db/install.php b/lib/db/install.php
index e7eba9a2459c..0415eae15447 100644
--- a/lib/db/install.php
+++ b/lib/db/install.php
@@ -45,8 +45,7 @@ function xmldb_main_install() {
         'rolesactive'           => '0', // marks fully set up system
         'auth'                  => 'email',
         'auth_pop3mailbox'      => 'INBOX',
-        'enrol'                 => 'manual',
-        'enrol_plugins_enabled' => 'manual',
+        'enrol_plugins_enabled' => 'manual,guest,self,cohort',
         'style'                 => 'default',
         'template'              => 'default',
         'theme'                 => theme_config::DEFAULT_THEME,
@@ -126,8 +125,8 @@ function xmldb_main_install() {
     update_log_display_entry('course', 'user report', 'user', 'CONCAT(firstname,\' \',lastname)');
     update_log_display_entry('course', 'view', 'course', 'fullname');
     update_log_display_entry('course', 'update', 'course', 'fullname');
-    update_log_display_entry('course', 'enrol', 'course', 'fullname');
-    update_log_display_entry('course', 'unenrol', 'course', 'fullname');
+    update_log_display_entry('course', 'enrol', 'user', 'course', 'fullname'); // there should be some way to store user id of the enrolled user!
+    update_log_display_entry('course', 'unenrol', 'user', 'course', 'fullname'); // there should be some way to store user id of the enrolled user!
     update_log_display_entry('course', 'report log', 'course', 'fullname');
     update_log_display_entry('course', 'report live', 'course', 'fullname');
     update_log_display_entry('course', 'report outline', 'course', 'fullname');
diff --git a/lib/db/install.xml b/lib/db/install.xml
index b807afea341b..e115725e07ca 100644
--- a/lib/db/install.xml
+++ b/lib/db/install.xml
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="lib/db" VERSION="20100527" COMMENT="XMLDB file for core Moodle tables"
+<XMLDB PATH="lib/db" VERSION="20100615" COMMENT="XMLDB file for core Moodle tables"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
 >
@@ -70,9 +70,8 @@
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="category"/>
         <FIELD NAME="category" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="sortorder"/>
-        <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="category" NEXT="password"/>
-        <FIELD NAME="password" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="sortorder" NEXT="fullname"/>
-        <FIELD NAME="fullname" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" PREVIOUS="password" NEXT="shortname"/>
+        <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="category" NEXT="fullname"/>
+        <FIELD NAME="fullname" TYPE="char" LENGTH="254" NOTNULL="true" SEQUENCE="false" PREVIOUS="sortorder" NEXT="shortname"/>
         <FIELD NAME="shortname" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" PREVIOUS="fullname" NEXT="idnumber"/>
         <FIELD NAME="idnumber" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" PREVIOUS="shortname" NEXT="summary"/>
         <FIELD NAME="summary" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" PREVIOUS="idnumber" NEXT="summaryformat"/>
@@ -80,38 +79,26 @@
         <FIELD NAME="format" TYPE="char" LENGTH="10" NOTNULL="true" DEFAULT="topics" SEQUENCE="false" PREVIOUS="summaryformat" NEXT="showgrades"/>
         <FIELD NAME="showgrades" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="format" NEXT="modinfo"/>
         <FIELD NAME="modinfo" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" PREVIOUS="showgrades" NEXT="newsitems"/>
-        <FIELD NAME="newsitems" TYPE="int" LENGTH="5" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="modinfo" NEXT="guest"/>
-        <FIELD NAME="guest" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="newsitems" NEXT="startdate"/>
-        <FIELD NAME="startdate" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="guest" NEXT="enrolperiod"/>
-        <FIELD NAME="enrolperiod" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="startdate" NEXT="numsections"/>
-        <FIELD NAME="numsections" TYPE="int" LENGTH="5" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="enrolperiod" NEXT="marker"/>
+        <FIELD NAME="newsitems" TYPE="int" LENGTH="5" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="modinfo" NEXT="startdate"/>
+        <FIELD NAME="startdate" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="newsitems" NEXT="numsections"/>
+        <FIELD NAME="numsections" TYPE="int" LENGTH="5" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="startdate" NEXT="marker"/>
         <FIELD NAME="marker" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="numsections" NEXT="maxbytes"/>
         <FIELD NAME="maxbytes" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="marker" NEXT="legacyfiles"/>
         <FIELD NAME="legacyfiles" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="course files are not necessary any more: 0 no legacy files, 1 legacy files disabled, 2 legacy files enabled" PREVIOUS="maxbytes" NEXT="showreports"/>
         <FIELD NAME="showreports" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="legacyfiles" NEXT="visible"/>
-        <FIELD NAME="visible" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="showreports" NEXT="hiddensections"/>
-        <FIELD NAME="hiddensections" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="visible" NEXT="groupmode"/>
+        <FIELD NAME="visible" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="showreports" NEXT="visibleold"/>
+        <FIELD NAME="visibleold" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" COMMENT="the state of visible field when hiding parent category, this helps us to recover hidden states when unhiding the parent category later" PREVIOUS="visible" NEXT="hiddensections"/>
+        <FIELD NAME="hiddensections" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="visibleold" NEXT="groupmode"/>
         <FIELD NAME="groupmode" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="hiddensections" NEXT="groupmodeforce"/>
         <FIELD NAME="groupmodeforce" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="groupmode" NEXT="defaultgroupingid"/>
         <FIELD NAME="defaultgroupingid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="default grouping used in course modules, does not have key intentionally" PREVIOUS="groupmodeforce" NEXT="lang"/>
         <FIELD NAME="lang" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="defaultgroupingid" NEXT="theme"/>
-        <FIELD NAME="theme" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="lang" NEXT="cost"/>
-        <FIELD NAME="cost" TYPE="char" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="theme" NEXT="currency"/>
-        <FIELD NAME="currency" TYPE="char" LENGTH="3" NOTNULL="true" DEFAULT="USD" SEQUENCE="false" PREVIOUS="cost" NEXT="timecreated"/>
-        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="currency" NEXT="timemodified"/>
-        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timecreated" NEXT="metacourse"/>
-        <FIELD NAME="metacourse" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified" NEXT="requested"/>
-        <FIELD NAME="requested" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="metacourse" NEXT="restrictmodules"/>
-        <FIELD NAME="restrictmodules" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="requested" NEXT="expirynotify"/>
-        <FIELD NAME="expirynotify" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="restrictmodules" NEXT="expirythreshold"/>
-        <FIELD NAME="expirythreshold" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="expirynotify" NEXT="notifystudents"/>
-        <FIELD NAME="notifystudents" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="expirythreshold" NEXT="enrollable"/>
-        <FIELD NAME="enrollable" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="1" SEQUENCE="false" PREVIOUS="notifystudents" NEXT="enrolstartdate"/>
-        <FIELD NAME="enrolstartdate" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="enrollable" NEXT="enrolenddate"/>
-        <FIELD NAME="enrolenddate" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="enrolstartdate" NEXT="enrol"/>
-        <FIELD NAME="enrol" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" PREVIOUS="enrolenddate" NEXT="defaultrole"/>
-        <FIELD NAME="defaultrole" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="The default role given to participants who self-enrol" PREVIOUS="enrol" NEXT="enablecompletion"/>
-        <FIELD NAME="enablecompletion" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = allow use of 'activty completion' progress-tracking on this course. 0 = disable activity completion tracking on this course." PREVIOUS="defaultrole" NEXT="completionstartonenrol"/>
+        <FIELD NAME="theme" TYPE="char" LENGTH="50" NOTNULL="true" SEQUENCE="false" PREVIOUS="lang" NEXT="timecreated"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="theme" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timecreated" NEXT="requested"/>
+        <FIELD NAME="requested" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified" NEXT="restrictmodules"/>
+        <FIELD NAME="restrictmodules" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="requested" NEXT="enablecompletion"/>
+        <FIELD NAME="enablecompletion" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = allow use of 'completion' progress-tracking on this course. 0 = disable completion tracking on this course." PREVIOUS="restrictmodules" NEXT="completionstartonenrol"/>
         <FIELD NAME="completionstartonenrol" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="1 = allow use of 'activty completion' progress-tracking on this course. 0 = disable activity completion tracking on this course." PREVIOUS="enablecompletion" NEXT="completionnotify"/>
         <FIELD NAME="completionnotify" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Notify users when they complete this course" PREVIOUS="completionstartonenrol"/>
       </FIELDS>
@@ -133,8 +120,9 @@
         <FIELD NAME="parent" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="descriptionformat" NEXT="sortorder"/>
         <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="parent" NEXT="coursecount"/>
         <FIELD NAME="coursecount" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="sortorder" NEXT="visible"/>
-        <FIELD NAME="visible" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" PREVIOUS="coursecount" NEXT="timemodified"/>
-        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="visible" NEXT="depth"/>
+        <FIELD NAME="visible" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" PREVIOUS="coursecount" NEXT="visibleold"/>
+        <FIELD NAME="visibleold" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="false" DEFAULT="1" SEQUENCE="false" COMMENT="the state of visible field when hiding parent category, this helps us to recover hidden states when unhiding the parent category later" PREVIOUS="visible" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="visibleold" NEXT="depth"/>
         <FIELD NAME="depth" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified" NEXT="path"/>
         <FIELD NAME="path" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="depth" NEXT="theme"/>
         <FIELD NAME="theme" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" COMMENT="Theme for the category" PREVIOUS="path"/>
@@ -237,7 +225,7 @@
         <INDEX NAME="timecompleted" UNIQUE="false" FIELDS="timecompleted" PREVIOUS="course"/>
       </INDEXES>
     </TABLE>
-    <TABLE NAME="course_display" COMMENT="Stores info about how to display the course" PREVIOUS="course_completions" NEXT="course_meta">
+    <TABLE NAME="course_display" COMMENT="Stores info about how to display the course" PREVIOUS="course_completions" NEXT="enrol">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
         <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="userid"/>
@@ -251,21 +239,67 @@
         <INDEX NAME="course_userid" UNIQUE="false" FIELDS="course, userid"/>
       </INDEXES>
     </TABLE>
-    <TABLE NAME="course_meta" COMMENT="to store meta-courses relations" PREVIOUS="course_display" NEXT="course_modules">
+    <TABLE NAME="enrol" COMMENT="Instances of enrolment plugins used in courses, fields marked as custom have a plugin defined meaning, core does not touch them. Create a new linked table if you need even more custom fields." PREVIOUS="course_display" NEXT="user_enrolments">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="enrol"/>
+        <FIELD NAME="enrol" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" PREVIOUS="id" NEXT="status"/>
+        <FIELD NAME="status" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="0..9 are system constants, 0 means active enrolment, see ENROL_STATUS_* constants, plugins may define own status greater than 10" PREVIOUS="enrol" NEXT="courseid"/>
+        <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="status" NEXT="sortorder"/>
+        <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="order of enrol plugins in each course" PREVIOUS="courseid" NEXT="name"/>
+        <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Optional instance name" PREVIOUS="sortorder" NEXT="enrolperiod"/>
+        <FIELD NAME="enrolperiod" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Custom - enrolment duration" PREVIOUS="name" NEXT="enrolstartdate"/>
+        <FIELD NAME="enrolstartdate" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Custom - start of self enrolment" PREVIOUS="enrolperiod" NEXT="enrolenddate"/>
+        <FIELD NAME="enrolenddate" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Custom - end of enrolment" PREVIOUS="enrolstartdate" NEXT="expirynotify"/>
+        <FIELD NAME="expirynotify" TYPE="int" LENGTH="1" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Custom - notify users before expiration" PREVIOUS="enrolenddate" NEXT="expirythreshold"/>
+        <FIELD NAME="expirythreshold" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Custom - when should be the participants notified" PREVIOUS="expirynotify" NEXT="notifyall"/>
+        <FIELD NAME="notifyall" TYPE="int" LENGTH="1" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Custom - Notify both participant and person responsible for enrolments" PREVIOUS="expirythreshold" NEXT="password"/>
+        <FIELD NAME="password" TYPE="char" LENGTH="50" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - enrolment or access password" PREVIOUS="notifyall" NEXT="cost"/>
+        <FIELD NAME="cost" TYPE="char" LENGTH="20" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - enrolment cost" PREVIOUS="password" NEXT="currency"/>
+        <FIELD NAME="currency" TYPE="char" LENGTH="3" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - cost currency" PREVIOUS="cost" NEXT="roleid"/>
+        <FIELD NAME="roleid" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Custom - the default role given to participants who self-enrol" PREVIOUS="currency" NEXT="customint1"/>
+        <FIELD NAME="customint1" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="roleid" NEXT="customint2"/>
+        <FIELD NAME="customint2" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint1" NEXT="customint3"/>
+        <FIELD NAME="customint3" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint2" NEXT="customint4"/>
+        <FIELD NAME="customint4" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" COMMENT="Custom - general int" PREVIOUS="customint3" NEXT="customchar1"/>
+        <FIELD NAME="customchar1" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general short name" PREVIOUS="customint4" NEXT="customchar2"/>
+        <FIELD NAME="customchar2" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general short name" PREVIOUS="customchar1" NEXT="customdec1"/>
+        <FIELD NAME="customdec1" TYPE="number" LENGTH="12" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" DECIMALS="7" COMMENT="Custom - general decimal" PREVIOUS="customchar2" NEXT="customdec2"/>
+        <FIELD NAME="customdec2" TYPE="number" LENGTH="12" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" DECIMALS="7" COMMENT="Custom - general decimal" PREVIOUS="customdec1" NEXT="customtext1"/>
+        <FIELD NAME="customtext1" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general text" PREVIOUS="customdec2" NEXT="customtext2"/>
+        <FIELD NAME="customtext2" TYPE="text" LENGTH="big" NOTNULL="false" SEQUENCE="false" COMMENT="Custom - general text" PREVIOUS="customtext1" NEXT="timecreated"/>
+        <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="customtext2" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timecreated"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="courseid"/>
+        <KEY NAME="courseid" TYPE="foreign" FIELDS="courseid" REFTABLE="course" REFFIELDS="id" PREVIOUS="primary"/>
+      </KEYS>
+      <INDEXES>
+        <INDEX NAME="enrol" UNIQUE="false" FIELDS="enrol"/>
+      </INDEXES>
+    </TABLE>
+    <TABLE NAME="user_enrolments" COMMENT="Users participating in courses (aka enrolled users) - everybody who is participating/visible in course, that means both teachers and students" PREVIOUS="enrol" NEXT="course_modules">
       <FIELDS>
-        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="parent_course"/>
-        <FIELD NAME="parent_course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="child_course"/>
-        <FIELD NAME="child_course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="parent_course"/>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="status"/>
+        <FIELD NAME="status" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="0..9 are system constants, 0 means active participation, see ENROL_PARTICIPATION_* constants, plugins may define own status greater than 10" PREVIOUS="id" NEXT="enrolid"/>
+        <FIELD NAME="enrolid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="status" NEXT="userid"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="enrolid" NEXT="timestart"/>
+        <FIELD NAME="timestart" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="timeend"/>
+        <FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="2147483647" SEQUENCE="false" PREVIOUS="timestart" NEXT="modifierid"/>
+        <FIELD NAME="modifierid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timeend" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="modifierid"/>
       </FIELDS>
       <KEYS>
-        <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="enrolid"/>
+        <KEY NAME="enrolid" TYPE="foreign" FIELDS="enrolid" REFTABLE="enrol" REFFIELDS="id" PREVIOUS="primary" NEXT="userid"/>
+        <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="enrolid" NEXT="modifierid"/>
+        <KEY NAME="modifierid" TYPE="foreign" FIELDS="modifierid" REFTABLE="user" REFFIELDS="id" PREVIOUS="userid"/>
       </KEYS>
       <INDEXES>
-        <INDEX NAME="parent_course" UNIQUE="false" FIELDS="parent_course" NEXT="child_course"/>
-        <INDEX NAME="child_course" UNIQUE="false" FIELDS="child_course" PREVIOUS="parent_course"/>
+        <INDEX NAME="enrolid-userid" UNIQUE="true" FIELDS="enrolid, userid" COMMENT="Only one enrolment per plugin allowed"/>
       </INDEXES>
     </TABLE>
-    <TABLE NAME="course_modules" COMMENT="course_modules table retrofitted from MySQL" PREVIOUS="course_meta" NEXT="course_modules_availability">
+    <TABLE NAME="course_modules" COMMENT="course_modules table retrofitted from MySQL" PREVIOUS="user_enrolments" NEXT="course_modules_availability">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="course"/>
         <FIELD NAME="course" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="module"/>
@@ -1066,19 +1100,17 @@
         <INDEX NAME="roleid-allowoverride" UNIQUE="true" FIELDS="roleid, allowswitch" COMMENT="Each pair (roleid, allowswitch) must appear at most once."/>
       </INDEXES>
     </TABLE>
-    <TABLE NAME="role_assignments" COMMENT="assigning roles to different context" PREVIOUS="role_allow_switch" NEXT="role_capabilities">
+    <TABLE NAME="role_assignments" COMMENT="assigning roles in different context" PREVIOUS="role_allow_switch" NEXT="role_capabilities">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="roleid"/>
         <FIELD NAME="roleid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="contextid"/>
         <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="roleid" NEXT="userid"/>
-        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="contextid" NEXT="hidden"/>
-        <FIELD NAME="hidden" TYPE="int" LENGTH="1" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="timestart"/>
-        <FIELD NAME="timestart" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="hidden" NEXT="timeend"/>
-        <FIELD NAME="timeend" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timestart" NEXT="timemodified"/>
-        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timeend" NEXT="modifierid"/>
-        <FIELD NAME="modifierid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified" NEXT="enrol"/>
-        <FIELD NAME="enrol" TYPE="char" LENGTH="20" NOTNULL="true" SEQUENCE="false" PREVIOUS="modifierid" NEXT="sortorder"/>
-        <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="enrol"/>
+        <FIELD NAME="userid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="contextid" NEXT="timemodified"/>
+        <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="userid" NEXT="modifierid"/>
+        <FIELD NAME="modifierid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="timemodified" NEXT="component"/>
+        <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" COMMENT="plugin responsible responsible for role assignment, empty when manually assigned" PREVIOUS="modifierid" NEXT="itemidid"/>
+        <FIELD NAME="itemid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" COMMENT="Id of enrolment/auth instance responsible for this role assignment" PREVIOUS="component" NEXT="sortorder"/>
+        <FIELD NAME="sortorder" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="itemidid"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="roleid"/>
@@ -1087,8 +1119,7 @@
         <KEY NAME="userid" TYPE="foreign" FIELDS="userid" REFTABLE="user" REFFIELDS="id" PREVIOUS="contextid"/>
       </KEYS>
       <INDEXES>
-        <INDEX NAME="contextid-roleid-userid" UNIQUE="true" FIELDS="contextid, roleid, userid" NEXT="sortorder"/>
-        <INDEX NAME="sortorder" UNIQUE="false" FIELDS="sortorder" PREVIOUS="contextid-roleid-userid"/>
+        <INDEX NAME="sortorder" UNIQUE="false" FIELDS="sortorder"/>
       </INDEXES>
     </TABLE>
     <TABLE NAME="role_capabilities" COMMENT="permission has to be signed, overriding a capability for a particular role in a particular context" PREVIOUS="role_assignments" NEXT="role_names">
@@ -2013,7 +2044,7 @@
         <FIELD NAME="idnumber" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false" COMMENT="Unique identifier of a cohort, useful especially for mapping to external entities" PREVIOUS="name" NEXT="description"/>
         <FIELD NAME="description" TYPE="text" LENGTH="small" NOTNULL="false" SEQUENCE="false" COMMENT="Standard description text box" PREVIOUS="idnumber" NEXT="descriptionformat"/>
         <FIELD NAME="descriptionformat" TYPE="int" LENGTH="2" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="description" NEXT="component"/>
-        <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="false" SEQUENCE="false" COMMENT="Component (plugintype_pluignname) that manages the cohort, manual modifications are allowed only when set to NULL" PREVIOUS="descriptionformat" NEXT="timecreated"/>
+        <FIELD NAME="component" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false" COMMENT="Component (plugintype_pluignname) that manages the cohort, manual modifications are allowed only when set to NULL" PREVIOUS="descriptionformat" NEXT="timecreated"/>
         <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="component" NEXT="timemodified"/>
         <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="timecreated"/>
       </FIELDS>
@@ -2291,7 +2322,7 @@
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="repository_instance_config" COMMENT="The config for intances" PREVIOUS="repository_instances" NEXT="backup_courses">
+    <TABLE NAME="repository_instance_config" COMMENT="The config for intances" PREVIOUS="repository_instances" NEXT="backup_files">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="instanceid"/>
         <FIELD NAME="instanceid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" PREVIOUS="id" NEXT="name"/>
@@ -2302,7 +2333,35 @@
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
       </KEYS>
     </TABLE>
-    <TABLE NAME="backup_courses" COMMENT="To store every course backup status" PREVIOUS="repository_instance_config" NEXT="backup_log">
+    <TABLE NAME="backup_files" COMMENT="To store and recode ids to user and course files" PREVIOUS="repository_instance_config" NEXT="backup_ids">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="backup_code"/>
+        <FIELD NAME="backup_code" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="file_type"/>
+        <FIELD NAME="file_type" TYPE="char" LENGTH="10" NOTNULL="true" SEQUENCE="false" PREVIOUS="backup_code" NEXT="path"/>
+        <FIELD NAME="path" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false" PREVIOUS="file_type" NEXT="old_id"/>
+        <FIELD NAME="old_id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="path" NEXT="new_id"/>
+        <FIELD NAME="new_id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="old_id"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="backup_code-file_type-path"/>
+        <KEY NAME="backup_code-file_type-path" TYPE="unique" FIELDS="backup_code, file_type, path" PREVIOUS="primary"/>
+      </KEYS>
+    </TABLE>
+    <TABLE NAME="backup_ids" COMMENT="To store and convert ids in backup/restore" PREVIOUS="backup_files" NEXT="backup_courses">
+      <FIELDS>
+        <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="backup_code"/>
+        <FIELD NAME="backup_code" TYPE="int" LENGTH="12" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="table_name"/>
+        <FIELD NAME="table_name" TYPE="char" LENGTH="30" NOTNULL="true" SEQUENCE="false" PREVIOUS="backup_code" NEXT="old_id"/>
+        <FIELD NAME="old_id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="table_name" NEXT="new_id"/>
+        <FIELD NAME="new_id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="old_id" NEXT="info"/>
+        <FIELD NAME="info" TYPE="text" LENGTH="medium" NOTNULL="true" SEQUENCE="false" PREVIOUS="new_id"/>
+      </FIELDS>
+      <KEYS>
+        <KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="backup_code-table_name-old_id"/>
+        <KEY NAME="backup_code-table_name-old_id" TYPE="unique" FIELDS="backup_code, table_name, old_id" PREVIOUS="primary"/>
+      </KEYS>
+    </TABLE>
+    <TABLE NAME="backup_courses" COMMENT="To store every course backup status" PREVIOUS="backup_ids" NEXT="backup_log">
       <FIELDS>
         <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="courseid"/>
         <FIELD NAME="courseid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" DEFAULT="0" SEQUENCE="false" PREVIOUS="id" NEXT="laststarttime"/>
diff --git a/lib/db/services.php b/lib/db/services.php
index 24673f29ea98..cd591f32c22e 100644
--- a/lib/db/services.php
+++ b/lib/db/services.php
@@ -131,19 +131,28 @@
         'type'        => 'write',
     ),
 
-    'moodle_enrol_role_assign' => array(
+    'moodle_enrol_get_enrolled_users' => array(
+        'classname'   => 'moodle_enrol_external',
+        'methodname'  => 'get_enrolled_users',
+        'classpath'   => 'enrol/externallib.php',
+        'description' => 'Get list of course participants',
+        'type'        => 'read',
+    ),
+
+    'moodle_role_assign' => array(
         'classname'   => 'moodle_enrol_external',
         'methodname'  => 'role_assign',
         'classpath'   => 'enrol/externallib.php',
-        'description' => 'Enrol users.',
+        'description' => 'Manual role assignments.',
         'type'        => 'write',
     ),
 
-    'moodle_enrol_role_unassign' => array(
+    'moodle_role_unassign' => array(
         'classname'   => 'moodle_enrol_external',
         'methodname'  => 'role_unassign',
         'classpath'   => 'enrol/externallib.php',
-        'description' => 'Unenrol users.',
+        'description' => 'Manual role unassignments.',
         'type'        => 'write',
     ),
+
 );
diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php
index 34e46505c8ac..2d073d78b963 100644
--- a/lib/db/upgrade.php
+++ b/lib/db/upgrade.php
@@ -897,78 +897,6 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint($result, 2008101300);
     }
 
-    /// New table for storing which roles can be assigned in which contexts.
-    if ($result && $oldversion < 2008110601) {
-
-    /// Define table role_context_levels to be created
-        $table = new xmldb_table('role_context_levels');
-
-    /// Adding fields to table role_context_levels
-        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
-        $table->add_field('roleid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
-        $table->add_field('contextlevel', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
-
-    /// Adding keys to table role_context_levels
-        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
-        $table->add_key('contextlevel-roleid', XMLDB_KEY_UNIQUE, array('contextlevel', 'roleid'));
-        $table->add_key('roleid', XMLDB_KEY_FOREIGN, array('roleid'), 'role', array('id'));
-
-    /// Conditionally launch create table for role_context_levels
-        if (!$dbman->table_exists($table)) {
-            $dbman->create_table($table);
-        }
-
-    /// Main savepoint reached
-        upgrade_main_savepoint($result, 2008110601);
-    }
-
-    /// Now populate the role_context_levels table with the defaults that match
-    /// moodle_install_roles, and any other combinations that exist in this system.
-    if ($result && $oldversion < 2008110602) {
-        $roleids = $DB->get_records_menu('role', array(), '', 'shortname,id');
-
-    /// Defaults, should match moodle_install_roles.
-        $rolecontextlevels = array();
-        if (isset($roleids['coursecreator'])) {
-            $rolecontextlevels[$roleids['coursecreator']] = get_default_contextlevels('coursecreator');
-        }
-        if (isset($roleids['editingteacher'])) {
-            $rolecontextlevels[$roleids['editingteacher']] = get_default_contextlevels('editingteacher');
-        }
-        if (isset($roleids['teacher'])) {
-            $rolecontextlevels[$roleids['teacher']] = get_default_contextlevels('teacher');
-        }
-        if (isset($roleids['student'])) {
-            $rolecontextlevels[$roleids['student']] = get_default_contextlevels('student');
-        }
-        if (isset($roleids['guest'])) {
-            $rolecontextlevels[$roleids['guest']] = get_default_contextlevels('guest');
-        }
-        if (isset($roleids['user'])) {
-            $rolecontextlevels[$roleids['user']] = get_default_contextlevels('user');
-        }
-
-    /// See what other role assignments are in this database, extend the allowed
-    /// lists to allow them too.
-        $existingrolecontextlevels = $DB->get_recordset_sql('SELECT DISTINCT ra.roleid, con.contextlevel FROM
-                {role_assignments} ra JOIN {context} con ON ra.contextid = con.id');
-        foreach ($existingrolecontextlevels as $rcl) {
-            if (!isset($rolecontextlevels[$rcl->roleid])) {
-                $rolecontextlevels[$rcl->roleid] = array($rcl->contextlevel);
-            } else if (!in_array($rcl->contextlevel, $rolecontextlevels[$rcl->roleid])) {
-                $rolecontextlevels[$rcl->roleid][] = $rcl->contextlevel;
-            }
-        }
-
-    /// Put the data into the database.
-        foreach ($rolecontextlevels as $roleid => $contextlevels) {
-            set_role_contextlevels($roleid, $contextlevels);
-        }
-
-    /// Main savepoint reached
-        upgrade_main_savepoint($result, 2008110602);
-    }
-
     /// Drop the deprecated teacher, teachers, student and students columns from the course table.
     if ($result && $oldversion < 2008111200) {
         $table = new xmldb_table('course');
@@ -1129,14 +1057,6 @@ function xmldb_main_upgrade($oldversion) {
         require_once($CFG->dirroot . '/course/lib.php');
         rebuild_course_cache(0, true);
 
-    /// For developer upgrades, turn on the conditional activities and completion
-    /// features automatically (to gain more testing)
-//TODO: remove before 2.0 final!
-        if (debugging('', DEBUG_DEVELOPER)) {
-            set_config('enableavailability', 1);
-            set_config('enablecompletion', 1);
-        }
-
     /// Main savepoint reached
         upgrade_main_savepoint($result, 2008121701);
     }
@@ -3304,14 +3224,6 @@ function xmldb_main_upgrade($oldversion) {
             $DB->delete_records('role_capabilities', array('capability'=>'moodle/site:config', 'roleid'=>$manager->id)); // only site admins may configure servers
             // note: doanything and legacy caps are deleted automatically, they get moodle/course:view later at the end of the upgrade
 
-            // set usable contexts
-            $DB->delete_records('role_context_levels', array('roleid'=>$manager->id));
-            $assignlevels = array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE);
-            foreach ($assignlevels as $assignlevel) {
-                $record = (object)array('roleid'=>$manager->id, 'contextlevel'=>$assignlevel);
-                $DB->insert_record('role_context_levels', $record);
-            }
-
             // remove manager role assignments bellow the course context level - admin role was never intended for activities and blocks,
             // the problem is that those assignments would not be visible after upgrade and old style admins in activities make no sense anyway
             $DB->delete_records_select('role_assignments', "roleid = :manager AND contextid IN (SELECT id FROM {context} WHERE contextlevel > 50)", array('manager'=>$manager->id));
@@ -3385,33 +3297,99 @@ function xmldb_main_upgrade($oldversion) {
                     unset_config('guestroleid');
                 }
             } else {
-                upgrade_log(UPGRADE_LOG_NOTICE, null, 'Role specified in Default guest role (guestroleid) doeas not exist, setting cleared.');
+                upgrade_log(UPGRADE_LOG_NOTICE, null, 'Role specified in Default guest role (guestroleid) does not exist, setting cleared.');
                 unset_config('guestroleid');
             }
         }
         // remove all roles of the guest account - the only way to change it is to override the guest role, sorry
-        // the guest account gets all the role assignemnts on the fly whcih works fine in has_capability(),
+        // the guest account gets all the role assignments on the fly which works fine in has_capability(),
         $DB->delete_records_select('role_assignments', "userid IN (SELECT id FROM {user} WHERE username = 'guest')");
 
         upgrade_main_savepoint($result, 2010033102.08);
     }
 
+    /// New table for storing which roles can be assigned in which contexts.
     if ($result && $oldversion < 2010033102.09) {
-        // For MDL-17501. Ensure that any role that has moodle/course:update also has moodle/course:visibility.
-        // Get the roles with 'moodle/course:update'.
-        $systemcontext = get_context_instance(CONTEXT_SYSTEM);
-        $roles = get_roles_with_capability('moodle/course:update', CAP_ALLOW, $systemcontext);
 
-        // Give those roles 'moodle/course:visibility'.
+    /// Define table role_context_levels to be created
+        $table = new xmldb_table('role_context_levels');
+
+    /// Adding fields to table role_context_levels
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('roleid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('contextlevel', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+
+    /// Adding keys to table role_context_levels
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('contextlevel-roleid', XMLDB_KEY_UNIQUE, array('contextlevel', 'roleid'));
+        $table->add_key('roleid', XMLDB_KEY_FOREIGN, array('roleid'), 'role', array('id'));
+
+    /// Conditionally launch create table for role_context_levels
+        if (!$dbman->table_exists($table)) {
+            $dbman->create_table($table);
+        }
+
+    /// Main savepoint reached
+        upgrade_main_savepoint($result, 2010033102.09);
+    }
+
+    if ($result && $oldversion < 2010033102.10) {
+        // Now populate the role_context_levels table with the default values
+        // NOTE: do not use accesslib methods here
+
+        $rolecontextlevels = array();
+        $defaults = array('manager'        => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT, CONTEXT_COURSE),
+                          'coursecreator'  => array(CONTEXT_SYSTEM, CONTEXT_COURSECAT),
+                          'editingteacher' => array(CONTEXT_COURSE, CONTEXT_MODULE),
+                          'teacher'        => array(CONTEXT_COURSE, CONTEXT_MODULE),
+                          'student'        => array(CONTEXT_COURSE, CONTEXT_MODULE),
+                          'guest'          => array(),
+                          'user'           => array(),
+                          'frontpage'      => array());
+
+        $roles = $DB->get_records('role', array(), '', 'id, archetype');
         foreach ($roles as $role) {
-            assign_capability('moodle/course:visibility', CAP_ALLOW, $role->id, $systemcontext->id);
+            if (isset($defaults[$role->archetype])) {
+                $rolecontextlevels[$role->id] = $defaults[$role->archetype];
+            }
+        }
+
+        // add roles without archetypes, it may contain weird things, but we can not fix them
+        $sql = "SELECT DISTINCT ra.roleid, con.contextlevel
+                  FROM {role_assignments} ra
+                  JOIN {context} con ON ra.contextid = con.id";
+        $existingrolecontextlevels = $DB->get_recordset_sql($sql);
+        foreach ($existingrolecontextlevels as $rcl) {
+            if (isset($roleids[$rcl->roleid])) {
+                continue;
+            }
+            if (!isset($rolecontextlevels[$rcl->roleid])) {
+                $rolecontextlevels[$rcl->roleid] = array($rcl->contextlevel);
+            } else if (!in_array($rcl->contextlevel, $rolecontextlevels[$rcl->roleid])) {
+                $rolecontextlevels[$rcl->roleid][] = $rcl->contextlevel;
+            }
+        }
+        $existingrolecontextlevels->close();
+
+        // Put the data into the database.
+        $rcl = new object();
+        foreach ($rolecontextlevels as $roleid => $contextlevels) {
+            $rcl->roleid = $roleid;
+            foreach ($contextlevels as $level) {
+                $rcl->contextlevel = $level;
+                $DB->insert_record('role_context_levels', $rcl, false);
+            }
         }
 
-        // Force all sessions to refresh access data.
-        mark_context_dirty($systemcontext->path);
+        // release memory!!
+        unset($roles);
+        unset($defaults);
+        unset($rcl);
+        unset($existingrolecontextlevels);
+        unset($rolecontextlevels);
 
         // Main savepoint reached
-        upgrade_main_savepoint($result, 2010033102.09);
+        upgrade_main_savepoint($result, 2010033102.10);
     }
 
     if ($result && $oldversion < 2010040700) {
@@ -3564,7 +3542,7 @@ function xmldb_main_upgrade($oldversion) {
         $table->add_field('idnumber', XMLDB_TYPE_CHAR, '100', null, null, null, null);
         $table->add_field('description', XMLDB_TYPE_TEXT, 'small', null, null, null, null);
         $table->add_field('descriptionformat', XMLDB_TYPE_INTEGER, '2', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
-        $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, null, null, null);
+        $table->add_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null);
         $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
         $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
 
@@ -4117,8 +4095,7 @@ function xmldb_main_upgrade($oldversion) {
         upgrade_main_savepoint($result, 2010052200);
     }
 
-
-     if ($result && $oldversion < 2010052401) {
+    if ($result && $oldversion < 2010052401) {
 
     /// Define field status to be added to course_published
         $table = new xmldb_table('course_published');
@@ -4178,11 +4155,668 @@ function xmldb_main_upgrade($oldversion) {
     /// Main savepoint reached
         upgrade_main_savepoint($result, 2010052801);
     }
+
+    if ($result && $oldversion < 2010061900.01) {
+        // Define table enrol to be created
+        $table = new xmldb_table('enrol');
+
+        // Adding fields to table enrol
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('enrol', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null);
+        $table->add_field('status', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('courseid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('sortorder', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('name', XMLDB_TYPE_CHAR, '255', null, null, null, null);
+        $table->add_field('enrolperiod', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('enrolstartdate', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('enrolenddate', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('expirynotify', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('expirythreshold', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('notifyall', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('password', XMLDB_TYPE_CHAR, '50', null, null, null, null);
+        $table->add_field('cost', XMLDB_TYPE_CHAR, '20', null, null, null, null);
+        $table->add_field('currency', XMLDB_TYPE_CHAR, '3', null, null, null, null);
+        $table->add_field('roleid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, '0');
+        $table->add_field('customint1', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+        $table->add_field('customint2', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+        $table->add_field('customint3', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+        $table->add_field('customint4', XMLDB_TYPE_INTEGER, '10', null, null, null, null);
+        $table->add_field('customchar1', XMLDB_TYPE_CHAR, '255', null, null, null, null);
+        $table->add_field('customchar2', XMLDB_TYPE_CHAR, '255', null, null, null, null);
+        $table->add_field('customdec1', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
+        $table->add_field('customdec2', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null);
+        $table->add_field('customtext1', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
+        $table->add_field('customtext2', XMLDB_TYPE_TEXT, 'big', null, null, null, null);
+        $table->add_field('timecreated', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+
+        // Adding keys to table enrol
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('courseid', XMLDB_KEY_FOREIGN, array('courseid'), 'course', array('id'));
+
+        // Adding indexes to table enrol
+        $table->add_index('enrol', XMLDB_INDEX_NOTUNIQUE, array('enrol'));
+
+        // launch create table for enrol
+        $dbman->create_table($table);
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.01);
+    }
+
+    if ($result && $oldversion < 2010061900.02) {
+        // Define table course_participant to be created
+        $table = new xmldb_table('user_enrolments');
+
+        // Adding fields to table course_participant
+        $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+        $table->add_field('status', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('enrolid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('userid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
+        $table->add_field('timestart', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timeend', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '2147483647');
+        $table->add_field('modifierid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+        $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0');
+
+        // Adding keys to table course_participant
+        $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+        $table->add_key('enrolid', XMLDB_KEY_FOREIGN, array('enrolid'), 'enrol', array('id'));
+        $table->add_key('userid', XMLDB_KEY_FOREIGN, array('userid'), 'user', array('id'));
+        $table->add_key('modifierid', XMLDB_KEY_FOREIGN, array('modifierid'), 'user', array('id'));
+
+
+        // Adding indexes to table user_enrolments
+        $table->add_index('enrolid-userid', XMLDB_INDEX_UNIQUE, array('enrolid', 'userid'));
+
+        // Launch create table for course_participant
+        $dbman->create_table($table);
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.02);
+    }
+
+    if ($result && $oldversion < 2010061900.03) {
+        // Define field itemid to be added to role_assignments
+        $table = new xmldb_table('role_assignments');
+        $field = new xmldb_field('itemid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, 0, 'enrol');
+
+        // Launch add field itemid
+        $dbman->add_field($table, $field);
+
+        // The new enrol plugins may assign one role several times in one context,
+        // if we did not allow it we would have big problems with roles when unenrolling
+        $table = new xmldb_table('role_assignments');
+        $index = new xmldb_index('contextid-roleid-userid', XMLDB_INDEX_UNIQUE, array('contextid', 'roleid', 'userid'));
+
+        // Conditionally launch drop index contextid-roleid-userid
+        if ($dbman->index_exists($table, $index)) {
+            $dbman->drop_index($table, $index);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.03);
+    }
+
+    if ($result && $oldversion < 2010061900.04) {
+        // there is no default course role any more, each enrol plugin has to handle it separately
+        if (!empty($CFG->defaultcourseroleid)) {
+            $sql = "UPDATE {course} SET defaultrole = :default WHERE defaultrole = 0";
+            $params = array('default' => $CFG->defaultcourseroleid);
+            $DB->execute($sql, $params);
+        }
+        unset_config('defaultcourseroleid');
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.04);
+    }
+
+    if ($result && $oldversion < 2010061900.05) {
+        // make sure enrol settings make actually sense and tweak defaults a bit
+
+        $sqlempty = $DB->sql_empty();
+
+        // set course->enrol to default value so that other upgrade code is simpler
+        $defaultenrol = empty($CFG->enrol) ? 'manual' : $CFG->enrol;
+        $sql = "UPDATE {course} SET enrol = ? WHERE enrol = '$sqlempty'";
+        $DB->execute($sql, array($defaultenrol));
+        unset_config('enrol');
+
+        if (!isset($CFG->enrol_plugins_enabled) or empty($CFG->enrol_plugins_enabled)) {
+            set_config('enrol_plugins_enabled', 'manual');
+        } else {
+            $enabledplugins = explode(',', $CFG->enrol_plugins_enabled);
+            $enabledplugins = array_unique($enabledplugins);
+            set_config('enrol_plugins_enabled', implode(',', $enabledplugins));
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.05);
+    }
+
+    if ($result && $oldversion < 2010061900.06) {
+        $sqlempty = $DB->sql_empty();
+        $params = array('siteid'=>SITEID);
+
+        // enable manual in all courses
+        $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, enrolperiod, expirynotify, expirythreshold, notifyall, roleid, timecreated, timemodified)
+                SELECT 'manual', 0, id, 0, enrolperiod, expirynotify, expirythreshold, notifystudents, defaultrole, timecreated, timemodified
+                  FROM {course}
+                 WHERE id <> :siteid";
+        $DB->execute($sql, $params);
+
+        // enable self enrol only when course enrollable
+        $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, enrolperiod, enrolstartdate, enrolenddate, expirynotify, expirythreshold,
+                                     notifyall, password, roleid, timecreated, timemodified)
+                SELECT 'self', 0, id, 1, enrolperiod, enrolstartdate, enrolenddate, expirynotify, expirythreshold,
+                       notifystudents, password, defaultrole, timecreated, timemodified
+                  FROM {course}
+                 WHERE enrollable = 1 AND id <> :siteid";
+        $DB->execute($sql, $params);
+
+        // enable guest access if previously allowed - separately with or without password
+        $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, timecreated, timemodified)
+                SELECT 'guest', 0, id, 2, timecreated, timemodified
+                  FROM {course}
+                 WHERE guest = 1 AND id <> :siteid";
+        $DB->execute($sql, $params);
+        $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, password, timecreated, timemodified)
+                SELECT 'guest', 0, id, 2, password, timecreated, timemodified
+                  FROM {course}
+                 WHERE guest = 2 and password <> '$sqlempty' AND id <> :siteid";
+        $DB->execute($sql, $params);
+
+        upgrade_main_savepoint($result, 2010061900.06);
+    }
+
+    if ($result && $oldversion < 2010061900.07) {
+        // now migrate old style "interactive" enrol plugins - we know them by looking into course.enrol
+        $params = array('siteid'=>SITEID);
+        $enabledplugins = explode(',', $CFG->enrol_plugins_enabled);
+        $usedplugins = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {course}");
+        foreach ($usedplugins as $plugin) {
+            if ($plugin === 'manual') {
+                continue;
+            }
+            $enabled = in_array($plugin, $enabledplugins) ? 0 : 1; // 0 means active, 1 disabled
+            $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, enrolperiod, enrolstartdate, enrolenddate, expirynotify, expirythreshold,
+                                         notifyall, password, cost, currency, roleid, timecreated, timemodified)
+                    SELECT enrol, $enabled, id, 4, enrolperiod, enrolstartdate, enrolenddate, expirynotify, expirythreshold,
+                           notifystudents, password, cost, currency, defaultrole, timecreated, timemodified
+                      FROM {course}
+                     WHERE enrol = :plugin AND id <> :siteid";
+            $params['plugin'] = $plugin;
+            $DB->execute($sql, $params);
+        }
+        upgrade_main_savepoint($result, 2010061900.07);
+    }
+
+    if ($result && $oldversion < 2010061900.08) {
+        // now migrate the rest - these plugins are not in course.enrol, instead we just look for suspicious role assignments,
+        // unfortunately old enrol plugins were doing sometimes weird role assignments :-(
+
+        // enabled
+        $processed = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
+        $enabledplugins = explode(',', $CFG->enrol_plugins_enabled);
+        list($sqlnotprocessed, $params) = $DB->get_in_or_equal($processed, SQL_PARAMS_NAMED, 'np00', false);
+        list($sqlenabled, $params2) = $DB->get_in_or_equal($enabledplugins, SQL_PARAMS_NAMED, 'ena00');
+        $params = array_merge($params, $params2);
+        $params['siteid'] = SITEID;
+        $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, enrolperiod, enrolstartdate, enrolenddate, expirynotify, expirythreshold,
+                                     notifyall, password, cost, currency, roleid, timecreated, timemodified)
+                SELECT DISTINCT ra.enrol, 0, c.id, 5, c.enrolperiod, c.enrolstartdate, c.enrolenddate, c.expirynotify, c.expirythreshold,
+                       c.notifystudents, c.password, c.cost, c.currency, c.defaultrole, c.timecreated, c.timemodified
+                  FROM {course} c
+                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = 50)
+                  JOIN {role_assignments} ra ON (ra.contextid = ctx.id)
+                 WHERE c.id <> :siteid AND ra.enrol $sqlnotprocessed AND ra.enrol $sqlenabled";
+        $DB->execute($sql, $params);
+
+        // disabled
+        $processed = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
+        list($sqlnotprocessed, $params) = $DB->get_in_or_equal($processed, SQL_PARAMS_NAMED, 'np00', false);
+        $params = array_merge($params, $params2);
+        $params['siteid'] = SITEID;
+        $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, enrolperiod, enrolstartdate, enrolenddate, expirynotify, expirythreshold,
+                                     notifyall, password, cost, currency, roleid, timecreated, timemodified)
+                SELECT DISTINCT ra.enrol, 1, c.id, 5, c.enrolperiod, c.enrolstartdate, c.enrolenddate, c.expirynotify, c.expirythreshold,
+                       c.notifystudents, c.password, c.cost, c.currency, c.defaultrole, c.timecreated, c.timemodified
+                  FROM {course} c
+                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = 50)
+                  JOIN {role_assignments} ra ON (ra.contextid = ctx.id)
+                 WHERE c.id <> :siteid AND ra.enrol $sqlnotprocessed";
+        $DB->execute($sql, $params);
+
+        upgrade_main_savepoint($result, 2010061900.08);
+    }
+
+    if ($result && $oldversion < 2010061900.09) {
+        // unfortunately there may be still some leftovers
+        // after reconfigured, uninstalled or borked enrol plugins,
+        // unfortunately this may be a bit slow - but there should not be many of these
+        $sqlempty = $DB->sql_empty();
+        $sql = "SELECT DISTINCT c.id AS courseid, ra.enrol, e.timecreated, c.timemodified
+                  FROM {course} c
+                  JOIN {context} ctx ON (ctx.instanceid = c.id AND ctx.contextlevel = 50)
+                  JOIN {role_assignments} ra ON (ra.contextid = ctx.id AND ra.enrol <> '$sqlempty')
+             LEFT JOIN {enrol} e ON (e.courseid = c.id AND e.enrol = ra.enrol)
+                 WHERE c.id <> :siteid AND e.id IS NULL";
+        $params = array('siteid'=>SITEID);
+        $rs = $DB->get_recordset_sql($sql, $params);
+        foreach ($rs as $enrol) {
+            $enrol->status = 1; // better disable them
+            $DB->inert_record('enrol', $enrol);
+        }
+        $rs->close();
+        upgrade_main_savepoint($result, 2010061900.09);
+    }
+
+    if ($result && $oldversion < 2010061900.10) {
+        // migrate existing setup of meta courses
+        $sql = "INSERT INTO {enrol} (enrol, status, courseid, sortorder, customint1)
+                SELECT 'meta', 0, parent_course, 5, child_course
+                  FROM {course_meta}";
+        $DB->execute($sql);
+
+        upgrade_main_savepoint($result, 2010061900.10);
+    }
+
+    if ($result && $oldversion < 2010061900.11) {
+        // nuke any old role assignments+enrolments in previous meta courses, we have to start from scratch
+        $select = "SELECT ctx.id
+                     FROM {context} ctx
+                     JOIN {course} c ON (c.id = ctx.instanceid AND ctx.contextlevel = 50 AND c.metacourse = 1)";
+        $DB->delete_records_select('role_assignments', "contextid IN ($select) AND enrol = 'manual'");
+
+        // course_meta to be dropped - we use enrol_meta plugin instead now
+        $table = new xmldb_table('course_meta');
+
+        // Launch drop table for course_meta
+        $dbman->drop_table($table);
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.11);
+    }
+
+    if ($result && $oldversion < 2010061900.12) {
+        // finally remove all obsolete fields from the course table - yay!
+        // all the information was migrated to the enrol table
+
+        // Define field guest to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('guest');
+
+        // Conditionally launch drop field guest
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field password to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('password');
+
+        // Conditionally launch drop field password
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field enrolperiod to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('enrolperiod');
+
+        // Conditionally launch drop field enrolperiod
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field cost to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('cost');
+
+        // Conditionally launch drop field cost
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field currency to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('currency');
+
+        // Conditionally launch drop field currency
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field metacourse to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('metacourse');
+
+        // Conditionally launch drop field metacourse
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field expirynotify to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('expirynotify');
+
+        // Conditionally launch drop field expirynotify
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field expirythreshold to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('expirythreshold');
+
+        // Conditionally launch drop field expirythreshold
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field notifystudents to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('notifystudents');
+
+        // Conditionally launch drop field notifystudents
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field enrollable to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('enrollable');
+
+        // Conditionally launch drop field enrollable
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field enrolstartdate to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('enrolstartdate');
+
+        // Conditionally launch drop field enrolstartdate
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field enrolenddate to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('enrolenddate');
+
+        // Conditionally launch drop field enrolenddate
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field enrol to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('enrol');
+
+        // Conditionally launch drop field enrol
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field defaultrole to be dropped from course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('defaultrole');
+
+        // Conditionally launch drop field defaultrole
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        upgrade_main_savepoint($result, 2010061900.12);
+    }
+
+    if ($result && $oldversion < 2010061900.13) {
+        // Define field visibleold to be added to course_categories
+        $table = new xmldb_table('course_categories');
+        $field = new xmldb_field('visibleold', XMLDB_TYPE_INTEGER, '1', null, XMLDB_NOTNULL, null, '1', 'visible');
+
+        // Launch add field visibleold
+        $dbman->add_field($table, $field);
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.13);
+    }
+
+    if ($result && $oldversion < 2010061900.14) {
+        // keep previous visible state
+        $DB->execute("UPDATE {course_categories} SET visibleold = visible");
+
+        // make sure all subcategories of hidden categories are hidden too, do not rely on category path yet
+        $sql = "SELECT c.id
+                  FROM {course_categories} c
+                  JOIN {course_categories} pc ON (pc.id = c.parent AND pc.visible = 0)
+                 WHERE c.visible = 1";
+        while ($categories = $DB->get_records_sql($sql)) {
+            foreach ($categories as $cat) {
+                $DB->set_field('course_categories', 'visible', 0, array('id'=>$cat->id));
+            }
+        }
+        upgrade_main_savepoint($result, 2010061900.14);
+    }
+
+    if ($result && $oldversion < 2010061900.15) {
+        // Define field visibleold to be added to course
+        $table = new xmldb_table('course');
+        $field = new xmldb_field('visibleold', XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '1', 'visible');
+
+        // Launch add field visibleold
+        $dbman->add_field($table, $field);
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.15);
+    }
+
+    if ($result && $oldversion < 2010061900.16) {
+        // keep previous visible state
+        $DB->execute("UPDATE {course} SET visibleold = visible");
+
+        // make sure all courses in hidden categories are hidden
+        $DB->execute("UPDATE {course} SET visible = 0 WHERE category IN (SELECT id FROM {course_categories} WHERE visible = 0)");
+
+        upgrade_main_savepoint($result, 2010061900.16);
+    }
+
+    if ($result && $oldversion < 2010061900.20) {
+        // now set up the enrolments - look for roles with course:participate only at course context - the category enrolments are synchronised later by archetype and new capability
+
+        $syscontext = get_context_instance(CONTEXT_SYSTEM);
+        $params = array('syscontext'=>$syscontext->id, 'participate'=>'moodle/course:participate');
+        $roles = $DB->get_fieldset_sql("SELECT DISTINCT roleid FROM {role_capabilities} WHERE contextid = :syscontext AND capability = :participate AND permission = 1", $params);
+        list($sqlroles, $params) = $DB->get_in_or_equal($roles, SQL_PARAMS_NAMED, 'r00');
+
+        $sql = "INSERT INTO {user_enrolments} (status, enrolid, userid, timestart, timeend, modifierid, timemodified)
+
+                SELECT 0, e.id, ra.userid, MIN(ra.timestart), MIN(ra.timeend), 0, MAX(ra.timemodified)
+                  FROM {role_assignments} ra
+                  JOIN {context} c ON (c.id = ra.contextid AND c.contextlevel = 50)
+                  JOIN {enrol} e ON (e.enrol = ra.enrol AND e.courseid = c.instanceid)
+                  JOIN {user} u ON u.id = ra.userid
+                 WHERE u.deleted = 0 AND ra.roleid $sqlroles
+              GROUP BY e.id, ra.userid";
+        $DB->execute($sql, $params);
+
+        upgrade_main_savepoint($result, 2010061900.20);
+    }
+
+    if ($result && $oldversion < 2010061900.21) {
+        // hidden is completely removed, timestart+timeend are now in the user_enrolments table
+
+        // Define field hidden to be dropped from role_assignments
+        $table = new xmldb_table('role_assignments');
+        $field = new xmldb_field('hidden');
+
+        // Conditionally launch drop field hidden
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field timestart to be dropped from role_assignments
+        $table = new xmldb_table('role_assignments');
+        $field = new xmldb_field('timestart');
+
+        // Conditionally launch drop field timestart
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Define field timeend to be dropped from role_assignments
+        $table = new xmldb_table('role_assignments');
+        $field = new xmldb_field('timeend');
+
+        // Conditionally launch drop field timeend
+        if ($dbman->field_exists($table, $field)) {
+            $dbman->drop_field($table, $field);
+        }
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.21);
+    }
+
+    if ($result && $oldversion < 2010061900.22) {
+        // Rename field enrol on table role_assignments to component and update content
+
+        $table = new xmldb_table('role_assignments');
+        $field = new xmldb_field('enrol', XMLDB_TYPE_CHAR, '20', null, XMLDB_NOTNULL, null, null, 'modifierid');
+
+        // Launch rename field enrol
+        $dbman->rename_field($table, $field, 'component');
+
+        // Changing precision of field component on table role_assignments to (100)
+        $table = new xmldb_table('role_assignments');
+        $field = new xmldb_field('component', XMLDB_TYPE_CHAR, '100', null, XMLDB_NOTNULL, null, null, 'modifierid');
+
+        // Launch change of precision for field component
+        $dbman->change_field_precision($table, $field);
+
+        // Manual is a special case - we use empty string instead now
+        $params = array('empty'=>$DB->sql_empty(), 'manual'=>'manual');
+        $sql = "UPDATE {role_assignments}
+                   SET component = :empty
+                 WHERE component = :manual";
+        $DB->execute($sql, $params);
+
+        // Now migrate to real enrol component names
+        $params = array('empty'=>$DB->sql_empty());
+        $concat = $DB->sql_concat("'enrol_'", 'component');
+        $sql = "UPDATE {role_assignments}
+                   SET component = $concat
+                 WHERE component <> :empty
+                       AND contextid IN (
+                           SELECT id
+                             FROM {context}
+                            WHERE contextlevel >= 50)";
+        $DB->execute($sql, $params);
+
+        // Now migrate to real auth component names
+        $params = array('empty'=>$DB->sql_empty());
+        $concat = $DB->sql_concat("'auth_'", 'component');
+        $sql = "UPDATE {role_assignments}
+                   SET component = $concat
+                 WHERE component <> :empty
+                       AND contextid IN (
+                           SELECT id
+                             FROM {context}
+                            WHERE contextlevel < 50)";
+        $DB->execute($sql, $params);
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.22);
+    }
+
+    if ($result && $oldversion < 2010061900.23) {
+        // add proper itemid to role assignments that were added by enrolment plugins
+        $sql = "UPDATE {role_assignments}
+                   SET itemid = (SELECT MIN({enrol}.id)
+                                    FROM {enrol}
+                                    JOIN {context} ON ({context}.contextlevel = 50 AND {context}.instanceid = {enrol}.courseid)
+                                   WHERE {role_assignments}.component = ".$DB->sql_concat("'enrol_'", "{enrol}.enrol")." AND {context}.id = {role_assignments}.contextid)
+                 WHERE component <> 'enrol_manual' AND component LIKE 'enrol_%'";
+        $DB->execute($sql);
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.23);
+    }
+
+    if ($result && $oldversion < 2010061900.30) {
+        // make new list of active enrol plugins - order is important, meta should be always last, manual first
+        $enabledplugins = explode(',', $CFG->enrol_plugins_enabled);
+        $enabledplugins = array_merge(array('manual', 'guest', 'self', 'cohort'), $enabledplugins);
+        if ($DB->record_exists('enrol', array('enrol'=>'meta'))) {
+            $enabledplugins[] = 'meta';
+        }
+        $enabledplugins = array_unique($enabledplugins);
+        set_config('enrol_plugins_enabled', implode(',', $enabledplugins));
+
+        // Main savepoint reached
+        upgrade_main_savepoint($result, 2010061900.30);
+    }
+
+    if ($result && $oldversion < 2010061900.31) {
+        // finalise all new enrol settings and cleanup old settings
+
+        // legacy allowunenrol was deprecated in 1.9 already
+        unset_config('allwunenroll');
+
+        // obsolete course presets
+        unset_config('metacourse', 'moodlecourse');
+        unset_config('enrol', 'moodlecourse');
+        unset_config('enrollable', 'moodlecourse');
+        unset_config('enrolperiod', 'moodlecourse');
+        unset_config('expirynotify', 'moodlecourse');
+        unset_config('notifystudents', 'moodlecourse');
+        unset_config('expirythreshold', 'moodlecourse');
+        unset_config('enrolpassword', 'moodlecourse');
+        unset_config('guest', 'moodlecourse');
+
+        unset_config('backup_sche_metacourse', 'backup');
+
+        unset_config('lastexpirynotify');
+
+        // hidden course categories now prevent only browsing, courses are accessible if you know the URL and course is visible
+        unset_config('allowvisiblecoursesinhiddencategories');
+
+        if (isset($CFG->coursemanager)) {
+            set_config('coursecontact', $CFG->coursemanager);
+            unset_config('coursemanager');
+        }
+
+        // migrate plugin settings - the problem here is we are splitting manual into three different plugins
+        if (isset($CFG->enrol_manual_usepasswordpolicy)) {
+            set_config('usepasswordpolicy', $CFG->enrol_manual_usepasswordpolicy, 'enrol_guest');
+            set_config('usepasswordpolicy', $CFG->enrol_manual_usepasswordpolicy, 'enrol_self');
+            set_config('groupenrolmentkeypolicy', $CFG->enrol_manual_usepasswordpolicy);
+            unset_config('enrol_manual_usepasswordpolicy');
+        }
+        if (isset($CFG->enrol_manual_requirekey)) {
+            set_config('requirepassword', $CFG->enrol_manual_requirekey, 'enrol_guest');
+            set_config('requirepassword', $CFG->enrol_manual_requirekey, 'enrol_self');
+            unset_config('enrol_manual_requirekey');
+        }
+        if (isset($CFG->enrol_manual_showhint)) {
+            set_config('showhint', $CFG->enrol_manual_showhint, 'enrol_guest');
+            set_config('showhint', $CFG->enrol_manual_showhint, 'enrol_self');
+            unset_config('enrol_manual_showhint');
+        }
+
+        upgrade_main_savepoint($result, 2010061900.31);
+    }
+
+    if ($result && $oldversion < 2010061900.32) {
+        // MDL-22797 course completion has to be updated to use new enrol framework, it will not be enabled in final 2.0
+        set_config('enableavailability', 0);
+        set_config('enablecompletion', 0);
+        upgrade_main_savepoint($result, 2010061900.32);
+    }
+
+
     return $result;
 }
 
 //TODO: Cleanup before the 2.0 release - we do not want to drag along these dev machine fixes forever
-// 1/ remove the automatic enabling of completion lib if debug enabled ( in 2008121701 block)
 // 2/ move 2009061300 block to the top of the file so that we may log upgrade queries
 // 3/ remove 2010033101 block
 // 4/ remove 2010032400 block
diff --git a/lib/ddl/mysql_sql_generator.php b/lib/ddl/mysql_sql_generator.php
index c84c08b63055..c7b893c6fb1a 100644
--- a/lib/ddl/mysql_sql_generator.php
+++ b/lib/ddl/mysql_sql_generator.php
@@ -66,7 +66,7 @@ class mysql_sql_generator extends sql_generator {
 
     public $alter_column_sql = 'ALTER TABLE TABLENAME MODIFY COLUMN COLUMNSPECS'; //The SQL template to alter columns
 
-    public $drop_index_sql = 'ALTER TABLE TABLENAME DROP INDEX INDEXNAME'; //SQL sentence to drop one index
+    public $drop_index_sql = 'ALTER TABLE TABLENAME DROP INDEX INDEXNAME'; //SQL sentence to drop one index - HACK for MDL-22804
                                                                //TABLENAME, INDEXNAME are dynamically replaced
 
     public $rename_index_sql = null; //SQL sentence to rename one index (MySQL doesn't support this!)
@@ -94,6 +94,7 @@ public function getResetSequenceSQL($table) {
         return array("ALTER TABLE $this->prefix$tablename AUTO_INCREMENT = $value");
     }
 
+
     /**
      * Given one correct xmldb_table, returns the SQL statements
      * to create temporary table (inside one array)
diff --git a/lib/deprecatedlib.php b/lib/deprecatedlib.php
index f23c3d9d70b3..f533dd93a2f7 100644
--- a/lib/deprecatedlib.php
+++ b/lib/deprecatedlib.php
@@ -39,6 +39,39 @@ function auth_get_plugin_title($authtype) {
     return get_string('pluginname', "auth_{$authtype}");
 }
 
+
+/**
+ * Enrol someone without using the default role in a course
+ * @deprecated
+ */
+function enrol_into_course($course, $user, $enrol) {
+    error('Function enrol_into_course() was removed, please use new enrol plugins instead!');
+}
+
+/**
+ * Returns a role object that is the default role for new enrolments in a given course
+ *
+ * @deprecated
+ * @param object $course
+ * @return object returns a role or NULL if none set
+ */
+function get_default_course_role($course) {
+    debugging('Function get_default_course_role() is deprecated, please use individual enrol plugin settings instead!');
+
+    $student = get_archetype_roles('student');
+    $student = reset($student);
+
+    return $student;
+}
+
+/**
+ * Extremely slow enrolled courses query.
+ * @deprecated
+ */
+function get_my_courses($userid, $sort='visible DESC,sortorder ASC', $fields=NULL, $doanything=false,$limit=0) {
+    error('Function get_my_courses() was removed, please use new enrol_get_my_courses() or enrol_get_users_courses()!');
+}
+
 /**
  * Was returning list of translations, use new string_manager instead
  *
diff --git a/lib/enrollib.php b/lib/enrollib.php
new file mode 100644
index 000000000000..cf8171faf20f
--- /dev/null
+++ b/lib/enrollib.php
@@ -0,0 +1,1284 @@
+<?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 library includes the basic parts of enrol api.
+ * It is available on each page.
+ *
+ * @package   moodlecore
+ * @copyright 2010 Petr Skoda {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+/** Course enrol instance enabled. (used in enrol->status) */
+define('ENROL_INSTANCE_ENABLED', 0);
+
+/** Course enrol instance disabled, user may enter course if other enrol instance enabled. (used in enrol->status)*/
+define('ENROL_INSTANCE_DISABLED', 1);
+
+/** User is active participant (used in user_enrolments->status)*/
+define('ENROL_USER_ACTIVE', 0);
+
+/** User participation in course is suspended (used in user_enrolments->status) */
+define('ENROL_USER_SUSPENDED', 1);
+
+/** Enrol info is cached for this number of seconds in require_login() */
+define('ENROL_REQUIRE_LOGIN_CACHE_PERIOD', 1800);
+
+/**
+ * Returns instances of enrol plugins
+ * @param bool $enable return enabled only
+ * @return array of enrol plugins name=>instance
+ */
+function enrol_get_plugins($enabled) {
+    global $CFG;
+
+    $result = array();
+
+    if ($enabled) {
+        // sorted by enabled plugin order
+        $enabled = explode(',', $CFG->enrol_plugins_enabled);
+        $plugins = array();
+        foreach ($enabled as $plugin) {
+            $plugins[$plugin] = "$CFG->dirroot/enrol/$plugin";
+        }
+    } else {
+        // sorted alphabetically
+        $plugins = get_plugin_list('enrol');
+        ksort($plugins);
+    }
+
+    foreach ($plugins as $plugin=>$location) {
+        if (!file_exists("$location/lib.php")) {
+            continue;
+        }
+        include_once("$location/lib.php");
+        $class = "enrol_{$plugin}_plugin";
+        if (!class_exists($class)) {
+            continue;
+        }
+
+        $result[$plugin] = new $class();
+    }
+
+    return $result;
+}
+
+/**
+ * Returns instance of enrol plugin
+ * @param  string $name name of enrol plugin ('manual', 'guest', ...)
+ * @return enrol_plugin
+ */
+function enrol_get_plugin($name) {
+    global $CFG;
+
+    if ($name !== clean_param($name, PARAM_SAFEDIR)) {
+        // ignore malformed plugin names completely
+        return null;
+    }
+
+    $location = "$CFG->dirroot/enrol/$name";
+
+    if (!file_exists("$location/lib.php")) {
+        return null;
+    }
+    include_once("$location/lib.php");
+    $class = "enrol_{$name}_plugin";
+    if (!class_exists($class)) {
+        return null;
+    }
+
+    return new $class();
+}
+
+/**
+ * Returns enrolment instances in given course.
+ * @param int $courseid
+ * @param bool $enabled
+ * @return array of enrol instances
+ */
+function enrol_get_instances($courseid, $enabled) {
+    global $DB, $CFG;
+
+    if (!$enabled) {
+        return $DB->get_records('enrol', array('courseid'=>$courseid), 'sortorder,id');
+    }
+
+    $result = $DB->get_records('enrol', array('courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder,id');
+
+    $enabled = $enabled = explode(',', $CFG->enrol_plugins_enabled);
+    foreach ($result as $key=>$instance) {
+        if (!in_array($instance->enrol, $enabled)) {
+            unset($result[$key]);
+            continue;
+        }
+        if (!file_exists("$CFG->dirroot/enrol/$instance->enrol/lib.php")) {
+            // broken plugin
+            unset($result[$key]);
+            continue;
+        }
+    }
+
+    return $result;
+}
+
+/**
+ * Checks if a given plugin is in the list of enabled enrolment plugins.
+ *
+ * @param string $enrol Enrolment plugin name
+ * @return boolean Whether the plugin is enabled
+ */
+function enrol_is_enabled($enrol) {
+    global $CFG;
+
+    if (empty($CFG->enrol_plugins_enabled)) {
+        return false;
+    }
+    return in_array($enrol, explode(',', $CFG->enrol_plugins_enabled));
+}
+
+/**
+ * Check all the login enrolment information for the given user object
+ * by querying the enrolment plugins
+ *
+ * @param object $user
+ * @return void
+ */
+function enrol_check_plugins($user) {
+    global $CFG;
+
+    if (empty($user->id) or isguestuser($user)) {
+        // shortcut - there is no enrolment work for guests and not-logged-in users
+        return;
+    }
+
+    static $inprogress = array();  // To prevent this function being called more than once in an invocation
+
+    if (!empty($inprogress[$user->id])) {
+        return;
+    }
+
+    $inprogress[$user->id] = true;  // Set the flag
+
+    $enabled = enrol_get_plugins(true);
+
+    foreach($enabled as $enrol) {
+        $enrol->sync_user_enrolments($user);
+    }
+
+    unset($inprogress[$user->id]);  // Unset the flag
+}
+
+/**
+ * This function adds necessary enrol plugins UI into the course edit form.
+ *
+ * @param MoodleQuickForm $mform
+ * @param object $data course edit form data
+ * @param object $context context of existing course or parent category if course does not exist
+ * @return void
+ */
+function enrol_course_edit_form(MoodleQuickForm $mform, $data, $context) {
+    $plugins = enrol_get_plugins(true);
+    if (!empty($data->id)) {
+        $instances = enrol_get_instances($data->id, false);
+        foreach ($instances as $instance) {
+            if (!isset($plugins[$instance->enrol])) {
+                continue;
+            }
+            $plugin = $plugins[$instance->enrol];
+            $plugin->course_edit_form($instance, $mform, $data, $context);
+        }
+    } else {
+        foreach ($plugins as $plugin) {
+            $plugin->course_edit_form(NULL, $mform, $data, $context);
+        }
+    }
+}
+
+/**
+ * Validate course edit form data
+ *
+ * @param array $data raw form data
+ * @param object $context context of existing course or parent category if course does not exist
+ * @return array errors array
+ */
+function enrol_course_edit_validation(array $data, $context) {
+    $errors = array();
+    $plugins = enrol_get_plugins(true);
+
+    if (!empty($data['id'])) {
+        $instances = enrol_get_instances($data['id'], false);
+        foreach ($instances as $instance) {
+            if (!isset($plugins[$instance->enrol])) {
+                continue;
+            }
+            $plugin = $plugins[$instance->enrol];
+            $errors = array_merge($errors, $plugin->course_edit_validation($instance, $data, $context));
+        }
+    } else {
+        foreach ($plugins as $plugin) {
+            $errors = array_merge($errors, $plugin->course_edit_validation(NULL, $data, $context));
+        }
+    }
+
+    return $errors;
+}
+
+/**
+ * Update enrol instances after course edit form submission
+ * @param bool $inserted true means new course added, false course already existed
+ * @param object $course
+ * @param object $data form data
+ * @return void
+ */
+function enrol_course_updated($inserted, $course, $data) {
+    global $DB, $CFG;
+
+    $plugins = enrol_get_plugins(true);
+
+    foreach ($plugins as $plugin) {
+        $plugin->course_updated($inserted, $course, $data);
+    }
+}
+
+/**
+ * Add navigation nodes
+ * @param navigation_node $coursenode
+ * @param object $course
+ * @return void
+ */
+function enrol_add_course_navigation(navigation_node $coursenode, $course) {
+
+    $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
+
+    $instances = enrol_get_instances($course->id, true);
+    $plugins   = enrol_get_plugins(true);
+
+    // we do not want to break all course pages if there is some borked enrol plugin, right?
+    foreach ($instances as $k=>$instance) {
+        if (!isset($plugins[$instance->enrol])) {
+            unset($instances[$k]);
+        }
+    }
+
+    $usersnode = $coursenode->add(get_string('users'), null, navigation_node::TYPE_CONTAINER);
+
+    if ($course->id != SITEID) {
+        // list all participants - allows assing roles, groups, etc.
+        if (has_capability('moodle/course:enrolreview', $coursecontext)) {
+            $url = new moodle_url('/enrol/users.php', array('id'=>$course->id));
+            $usersnode->add(get_string('enrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/users', ''));
+        }
+
+        // manage enrol plugin instances
+        if (has_capability('moodle/course:enrolconfig', $coursecontext) or has_capability('moodle/course:enrolreview', $coursecontext)) {
+            $url = new moodle_url('/enrol/instances.php', array('id'=>$course->id));
+        } else {
+            $url = NULL;
+        }
+        $instancesnode = $usersnode->add(get_string('enrolmentinstances', 'enrol'), $url);
+
+        // each instance decides how to configure itself or how many other nav items are exposed
+        foreach ($instances as $instance) {
+            if (!isset($plugins[$instance->enrol])) {
+                continue;
+            }
+            $plugins[$instance->enrol]->add_course_navigation($instancesnode, $instance);
+        }
+
+        if (!$url) {
+            $instancesnode->trim_if_empty();
+        }
+    }
+
+    // Manage groups in this course or even frontpage
+    if (($course->groupmode || !$course->groupmodeforce) && has_capability('moodle/course:managegroups', $coursecontext)) {
+        $url = new moodle_url('/group/index.php', array('id'=>$course->id));
+        $usersnode->add(get_string('groups'), $url, navigation_node::TYPE_SETTING, null, 'groups', new pix_icon('i/group', ''));
+    }
+
+     if (has_any_capability(array( 'moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:review'), $coursecontext)) {
+        // Override roles
+        if (has_capability('moodle/role:review', $coursecontext)) {
+            $url = new moodle_url('/admin/roles/permissions.php', array('contextid'=>$coursecontext->id));
+        } else {
+            $url = NULL;
+        }
+        $permissionsnode = $usersnode->add(get_string('permissions', 'role'), $url);
+
+        // Add assign or override roles if allowed
+        if ($course->id == SITEID or (!empty($CFG->adminsassignrolesincourse) and is_siteadmin())) {
+            if (has_capability('moodle/role:assign', $coursecontext)) {
+                $url = new moodle_url('/admin/roles/assign.php', array('contextid'=>$coursecontext->id));
+                $permissionsnode->add(get_string('assignedroles', 'role'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/roles', ''));
+            }
+        }
+        // Check role permissions
+        if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:assign'), $coursecontext)) {
+            $url = new moodle_url('/admin/roles/check.php', array('contextid'=>$coursecontext->id));
+            $permissionsnode->add(get_string('checkpermissions', 'role'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/checkpermissions', ''));
+        }
+     }
+
+     // Deal somehow with users that are not enrolled but still got a role somehow
+    if ($course->id != SITEID) {
+        //TODO, create some new UI for role assignments at course level
+        if (has_capability('moodle/role:assign', $coursecontext)) {
+            $url = new moodle_url('/enrol/otherusers.php', array('id'=>$course->id));
+            $usersnode->add(get_string('notenrolledusers', 'enrol'), $url, navigation_node::TYPE_SETTING, null, null, new pix_icon('i/roles', ''));
+        }
+    }
+
+    // just in case nothing was actually added
+    $usersnode->trim_if_empty();
+
+    if ($course->id != SITEID) {
+        // Unenrol link
+        $unenrolprinted = false;
+        foreach ($instances as $instance) {
+            if (!isset($plugins[$instance->enrol])) {
+                continue;
+            }
+            $plugin = $plugins[$instance->enrol];
+            if ($unenrollink = $plugin->get_unenrolself_link($instance)) {
+                $coursenode->add(get_string('unenrolme', 'core_enrol', format_string($course->shortname)), $unenrollink, navigation_node::TYPE_SETTING, null, 'unenrolself', new pix_icon('i/user', ''));
+                $unenrolprinted = true;
+                //TODO. deal with multiple unenrol links - not likely case, but still...
+            }
+        }
+        // Enrol link
+        if (!$unenrolprinted and !is_viewing($coursecontext) and !is_enrolled($coursecontext)) {
+            $url = new moodle_url('/enrol/index.php', array('id'=>$course->id));
+            $coursenode->add(get_string('enrolme', 'core_enrol', format_string($course->shortname)), $url, navigation_node::TYPE_SETTING, null, 'enrolself', new pix_icon('i/user', ''));
+        }
+    }
+}
+
+/**
+ * Returns list of courses current $USER is enrolled in and can access
+ *
+ * - $fields is an array of field names to ADD
+ *   so name the fields you really need, which will
+ *   be added and uniq'd
+ *
+ * @param strin|array $fields
+ * @param string $sort
+ * @param int $limit max number of courses
+ * @return array
+ */
+function enrol_get_my_courses($fields = NULL, $sort = 'visible DESC,sortorder ASC', $limit = 0) {
+    global $DB, $USER;
+
+    // Guest account does not have any courses
+    if (isguestuser() or !isloggedin()) {
+        return(array());
+    }
+
+    $basefields = array('id', 'category', 'sortorder',
+                        'shortname', 'fullname', 'idnumber',
+                        'startdate', 'visible',
+                        'groupmode', 'groupmodeforce');
+
+    if (empty($fields)) {
+        $fields = $basefields;
+    } else if (is_string($fields)) {
+        // turn the fields from a string to an array
+        $fields = explode(',', $fields);
+        $fields = array_map('trim', $fields);
+        $fields = array_unique(array_merge($basefields, $fields));
+    } else if (is_array($fields)) {
+        $fields = array_unique(array_merge($basefields, $fields));
+    } else {
+        throw new coding_exception('Invalid $fileds parameter in enrol_get_my_courses()');
+    }
+    if (in_array('*', $fields)) {
+        $fields = array('*');
+    }
+
+    $orderby = "";
+    $sort    = trim($sort);
+    if (!empty($sort)) {
+        $rawsorts = explode(',', $sort);
+        $sorts = array();
+        foreach ($rawsorts as $rawsort) {
+            $rawsort = trim($rawsort);
+            if (strpos($rawsort, 'c.') === 0) {
+                $rawsort = substr($rawsort, 2);
+            }
+            $sorts[] = trim($rawsort);
+        }
+        $sort = 'c.'.implode(',c.', $sorts);
+        $orderby = "ORDER BY $sort";
+    }
+
+    $wheres = array("c.id <> :siteid");
+    $params = array('siteid'=>SITEID);
+
+    if (isset($USER->loginascontext) and $USER->loginascontext->contextlevel == CONTEXT_COURSE) {
+        // list _only_ this course - anything else is asking for trouble...
+        $wheres[] = "courseid = :loginas";
+        $params['loginas'] = $USER->loginascontext->instanceid;
+    }
+
+    $coursefields = 'c.' .join(',c.', $fields);
+    list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
+    $wheres = " AND ".implode(" AND ", $wheres);
+
+    $sql = "SELECT DISTINCT $coursefields $ccselect
+              FROM {course} c
+              JOIN {enrol} e ON (e.courseid = c.id AND e.status = :enabled)
+              JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid AND ue.status = :active)
+           $ccjoin
+             WHERE ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)
+                   $wheres
+          $orderby";
+    $params['userid']  = $USER->id;
+    $params['active']  = ENROL_USER_ACTIVE;
+    $params['enabled'] = ENROL_INSTANCE_ENABLED;
+    $params['now1']    = round(time(), -2); // improves db caching
+    $params['now2']    = $params['now1'];
+
+    $courses = $DB->get_records_sql($sql, $params, 0, $limit);
+
+    // preload contexts and check visibility
+    foreach ($courses as $id=>$course) {
+        context_instance_preload($course);
+        if (!$course->visible) {
+            if (!$context = get_context_instance(CONTEXT_COURSE, $id)) {
+                unset($course[$id]);
+                continue;
+            }
+            if (!has_capability('moodle/course:viewhiddencourses', $context)) {
+                unset($course[$id]);
+                continue;
+            }
+        }
+        $courses[$id] = $course;
+    }
+
+    //wow! Is that really all? :-D
+
+    return $courses;
+}
+
+/**
+ * Returns list of courses user is enrolled into.
+ *
+ * - $fields is an array of fieldnames to ADD
+ *   so name the fields you really need, which will
+ *   be added and uniq'd
+ *
+ * @param int $userid
+ * @param bool $onlyactive return only active enrolments in courses user may see
+ * @param strin|array $fields
+ * @param string $sort
+ * @return array
+ */
+function enrol_get_users_courses($userid, $onlyactive = false, $fields = NULL, $sort = 'visible DESC,sortorder ASC') {
+    global $DB;
+
+    // Guest account does not have any courses
+    if (isguestuser($userid) or !empty($userid)) {
+        return(array());
+    }
+
+    $basefields = array('id', 'category', 'sortorder',
+                        'shortname', 'fullname', 'idnumber',
+                        'startdate', 'visible',
+                        'groupmode', 'groupmodeforce');
+
+    if (empty($fields)) {
+        $fields = $basefields;
+    } else if (is_string($fields)) {
+        // turn the fields from a string to an array
+        $fields = explode(',', $fields);
+        $fields = array_map('trim', $fields);
+        $fields = array_unique(array_merge($basefields, $fields));
+    } else if (is_array($fields)) {
+        $fields = array_unique(array_merge($basefields, $fields));
+    } else {
+        throw new coding_exception('Invalid $fileds parameter in enrol_get_my_courses()');
+    }
+    if (in_array('*', $fields)) {
+        $fields = array('*');
+    }
+
+    $orderby = "";
+    $sort    = trim($sort);
+    if (!empty($sort)) {
+        $rawsorts = explode(',', $sort);
+        $sorts = array();
+        foreach ($rawsorts as $rawsort) {
+            $rawsort = trim($rawsort);
+            if (strpos($rawsort, 'c.') === 0) {
+                $rawsort = substr($rawsort, 2);
+            }
+            $sorts[] = trim($rawsort);
+        }
+        $sort = 'c.'.implode(',c.', $sorts);
+        $orderby = "ORDER BY $sort";
+    }
+
+    $wheres = array("c.id <> :siteid");
+    $params = array('siteid'=>SITEID);
+
+    if ($onlyactive) {
+        $wheres[] = "ue.status = :active";
+        $wheres[] = "e.status = :enabled";
+        $wheres[] = "ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)";
+        $params['now1']    = round(time(), -2); // improves db caching
+        $params['now2']    = $params['now1'];
+        $params['active']  = ENROL_USER_ACTIVE;
+        $params['enabled'] = ENROL_INSTANCE_ENABLED;
+    }
+
+    $coursefields = 'c.' .join(',c.', $fields);
+    list($ccselect, $ccjoin) = context_instance_preload_sql('c.id', CONTEXT_COURSE, 'ctx');
+    $wheres = "WHERE ".implode(" AND ", $wheres);
+
+    $sql = "SELECT DISTINCT $coursefields $ccselect
+              FROM {course} c
+              JOIN {enrol} e ON (e.courseid = c.id)
+              JOIN {user_enrolments} ue ON (ue.enrolid = e.id AND ue.userid = :userid)
+           $ccjoin
+           $wheres
+          $orderby";
+    $params['userid']  = $USER->id;
+
+    $courses = $DB->get_records_sql($sql, $params);
+
+    // preload contexts and check visibility
+    foreach ($courses as $id=>$course) {
+        context_instance_preload($course);
+        if ($onlyactive) {
+            if (!$course->visible) {
+                if (!$context = get_context_instance(CONTEXT_COURSE, $id)) {
+                    unset($course[$id]);
+                    continue;
+                }
+                if (!has_capability('moodle/course:viewhiddencourses', $context, $userid)) {
+                    unset($course[$id]);
+                    continue;
+                }
+            }
+        }
+        $courses[$id] = $course;
+    }
+
+    //wow! Is that really all? :-D
+
+    return $courses;
+
+}
+
+/**
+ * Called when user is about to be deleted.
+ * @param object $user
+ * @return void
+ */
+function enrol_user_delete($user) {
+    global $DB;
+
+    $plugins = enrol_get_plugins(true);
+    foreach ($plugins as $plugin) {
+        $plugin->user_delete($user);
+    }
+
+    // force cleanup of all broken enrolments
+    $DB->delete_records('user_enrolments', array('userid'=>$user->id));
+}
+
+/**
+ * Try to enrol user via default internal auth plugin.
+ *
+ * For now this is always using the manual enrol plugin...
+ *
+ * @param $courseid
+ * @param $userid
+ * @param $roleid
+ * @param $timestart
+ * @param $timeend
+ * @return bool success
+ */
+function enrol_try_internal_enrol($courseid, $userid, $roleid = null, $timestart = 0, $timeend = 0) {
+    global $DB;
+
+    //note: this is hardcoded to manual plugin for now
+
+    if (!enrol_is_enabled('manual')) {
+        return false;
+    }
+
+    if (!$enrol = enrol_get_plugin('manual')) {
+        return false;
+    }
+    if (!$instances = $DB->get_records('enrol', array('enrol'=>'manual', 'courseid'=>$courseid, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder,id ASC')) {
+        return false;
+    }
+    $instance = reset($instances);
+
+    $enrol->enrol_user($instance, $userid, $roleid, $timestart, $timeend);
+
+    return true;
+}
+
+/**
+ * All enrol plugins should be based on this class,
+ * this is also the main source of documentation.
+ */
+abstract class enrol_plugin {
+    protected $config = null;
+
+    /**
+     * Returns name of this enrol plugin
+     * @return string
+     */
+    public function get_name() {
+        // second word in class is always enrol name
+        $words = explode('_', get_class($this));
+        return $words[1];
+    }
+
+    /**
+     * Returns localised name of enrol instance
+     *
+     * @param object $instance (null is accepted too)
+     * @return string
+     */
+    public function get_instance_name($instance) {
+        if (empty($instance->name)) {
+            $enrol = $this->get_name();
+            return get_string('pluginname', 'enrol_'.$enrol);
+        } else {
+            return format_string($instance->name);
+        }
+    }
+
+    /**
+     * Makes sure config is loaded and cached.
+     * @return void
+     */
+    protected function load_config() {
+        if (!isset($this->config)) {
+            $name = $this->get_name();
+            if (!$config = get_config("enrol_$name")) {
+                $config = new object();
+            }
+            $this->config = $config;
+        }
+    }
+
+    /**
+     * Returns plugin config value
+     * @param  string $name
+     * @param  string $default value if config does not exist yet
+     * @return string value or default
+     */
+    public function get_config($name, $default = NULL) {
+        $this->load_config();
+        return isset($this->config->$name) ? $this->config->$name : $default;
+    }
+
+    /**
+     * Sets plugin config value
+     * @param  string $name name of config
+     * @param  string $value string config value, null means delete
+     * @return string value
+     */
+    public function set_config($name, $value) {
+        $name = $this->get_name();
+        $this->load_config();
+        if ($value === NULL) {
+            unset($this->config->$name);
+        } else {
+            $this->config->$name = $value;
+        }
+        set_config($name, $value, "enrol_$name");
+    }
+
+    /**
+     * Does this plugin assign protected roles are can they be manually removed?
+     * @return bool - false means anybody may tweak roles, it does not use itemid and component when assigning roles
+     */
+    public function roles_protected() {
+        return true;
+    }
+
+    /**
+     * Does this plugin allow manual unenrolments?
+     *
+     * @param stdClass $instance course enrol instance
+     * ALl plugins allowing this must implement 'enrol/xxx:unenrol' capability
+     *
+     * @return bool - true means anybody may unenrol others freely, trues means nobody may touch user_enrolments
+     */
+    public function allow_unenrol(stdClass $instance) {
+        return false;
+    }
+
+    /**
+     * Does this plugin allow manual changes in user_enrolments table?
+     *
+     * ALl plugins allowing this must implement 'enrol/xxx:manage' capability
+     *
+     * @param stdClass $instance course enrol instance
+     * @return bool - true means it is possible to change enrol period and status in user_enrolments table
+     */
+    public function allow_manage(stdClass $instance) {
+        return false;
+    }
+
+    /**
+     * Attempt to automatically enrol current user in course without any interaction,
+     * calling code has to make sure the plugin and instance are active.
+     *
+     * @param stdClass $instance course enrol instance
+     * @param stdClass $user record
+     * @return bool|int false means not enrolled, integer means timeend
+     */
+    public function try_autoenrol(stdClass $instance) {
+        global $USER;
+
+        return false;
+    }
+
+    /**
+     * Attempt to automatically gain temporary guest access to course,
+     * calling code has to make sure the plugin and instance are active.
+     *
+     * @param stdClass $instance course enrol instance
+     * @param stdClass $user record
+     * @return bool|int false means no guest access, integer means timeend
+     */
+    public function try_guestaccess(stdClass $instance) {
+        global $USER;
+
+        return false;
+    }
+
+    /**
+     * Enrol user into course via enrol instance.
+     *
+     * @param stdClass $instance
+     * @param int $userid
+     * @param int $roleid optional role id
+     * @param int $timestart
+     * @param int $timeend
+     * @return void
+     */
+    public function enrol_user(stdClass $instance, $userid, $roleid = null, $timestart = 0, $timeend = 0) {
+        global $DB, $USER, $CFG; // CFG necessary!!!
+
+        if ($instance->courseid == SITEID) {
+            throw new coding_exception('invalid attempt to enrol into frontpage course!');
+        }
+
+        $name = $this->get_name();
+        $courseid = $instance->courseid;
+
+        if ($instance->enrol !== $name) {
+            throw new coding_exception('invalid enrol instance!');
+        }
+        $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
+
+        $inserted = false;
+        if ($ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
+            if ($ue->timestart != $timestart or $ue->timeend != $timeend) {
+                $ue->timestart    = $timestart;
+                $ue->timeend      = $timeend;
+                $ue->modifier     = $USER->id;
+                $ue->timemodified = time();
+                $DB->update_record('user_enrolments', $ue);
+            }
+        } else {
+            $ue = new object();
+            $ue->enrolid      = $instance->id;
+            $ue->status       = ENROL_USER_ACTIVE;
+            $ue->userid       = $userid;
+            $ue->timestart    = $timestart;
+            $ue->timeend      = $timeend;
+            $ue->modifier     = $USER->id;
+            $ue->timemodified = time();
+            $ue->id = $DB->insert_record('user_enrolments', $ue);
+
+            $inserted = true;
+        }
+
+        if ($roleid) {
+            if ($this->roles_protected()) {
+                role_assign($roleid, $userid, $context->id, 'enrol_'.$name, $instance->id);
+            } else {
+                role_assign($roleid, $userid, $context->id);
+            }
+        }
+
+        if ($inserted) {
+            // add extra info and trigger event
+            $ue->courseid  = $courseid;
+            $ue->enrol     = $name;
+            events_trigger('user_enrolled', $ue);
+        }
+
+        // reset primitive require_login() caching
+        if ($userid == $USER->id) {
+            if (isset($USER->enrol['enrolled'][$courseid])) {
+                unset($USER->enrol['enrolled'][$courseid]);
+            }
+            if (isset($USER->enrol['tempguest'][$courseid])) {
+                unset($USER->enrol['tempguest'][$courseid]);
+                $USER->access = remove_temp_roles($context, $USER->access);
+            }
+        }
+    }
+
+    /**
+     * Store user_enrolments changes and trigger event.
+     *
+     * @param object $ue
+     * @param int $user id
+     * @param int $status
+     * @param int $timestart
+     * @param int $timeend
+     * @return void
+     */
+    public function update_user_enrol(stdClass $instance, $userid, $status = NULL, $timestart = NULL, $timeend = NULL) {
+        global $DB, $USER;
+
+        $name = $this->get_name();
+
+        if ($instance->enrol !== $name) {
+            throw new coding_exception('invalid enrol instance!');
+        }
+
+        if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
+            // weird, user not enrolled
+            return;
+        }
+
+        $modified = false;
+        if (isset($status) and $ue->status != $status) {
+            $ue->status = $status;
+            $modified = true;
+        }
+        if (isset($timestart) and $ue->timestart != $timestart) {
+            $ue->timestart = $timestart;
+            $modified = true;
+        }
+        if (isset($timeend) and $ue->timeend != $timeend) {
+            $ue->timeend = $timeend;
+            $modified = true;
+        }
+
+        if (!$modified) {
+            // no change
+            return;
+        }
+
+        $ue->modifierid = $USER->id;
+        $DB->update_record('user_enrolments', $ue);
+
+        // trigger event
+        $ue->courseid  = $instance->courseid;
+        $ue->enrol     = $instance->name;
+        events_trigger('user_unenrol_modified', $ue);
+    }
+
+    /**
+     * Unenrol user from course,
+     * the last unenrolment removes all remaining roles.
+     *
+     * @param stdClass $instance
+     * @param int $userid
+     * @return void
+     */
+    public function unenrol_user(stdClass $instance, $userid) {
+        global $CFG, $USER, $DB;
+
+        $name = $this->get_name();
+        $courseid = $instance->courseid;
+
+        if ($instance->enrol !== $name) {
+            throw new coding_exception('invalid enrol instance!');
+        }
+        $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
+
+        if (!$ue = $DB->get_record('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$userid))) {
+            // weird, user not enrolled
+            return;
+        }
+
+        role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id, 'component'=>'enrol_'.$name, 'itemid'=>$instance->id));
+        $DB->delete_records('user_enrolments', array('id'=>$ue->id));
+
+        // add extra info and trigger event
+        $ue->courseid  = $courseid;
+        $ue->enrol     = $name;
+
+        $sql = "SELECT 'x'
+                  FROM {user_enrolments} ue
+                  JOIN {enrol} e ON (e.id = ue.enrolid)
+                  WHERE ue.userid = :userid AND e.courseid = :courseid";
+        if ($DB->record_exists_sql($sql, array('userid'=>$userid, 'courseid'=>$courseid))) {
+            $ue->lastenrol = false;
+            events_trigger('user_unenrolled', $ue);
+            // user still has some enrolments, no big cleanup yet
+        } else {
+            // the big cleanup IS necessary!
+
+            require_once("$CFG->dirroot/group/lib.php");
+            require_once("$CFG->libdir/gradelib.php");
+
+            // remove all remaining roles
+            role_unassign_all(array('userid'=>$userid, 'contextid'=>$context->id), true, false);
+
+            //clean up ALL invisible user data from course if this is the last enrolment - groups, grades, etc.
+            groups_delete_group_members($courseid, $userid);
+
+            grade_user_unenrol($courseid, $userid);
+
+            $DB->delete_records('user_lastaccess', array('userid'=>$userid, 'courseid'=>$courseid));
+
+            $ue->lastenrol = false;
+            events_trigger('user_unenrolled', $ue);
+        }
+        // reset primitive require_login() caching
+        if ($userid == $USER->id) {
+            if (isset($USER->enrol['enrolled'][$courseid])) {
+                unset($USER->enrol['enrolled'][$courseid]);
+            }
+            if (isset($USER->enrol['tempguest'][$courseid])) {
+                unset($USER->enrol['tempguest'][$courseid]);
+                $USER->access = remove_temp_roles($context, $USER->access);
+            }
+        }
+    }
+
+    /**
+     * Forces synchronisation of user enrolments.
+     *
+     * This is important especially for external enrol plugins,
+     * this function is called for all enabled enrol plugins
+     * right after every user login.
+     *
+     * @param object $user user record
+     * @return void
+     */
+    public function sync_user_enrolments($user) {
+        // override if necessary
+    }
+
+    /**
+     * Returns link to page which may be used to add new instance of enrolment plugin in course.
+     * @param int $courseid
+     * @return moodle_url page url
+     */
+    public function get_candidate_link($courseid) {
+        // override for most plugins, check if instance already exists in cases only one instance is supported
+        return NULL;
+    }
+
+    /**
+     * Is it possible to delete enrol instance via standard UI?
+     *
+     * @param object $instance
+     * @return bool
+     */
+    public function instance_deleteable($instance) {
+        return true;
+    }
+
+    /**
+     * Returns link to manual enrol UI if exists.
+     * Does the access control tests automatically.
+     *
+     * @param object $instance
+     * @return moodle_url
+     */
+    public function get_manual_enrol_link($instance) {
+        return NULL;
+    }
+
+    /**
+     * Returns list of unenrol links for all enrol instances in course.
+     *
+     * @param int $courseid
+     * @return moodle_url
+     */
+    public function get_unenrolself_link($instance) {
+        global $USER, $CFG, $DB;
+
+        $name = $this->get_name();
+        if ($instance->enrol !== $name) {
+            throw new coding_exception('invalid enrol instance!');
+        }
+
+        if ($instance->courseid == SITEID) {
+            return NULL;
+        }
+
+        if (!enrol_is_enabled($name)) {
+            return NULL;
+        }
+
+        if ($instance->status != ENROL_INSTANCE_ENABLED) {
+            return NULL;
+        }
+
+        $context = get_context_instance(CONTEXT_COURSE, $instance->courseid, MUST_EXIST);
+        $courseid = $instance->courseid;
+
+        if (!file_exists("$CFG->dirroot/enrol/$name/unenrolself.php")) {
+            return NULL;
+        }
+
+        $context = get_context_instance(CONTEXT_COURSE, $courseid);
+        if (!has_capability("enrol/$name:unenrolself", $context)) {
+            return NULL;
+        }
+
+        if (!$DB->record_exists('user_enrolments', array('enrolid'=>$instance->id, 'userid'=>$USER->id, 'status'=>ENROL_USER_ACTIVE))) {
+            return NULL;
+        }
+
+        return new moodle_url("/enrol/$name/unenrolself.php", array('enrolid'=>$instance->id));;
+    }
+
+    /**
+     * Adds enrol instance UI to course edit form
+     *
+     * @param object $instance enrol instance or null if does not exist yet
+     * @param MoodleQuickForm $mform
+     * @param object $data
+     * @param object $context context of existing course or parent category if course does not exist
+     * @return void
+     */
+    public function course_edit_form($instance, MoodleQuickForm $mform, $data, $context) {
+        // override - usually at least enable/disable switch, has to add own form header
+    }
+
+    /**
+     * Validates course edit form data
+     *
+     * @param object $instance enrol instance or null if does not exist yet
+     * @param array $data
+     * @param object $context context of existing course or parent category if course does not exist
+     * @return array errors array
+     */
+    public function course_edit_validation($instance, array $data, $context) {
+        return array();
+    }
+
+    /**
+     * Called after updating/inserting course.
+     *
+     * @param bool $inserted true if course just inserted
+     * @param object $course
+     * @param object $data form data
+     * @return void
+     */
+    public function course_updated($inserted, $course, $data) {
+        // override if settings on course edit page or some automatic sync needed
+    }
+
+    /**
+     * Add new instance of enrol plugin settings.
+     * @param object $course
+     * @param array instance fields
+     * @return int id of new instance
+     */
+    public function add_instance($course, array $fields = NULL) {
+        global $DB;
+
+        if ($course->id == SITEID) {
+            throw new coding_exception('Invalid request to add enrol instance to frontpage.');
+        }
+
+        $instance = new object();
+        $instance->enrol          = $this->get_name();
+        $instance->status         = ENROL_INSTANCE_ENABLED;
+        $instance->courseid       = $course->id;
+        $instance->enrolstartdate = 0;
+        $instance->enrolenddate   = 0;
+        $instance->timemodified   = time();
+        $instance->timecreated    = $instance->timemodified;
+        $instance->sortorder      = $DB->get_field('enrol', 'COALESCE(MAX(sortorder), -1) + 1', array('courseid'=>$course->id));
+
+        $fields = (array)$fields;
+        unset($fields['enrol']);
+        unset($fields['courseid']);
+        unset($fields['sortorder']);
+        foreach($fields as $field=>$value) {
+            $instance->$field = $value;
+        }
+
+        return $DB->insert_record('enrol', $instance);
+    }
+
+    /**
+     * Add new instance of enrol plugin with default settings,
+     * called when adding new instance manually or when adding new course.
+     *
+     * Not all plugins support this.
+     *
+     * @param object $course
+     * @return int id of new instance or null if no default supported
+     */
+    public function add_default_instance($course) {
+        return null;
+    }
+
+    /**
+     * Delete course enrol plugin instance, unenrol all users.
+     * @param object $instance
+     * @return void
+     */
+    public function delete_instance($instance) {
+        global $DB;
+
+        $name = $this->get_name();
+        if ($instance->enrol !== $name) {
+            throw new coding_exception('invalid enrol instance!');
+        }
+
+        //first unenrol all users
+        $participants = $DB->get_recordset('user_enrolments', array('enrolid'=>$instance->id));
+        foreach ($participants as $participant) {
+            $this->unenrol_user($instance, $participant->userid);
+        }
+        $participants->close();
+
+        // now clean up all remainders that were not removed correctly
+        $DB->delete_records('role_assignments', array('itemid'=>$instance->id, 'component'=>$name));
+        $DB->delete_records('user_enrolments', array('enrolid'=>$instance->id));
+
+        // finally drop the enrol row
+        $DB->delete_records('enrol', array('id'=>$instance->id));
+    }
+
+    /**
+     * Creates course enrol form, checks if form submitted
+     * and enrols user if necessary. It can also redirect.
+     *
+     * @param stdClass $instance
+     * @return string html text, usually a form in a text box
+     */
+    public function enrol_page_hook(stdClass $instance) {
+        return null;
+    }
+
+    /**
+     * Adds navigation links into course admin block.
+     *
+     * By defaults looks for manage links only.
+     *
+     * @param navigation_node $instancesnode
+     * @param object $instance
+     * @return moodle_url;
+     */
+    public function add_course_navigation($instancesnode, stdClass $instance) {
+        if ($managelink = $this->get_manage_link($instance)) {
+            $instancesnode->add($this->get_instance_name($instance), $managelink, navigation_node::TYPE_SETTING);
+        }
+    }
+
+    /**
+     * Returns enrolment instance manage link.
+     *
+     * By defaults looks for manage.php file and tests for manage capability.
+     *
+     * @param object $instance
+     * @return moodle_url;
+     */
+    public function get_manage_link($instance) {
+        global $CFG, $DB;
+
+        $name = $this->get_name();
+
+        if ($instance->enrol !== $name) {
+             throw new coding_exception('Invalid enrol instance type!');
+        }
+
+        if (!file_exists("$CFG->dirroot/enrol/$name/manage.php")) {
+            return NULL;
+        }
+
+        if ($instance->courseid == SITEID) {
+            // no enrolments on the frontpage, only roles there allowed
+            return NULL;
+        }
+
+        $context = get_context_instance(CONTEXT_COURSE, $instance->courseid);
+        if (!has_capability('enrol/'.$name.':manage', $context)) {
+            return NULL;
+        }
+
+        return new moodle_url("/enrol/$name/manage.php", array('enrolid'=>$instance->id));
+    }
+
+    /**
+     * Reads version.php and determines if it is necessary
+     * to execute the cron job now.
+     * @return bool
+     */
+    public function is_cron_required() {
+        global $CFG;
+
+        $name = $this->get_name();
+        $versionfile = "$CFG->dirroot/enrol/$name/version.php";
+        $plugin = new object();
+        include($versionfile);
+        if (empty($plugin->cron)) {
+            return false;
+        }
+        $lastexecuted = $this->get_config('lastcron', 0);
+        if ($lastexecuted + $plugin->cron < time()) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Called for all enabled enrol plugins that returned true from is_cron_required().
+     * @return void
+     */
+    public function cron() {
+    }
+
+    /**
+     * Called when user is about to be deleted
+     * @param object $user
+     * @return void
+     */
+    public function user_delete($user) {
+        global $DB;
+
+        $sql = "SELECT e.*
+                  FROM {enrol} e
+                  JOIN {user_enrolments} ue ON (ue.courseid = e.courseid)
+                 WHERE e.enrol = :meta AND ue.userid = :userid";
+        $params = array('name'=>$this->get_name(), 'userid'=>$user->id);
+
+        $rs = $DB->get_records_recordset($sql, $params);
+        foreach($rs as $instance) {
+            $this->unenrol_user($instance, $user->id);
+        }
+        $rs->close();
+    }
+}
+
diff --git a/lib/eventslib.php b/lib/eventslib.php
index bcee5a7f0f01..49fb7d6cacec 100755
--- a/lib/eventslib.php
+++ b/lib/eventslib.php
@@ -472,6 +472,12 @@ function events_trigger($eventname, $eventdata) {
                     $dump = '';
                     $callers = debug_backtrace();
                     foreach ($callers as $caller) {
+                        if (!isset($caller['line'])) {
+                            $caller['line'] = '?';
+                        }
+                        if (!isset($caller['file'])) {
+                            $caller['file'] = '?';
+                        }
                         $dump .= 'line ' . $caller['line'] . ' of ' . substr($caller['file'], strlen($CFG->dirroot) + 1);
                         if (isset($caller['function'])) {
                             $dump .= ': call to ';
diff --git a/lib/externallib.php b/lib/externallib.php
index c8742185e461..d4289dc327bd 100644
--- a/lib/externallib.php
+++ b/lib/externallib.php
@@ -307,25 +307,7 @@ protected static function validate_context($context) {
 
         if ($context->contextlevel >= CONTEXT_COURSE) {
             list($context, $course, $cm) = get_context_info_array($context->id);
-            // must be enrolled or viewing
-            if (!is_enrolled($context) and !is_viewing($context)) {
-                throw new invalid_parameter_exception('Must be enrolled in course or be allowed to inspect it.');
-            }
-            // make sure the course is actually visible
-            if (!($course->visible && course_parent_visible($COURSE)) && !has_capability('moodle/course:viewhiddencourses', get_context_instance(CONTEXT_COURSE, $course->id))) {
-                throw new invalid_parameter_exception('Invalid course.');
-            }
-            // make sure the activity is actually visible
-            if ($cm && !$cm->visible && !has_capability('moodle/course:viewhiddenactivities', get_context_instance(CONTEXT_MODULE, $cm->id))) {
-                throw new invalid_parameter_exception('Invalid activity.');
-            }
-            // verify group memebers
-            if (!empty($CFG->enablegroupmembersonly) and $cm and $cm->groupmembersonly and !has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
-                if (!groups_has_membership($cm)) {
-                    throw new invalid_parameter_exception('Must be member of at least one group.');
-                }
-            }
-            //TODO: verify course completion
+            require_login($course, false, $cm, false, true);
         }
     }
 }
@@ -468,7 +450,7 @@ function external_generate_token($tokentype, $serviceorid, $userid, $contextorid
     return $newtoken->token;
 }
 /**
- * Create and return a session linked token. Token to be used for html embedded client apps that want to communicate 
+ * Create and return a session linked token. Token to be used for html embedded client apps that want to communicate
  * with the Moodle server through web services. The token is linked to the current session for the current page request.
  * It is expected this will be called in the script generating the html page that is embedding the client app and that the
  * returned token will be somehow passed into the client app being embedded in the page.
diff --git a/lib/file/file_browser.php b/lib/file/file_browser.php
index 399c8488c7c0..752bd60c7321 100644
--- a/lib/file/file_browser.php
+++ b/lib/file/file_browser.php
@@ -318,7 +318,7 @@ private function get_file_info_course($context, $filearea=null, $itemid=null, $f
             return null;
         }
 
-        if (!is_null($filearea) and !in_array($filearea, array('course_intro', 'course_content', 'course_section', 'course_backup', 'section_backup'))) {
+        if (!is_null($filearea) and !in_array($filearea, array('course_summary', 'course_content', 'course_section', 'course_backup', 'section_backup'))) {
             // file area does not exist, sorry
             $filearea = null;
         }
@@ -344,7 +344,7 @@ private function get_file_info_course($context, $filearea=null, $itemid=null, $f
         return null;
     }
 
-    private function get_file_info_course_intro($course, $context, $filearea=null, $itemid=null, $filepath=null, $filename=null) {
+    private function get_file_info_course_summary($course, $context, $filearea=null, $itemid=null, $filepath=null, $filename=null) {
         global $CFG;
 
         $fs = get_file_storage();
diff --git a/lib/file/file_info_course.php b/lib/file/file_info_course.php
index 9d6e825a5b71..3cdd866a2c00 100644
--- a/lib/file/file_info_course.php
+++ b/lib/file/file_info_course.php
@@ -78,7 +78,7 @@ public function is_directory() {
     public function get_children() {
         $children = array();
 
-        if ($child = $this->browser->get_file_info($this->context, 'course_intro', 0)) {
+        if ($child = $this->browser->get_file_info($this->context, 'course_summary', 0)) {
             $children[] = $child;
         }
         if ($child = $this->browser->get_file_info($this->context, 'course_section')) {
diff --git a/lib/gradelib.php b/lib/gradelib.php
index af512664b442..f44c76983825 100644
--- a/lib/gradelib.php
+++ b/lib/gradelib.php
@@ -1208,6 +1208,34 @@ function grade_uninstalled_module($modname) {
     }
 }
 
+/**
+ * Deletes all user data from gradebook.
+ * @param $userid
+ */
+function grade_user_delete($userid) {
+    if ($grades = grade_grade::fetch_all(array('userid'=>$userid))) {
+        foreach ($grades as $grade) {
+            $grade->delete('userdelete');
+        }
+    }
+}
+
+/**
+ * Purge course data when user unenrolled.
+ * @param $userid
+ */
+function grade_user_unenrol($courseid, $userid) {
+    if ($items = grade_item::fetch_all(array('courseid'=>$courseid))) {
+        foreach ($items as $item) {
+            if ($grades = grade_grade::fetch_all(array('userid'=>$userid, 'itemid'=>$item->id))) {
+                foreach ($grades as $grade) {
+                    $grade->delete('userdelete');
+                }
+            }
+        }
+    }
+}
+
 /**
  * Grading cron job
  *
diff --git a/lib/moodlelib.php b/lib/moodlelib.php
index b9496992d225..8c796fd379a1 100644
--- a/lib/moodlelib.php
+++ b/lib/moodlelib.php
@@ -2147,60 +2147,65 @@ function get_login_url($loginguest=false) {
  * When $cm parameter specified, this function sets page layout to 'module'.
  * You need to change it manually later if some other layout needed.
  *
- * @global object
- * @global object
- * @global object
- * @global object
- * @global string
- * @global object
- * @global object
- * @global object
- * @uses SITEID Define
  * @param mixed $courseorid id of the course or course object
  * @param bool $autologinguest default true
  * @param object $cm course module object
  * @param bool $setwantsurltome Define if we want to set $SESSION->wantsurl, defaults to
  *             true. Used to avoid (=false) some scripts (file.php...) to set that variable,
  *             in order to keep redirects working properly. MDL-14495
+ * @param bool $preventredirect set to true in scripts that can not redirect (CLI, rss feeds, etc.), throws exceptions
  * @return mixed Void, exit, and die depending on path
  */
-function require_login($courseorid=0, $autologinguest=true, $cm=null, $setwantsurltome=true) {
-    global $CFG, $SESSION, $USER, $COURSE, $FULLME, $PAGE, $SITE, $DB, $OUTPUT;
+function require_login($courseorid = NULL, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) {
+    global $CFG, $SESSION, $USER, $FULLME, $PAGE, $SITE, $DB, $OUTPUT;
 
-/// setup global $COURSE, themes, language and locale
+    // setup global $COURSE, themes, language and locale
     if (!empty($courseorid)) {
         if (is_object($courseorid)) {
             $course = $courseorid;
         } else if ($courseorid == SITEID) {
             $course = clone($SITE);
         } else {
-            $course = $DB->get_record('course', array('id' => $courseorid));
-            if (!$course) {
-                throw new moodle_exception('invalidcourseid');
-            }
+            $course = $DB->get_record('course', array('id' => $courseorid), '*', MUST_EXIST);
         }
         if ($cm) {
+            if ($cm->course != $course->id) {
+                throw new coding_exception('course and cm parameters in require_login() call do not match!!');
+            }
             $PAGE->set_cm($cm, $course); // set's up global $COURSE
             $PAGE->set_pagelayout('incourse');
         } else {
             $PAGE->set_course($course); // set's up global $COURSE
         }
     } else {
-        // do not touch global $COURSE via $PAGE->set_course() !!
+        // do not touch global $COURSE via $PAGE->set_course(),
+        // the reasons is we need to be able to call require_login() at any time!!
+        $course = $SITE;
+        if ($cm) {
+            throw new coding_exception('cm parameter in require_login() requires valid course parameter!');
+        }
     }
 
-/// If the user is not even logged in yet then make sure they are
+    // If the user is not even logged in yet then make sure they are
     if (!isloggedin()) {
         //NOTE: $USER->site check was obsoleted by session test cookie,
         //      $USER->confirmed test is in login/index.php
+        if ($preventredirect) {
+            throw new require_login_exception('You are not logged in');
+        }
+
         if ($setwantsurltome) {
             $SESSION->wantsurl = $FULLME;
         }
         if (!empty($_SERVER['HTTP_REFERER'])) {
             $SESSION->fromurl  = $_SERVER['HTTP_REFERER'];
         }
-        if ($autologinguest and !empty($CFG->guestloginbutton) and !empty($CFG->autologinguests) and ($COURSE->id == SITEID or $COURSE->guest) ) {
-            $loginguest = true;
+        if ($autologinguest and !empty($CFG->guestloginbutton) and !empty($CFG->autologinguests)) {
+            if ($course->id == SITEID) {
+                $loginguest = true;
+            } else {
+                $loginguest = false;
+            }
         } else {
             $loginguest = false;
         }
@@ -2208,19 +2213,19 @@ function require_login($courseorid=0, $autologinguest=true, $cm=null, $setwantsu
         exit; // never reached
     }
 
-/// loginas as redirection if needed
-    if ($COURSE->id != SITEID and session_is_loggedinas()) {
+    // loginas as redirection if needed
+    if ($course->id != SITEID and session_is_loggedinas()) {
         if ($USER->loginascontext->contextlevel == CONTEXT_COURSE) {
-            if ($USER->loginascontext->instanceid != $COURSE->id) {
+            if ($USER->loginascontext->instanceid != $course->id) {
                 print_error('loginasonecourse', '', $CFG->wwwroot.'/course/view.php?id='.$USER->loginascontext->instanceid);
             }
         }
     }
 
-/// check whether the user should be changing password (but only if it is REALLY them)
+    // check whether the user should be changing password (but only if it is REALLY them)
     if (get_user_preferences('auth_forcepasswordchange') && !session_is_loggedinas()) {
         $userauth = get_auth_plugin($USER->auth);
-        if ($userauth->can_change_password()) {
+        if ($userauth->can_change_password() and !$preventredirect) {
             $SESSION->wantsurl = $FULLME;
             if ($changeurl = $userauth->change_password_url()) {
                 //use plugin custom url
@@ -2239,46 +2244,212 @@ function require_login($courseorid=0, $autologinguest=true, $cm=null, $setwantsu
         }
     }
 
-/// Check that the user account is properly set up
+    // Check that the user account is properly set up
     if (user_not_fully_set_up($USER)) {
+        if ($preventredirect) {
+            throw new require_login_exception('User not fully set-up');
+        }
         $SESSION->wantsurl = $FULLME;
         redirect($CFG->wwwroot .'/user/edit.php?id='. $USER->id .'&amp;course='. SITEID);
     }
 
-/// Make sure the USER has a sesskey set up.  Used for checking script parameters.
+    // Make sure the USER has a sesskey set up. Used for CSRF protection.
     sesskey();
 
+    // Do not bother admins with any formalities, no last access updates either
+    if (is_siteadmin()) {
+        return;
+    }
+
     // Check that the user has agreed to a site policy if there is one
     if (!empty($CFG->sitepolicy)) {
+        if ($preventredirect) {
+            throw new require_login_exception('Policy not agreed');
+        }
         if (!$USER->policyagreed) {
             $SESSION->wantsurl = $FULLME;
             redirect($CFG->wwwroot .'/user/policy.php');
         }
     }
 
-    // Fetch the system context, we are going to use it a lot.
+    // Fetch the system context, the course context, and prefetch its child contexts
     $sysctx = get_context_instance(CONTEXT_SYSTEM);
+    $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id, MUST_EXIST);
+    if ($cm) {
+        $cmcontext = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
+    } else {
+        $cmcontext = null;
+    }
 
-/// If the site is currently under maintenance, then print a message
+    // If the site is currently under maintenance, then print a message
     if (!empty($CFG->maintenance_enabled) and !has_capability('moodle/site:config', $sysctx)) {
+        if ($preventredirect) {
+            throw new require_login_exception('Maintenance in progress');
+        }
+
         print_maintenance_message();
     }
 
-/// groupmembersonly access control
+    // make sure the course itself is not hidden
+    if ($course->id == SITEID) {
+        // frontpage can not be hidden
+    } else {
+        if (!empty($USER->access['rsw'][$coursecontext->path])) {
+            // when switching roles ignore the hidden flag - user had to be in course to do the switch
+        } else {
+            if (!$course->visible and !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
+                // originally there was also test of parent category visibility,
+                // BUT is was very slow in complex queries involving "my courses"
+                // now it is also possible to simply hide all courses user is not enrolled in :-)
+                if ($preventredirect) {
+                    throw new require_login_exception('Course is hidden');
+                }
+                notice(get_string('coursehidden'), $CFG->wwwroot .'/');
+            }
+        }
+    }
+
+    // is the user enrolled?
+    if ($course->id == SITEID) {
+        // everybody is enrolled on the frontpage
+
+    } else {
+        if (session_is_loggedinas()) {
+            // Make sure the REAL person can access this course first
+            $realuser = session_get_realuser();
+            if (!is_enrolled($coursecontext, $realuser->id, '', true) and !is_viewing($coursecontext, $realuser->id) and !is_siteadmin($realuser->id)) {
+                if ($preventredirect) {
+                    throw new require_login_exception('Invalid course login-as access');
+                }
+                echo $OUTPUT->header();
+                notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
+            }
+        }
+
+        // very simple enrolment caching - changes in course setting are not reflected immediately
+        if (!isset($USER->enrol)) {
+            $USER->enrol = array();
+            $USER->enrol['enrolled'] = array();
+            $USER->enrol['tempguest'] = array();
+        }
+
+        $access = false;
+
+        if (is_viewing($coursecontext, $USER)) {
+            // ok, no need to mess with enrol
+            $access = true;
+
+        } else {
+            if (isset($USER->enrol['enrolled'][$course->id])) {
+                if ($USER->enrol['enrolled'][$course->id] == 0) {
+                    $access = true;
+                } else if ($USER->enrol['enrolled'][$course->id] > time()) {
+                    $access = true;
+                } else {
+                    //expired
+                    unset($USER->enrol['enrolled'][$course->id]);
+                }
+            }
+            if (isset($USER->enrol['tempguest'][$course->id])) {
+                if ($USER->enrol['tempguest'][$course->id] == 0) {
+                    $access = true;
+                } else if ($USER->enrol['tempguest'][$course->id] > time()) {
+                    $access = true;
+                } else {
+                    //expired
+                    unset($USER->enrol['tempguest'][$course->id]);
+                    $USER->access = remove_temp_roles($coursecontext, $USER->access);
+                }
+            }
+
+            if ($access) {
+                // cache ok
+            } else if (is_enrolled($coursecontext, $USER, '', true)) {
+                // active participants may always access
+                // TODO: refactor this into some new function
+                $now = time();
+                $sql = "SELECT MAX(ue.timeend)
+                          FROM {user_enrolments} ue
+                          JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
+                          JOIN {user} u ON u.id = ue.userid
+                         WHERE ue.userid = :userid AND ue.status = :active AND e.status = :enabled AND u.deleted = 0
+                               AND ue.timestart < :now1 AND (ue.timeend = 0 OR ue.timeend > :now2)";
+                $params = array('enabled'=>ENROL_INSTANCE_ENABLED, 'active'=>ENROL_USER_ACTIVE,
+                                'userid'=>$USER->id, 'courseid'=>$coursecontext->instanceid, 'now1'=>$now, 'now2'=>$now);
+                $until = $DB->get_field_sql($sql, $params);
+                if (!$until or $until > time() + ENROL_REQUIRE_LOGIN_CACHE_PERIOD) {
+                    $until = time() + ENROL_REQUIRE_LOGIN_CACHE_PERIOD;
+                }
+
+                $USER->enrol['enrolled'][$course->id] = $until;
+                $access = true;
+
+                // remove traces of previous temp guest access
+                $USER->access = remove_temp_roles($coursecontext, $USER->access);
+
+            } else {
+                $instances = $DB->get_records('enrol', array('courseid'=>$course->id, 'status'=>ENROL_INSTANCE_ENABLED), 'sortorder, id ASC');
+                $enrols = enrol_get_plugins(true);
+                // first ask all enabled enrol instances in course if they want to auto enrol user
+                foreach($instances as $instance) {
+                    if (!isset($enrols[$instance->enrol])) {
+                        continue;
+                    }
+                    $until = $enrols[$instance->enrol]->try_autoenrol($instance);
+                    if ($until !== false) {
+                        $USER->enrol['enrolled'][$course->id] = $until;
+                        $USER->access = remove_temp_roles($coursecontext, $USER->access);
+                        $access = true;
+                        break;
+                    }
+                }
+                // if not enrolled yet try to gain temporary guest access
+                if (!$access) {
+                    foreach($instances as $instance) {
+                        if (!isset($enrols[$instance->enrol])) {
+                            continue;
+                        }
+                        $until = $enrols[$instance->enrol]->try_guestaccess($instance);
+                        if ($until !== false) {
+                            $USER->enrol['tempguest'][$course->id] = $until;
+                            $access = true;
+                            break;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (!$access) {
+            if ($preventredirect) {
+                throw new require_login_exception('Not enrolled');
+            }
+            $SESSION->wantsurl = $FULLME;
+            redirect($CFG->wwwroot .'/enrol/index.php?id='. $course->id);
+        }
+    }
+
+    // test visibility
+    if ($cm && !$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $cmcontext)) {
+        if ($preventredirect) {
+            throw new require_login_exception('Activity is hidden');
+        }
+        redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden'));
+    }
+
+    // groupmembersonly access control
     if (!empty($CFG->enablegroupmembersonly) and $cm and $cm->groupmembersonly and !has_capability('moodle/site:accessallgroups', get_context_instance(CONTEXT_MODULE, $cm->id))) {
         if (isguestuser() or !groups_has_membership($cm)) {
+            if ($preventredirect) {
+                throw new require_login_exception('Not member of a group');
+            }
             print_error('groupmembersonlyerror', 'group', $CFG->wwwroot.'/course/view.php?id='.$cm->course);
         }
     }
 
-    // Fetch the course context, and prefetch its child contexts
-    $coursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id, MUST_EXIST);
-    if ($cm) {
-        $cmcontext = get_context_instance(CONTEXT_MODULE, $cm->id, MUST_EXIST);
-    }
-
     // Conditional activity access control
     if (!empty($CFG->enableavailability) and $cm) {
+        // TODO: this is going to work with login-as-user, sorry!
         // We cache conditional access in session
         if (!isset($SESSION->conditionaccessok)) {
             $SESSION->conditionaccessok = array();
@@ -2299,93 +2470,8 @@ function require_login($courseorid=0, $autologinguest=true, $cm=null, $setwantsu
         }
     }
 
-    if ($COURSE->id == SITEID) {
-        /// Eliminate hidden site activities straight away
-        if ($cm && !$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $cmcontext)) {
-            redirect($CFG->wwwroot, get_string('activityiscurrentlyhidden'));
-        }
-        user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times
-        return;
-
-    } else {
-
-        /// Check if the user can be in a particular course
-        if (empty($USER->access['rsw'][$coursecontext->path])) {
-            //
-            // MDL-13900 - If the course or the parent category are hidden
-            // and the user hasn't the 'course:viewhiddencourses' capability, prevent access
-            //
-            if ( !($COURSE->visible && course_parent_visible($COURSE)) && !has_capability('moodle/course:viewhiddencourses', $coursecontext)) {
-                echo $OUTPUT->header();
-                notice(get_string('coursehidden'), $CFG->wwwroot .'/');
-            }
-        }
-
-        if (is_enrolled($coursecontext) or is_viewing($coursecontext)) {
-            // Enrolled user or allowed to visit course (managers, inspectors, etc.)
-            if (session_is_loggedinas()) {   // Make sure the REAL person can also access this course
-                $realuser = session_get_realuser();
-                if (!is_enrolled($coursecontext, $realuser->id) and !is_viewing($coursecontext, $realuser->id) and !is_siteadmin($realuser->id)) {
-                    echo $OUTPUT->header();
-                    notice(get_string('studentnotallowed', '', fullname($USER, true)), $CFG->wwwroot .'/');
-                }
-            }
-
-            // Make sure they can read this activity too, if specified
-            if ($cm && !$cm->visible && !has_capability('moodle/course:viewhiddenactivities', $cmcontext)) {
-                redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course, get_string('activityiscurrentlyhidden'));
-            }
-            user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times
-            return;   // User is allowed to see this course
-
-        } else {
-            // guest access
-            switch ($COURSE->guest) {    /// Check course policy about guest access
-
-                case 1:    /// Guests always allowed
-                    if ($cm and !$cm->visible) { // Not allowed to see module, send to course page
-                        redirect($CFG->wwwroot.'/course/view.php?id='.$cm->course,
-                                 get_string('activityiscurrentlyhidden'));
-                    }
-
-                    if ($USER->username != 'guest' and !empty($CFG->guestroleid)) {
-                        // Non-guests who don't currently have access, check if they can be allowed in as a guest
-                        // Temporarily assign them guest role for this context, if it fails later user is asked to enrol
-                        $USER->access = load_temp_role($coursecontext, $CFG->guestroleid, $USER->access);
-                    }
-
-                    user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times
-                    return;   // User is allowed to see this course
-
-                case 2:    /// Guests allowed with key
-                    if (!empty($USER->enrolkey[$COURSE->id])) {   // Set by enrol/manual/enrol.php
-                        user_accesstime_log($COURSE->id); /// Access granted, update lastaccess times
-                        return true;
-                    }
-                    //  otherwise drop through to logic below (--> enrol.php)
-                    break;
-
-                default:    /// Guests not allowed
-                    $strloggedinasguest = get_string('loggedinasguest');
-                    $PAGE->navbar->add($strloggedinasguest);
-                    echo $OUTPUT->header();
-                    if (empty($USER->access['rsw'][$coursecontext->path])) {  // Normal guest
-                        notice(get_string('guestsnotallowed', '', format_string($COURSE->fullname)), get_login_url());
-                    } else {
-                        echo $OUTPUT->notification(get_string('guestsnotallowed', '', format_string($COURSE->fullname)));
-                        echo '<div class="notifyproblem">'.switchroles_form($COURSE->id).'</div>';
-                        echo $OUTPUT->footer();
-                        exit;
-                    }
-                    break;
-            }
-        }
-
-        // Currently not enrolled in the course, so see if they want to enrol
-        $SESSION->wantsurl = $FULLME;
-        redirect($CFG->wwwroot .'/course/enrol.php?id='. $COURSE->id);
-        die;
-    }
+    // Finally access granted, update lastaccess times
+    user_accesstime_log($course->id);
 }
 
 
@@ -2425,26 +2511,28 @@ function require_logout() {
  * @param bool $setwantsurltome Define if we want to set $SESSION->wantsurl, defaults to
  *             true. Used to avoid (=false) some scripts (file.php...) to set that variable,
  *             in order to keep redirects working properly. MDL-14495
+ * @param bool $preventredirect set to true in scripts that can not redirect (CLI, rss feeds, etc.), throws exceptions
+ * @return void
  */
-function require_course_login($courseorid, $autologinguest=true, $cm=null, $setwantsurltome=true) {
+function require_course_login($courseorid, $autologinguest = true, $cm = NULL, $setwantsurltome = true, $preventredirect = false) {
     global $CFG, $PAGE, $SITE;
     if (!empty($CFG->forcelogin)) {
         // login required for both SITE and courses
-        require_login($courseorid, $autologinguest, $cm, $setwantsurltome);
+        require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
 
     } else if (!empty($cm) and !$cm->visible) {
         // always login for hidden activities
-        require_login($courseorid, $autologinguest, $cm, $setwantsurltome);
+        require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
 
     } else if ((is_object($courseorid) and $courseorid->id == SITEID)
           or (!is_object($courseorid) and $courseorid == SITEID)) {
               //login for SITE not required
         if ($cm and empty($cm->visible)) {
             // hidden activities are not accessible without login
-            require_login($courseorid, $autologinguest, $cm, $setwantsurltome);
+            require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
         } else if ($cm and !empty($CFG->enablegroupmembersonly) and $cm->groupmembersonly) {
             // not-logged-in users do not have any group membership
-            require_login($courseorid, $autologinguest, $cm, $setwantsurltome);
+            require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
         } else {
             // We still need to instatiate PAGE vars properly so that things
             // that rely on it like navigation function correctly.
@@ -2455,6 +2543,9 @@ function require_course_login($courseorid, $autologinguest=true, $cm=null, $setw
                     $course = clone($SITE);
                 }
                 if ($cm) {
+                    if ($cm->course != $course->id) {
+                        throw new coding_exception('course and cm parameters in require_course_login() call do not match!!');
+                    }
                     $PAGE->set_cm($cm, $course);
                     $PAGE->set_pagelayout('incourse');
                 } else {
@@ -2472,7 +2563,7 @@ function require_course_login($courseorid, $autologinguest=true, $cm=null, $setw
 
     } else {
         // course login always required
-        require_login($courseorid, $autologinguest, $cm, $setwantsurltome);
+        require_login($courseorid, $autologinguest, $cm, $setwantsurltome, $preventredirect);
     }
 }
 
@@ -2754,25 +2845,6 @@ function reset_login_count() {
     $SESSION->logincount = 0;
 }
 
-/**
- * Sync all meta courses
- * Goes through all enrolment records for the courses inside all metacourses and syncs with them.
- * @see sync_metacourse()
- *
- * @global object
- */
-function sync_metacourses() {
-    global $DB;
-
-    if (!$courses = $DB->get_records('course', array('metacourse'=>1))) {
-        return;
-    }
-
-    foreach ($courses as $course) {
-        sync_metacourse($course);
-    }
-}
-
 /**
  * Returns reference to full info about modules in course (including visibility).
  * Cached and as fast as possible (0 or 1 db query).
@@ -2970,157 +3042,6 @@ function &get_fast_modinfo(&$course, $userid=0) {
     return $cache[$course->id];
 }
 
-/**
- * Goes through all enrolment records for the courses inside the metacourse and sync with them.
- *
- * @todo finish timeend and timestart maybe we could rely on cron
- *       job to do the cleaning from time to time
- *
- * @global object
- * @global object
- * @uses CONTEXT_COURSE
- * @param mixed $course the metacourse to synch. Either the course object itself, or the courseid.
- * @return bool Success
- */
-function sync_metacourse($course) {
-    global $CFG, $DB;
-
-    // Check the course is valid.
-    if (!is_object($course)) {
-        if (!$course = $DB->get_record('course', array('id'=>$course))) {
-            return false; // invalid course id
-        }
-    }
-
-    // Check that we actually have a metacourse.
-    if (empty($course->metacourse)) {
-        return false;
-    }
-
-    // Get a list of roles that should not be synced.
-    if (!empty($CFG->nonmetacoursesyncroleids)) {
-        $roleexclusions = 'ra.roleid NOT IN (' . $CFG->nonmetacoursesyncroleids . ') AND';
-    } else {
-        $roleexclusions = '';
-    }
-
-    // Get the context of the metacourse.
-    $context = get_context_instance(CONTEXT_COURSE, $course->id); // SITEID can not be a metacourse
-
-    // We do not ever want to unassign the list of metacourse manager, so get a list of them.
-    if ($users = get_users_by_capability($context, 'moodle/course:managemetacourse')) {
-        $managers = array_keys($users);
-    } else {
-        $managers = array();
-    }
-
-    // Get assignments of a user to a role that exist in a child course, but
-    // not in the meta coure. That is, get a list of the assignments that need to be made.
-    if (!$assignments = $DB->get_records_sql("
-            SELECT ra.id, ra.roleid, ra.userid
-              FROM {role_assignments} ra, {context} con, {course_meta} cm
-             WHERE ra.contextid = con.id AND
-                   con.contextlevel = ".CONTEXT_COURSE." AND
-                   con.instanceid = cm.child_course AND
-                   cm.parent_course = ? AND
-                   $roleexclusions
-                   NOT EXISTS (
-                     SELECT 1
-                       FROM {role_assignments} ra2
-                      WHERE ra2.userid = ra.userid AND
-                            ra2.roleid = ra.roleid AND
-                            ra2.contextid = ?
-                  )", array($course->id, $context->id))) {
-        $assignments = array();
-    }
-
-    // Get assignments of a user to a role that exist in the meta course, but
-    // not in any child courses. That is, get a list of the unassignments that need to be made.
-    if (!$unassignments = $DB->get_records_sql("
-            SELECT ra.id, ra.roleid, ra.userid
-              FROM {role_assignments} ra
-             WHERE ra.contextid = ? AND
-                   $roleexclusions
-                   NOT EXISTS (
-                    SELECT 1
-                      FROM {role_assignments} ra2, {context} con2, {course_meta} cm
-                    WHERE ra2.userid = ra.userid AND
-                          ra2.roleid = ra.roleid AND
-                          ra2.contextid = con2.id AND
-                          con2.contextlevel = " . CONTEXT_COURSE . " AND
-                          con2.instanceid = cm.child_course AND
-                          cm.parent_course = ?
-                  )", array($context->id, $course->id))) {
-        $unassignments = array();
-    }
-
-    $success = true;
-
-    // Make the unassignments, if they are not managers.
-    foreach ($unassignments as $unassignment) {
-        if (!in_array($unassignment->userid, $managers)) {
-            $success = role_unassign($unassignment->roleid, $unassignment->userid, 0, $context->id) && $success;
-        }
-    }
-
-    // Make the assignments.
-    foreach ($assignments as $assignment) {
-        $success = role_assign($assignment->roleid, $assignment->userid, 0, $context->id, 0, 0) && $success;
-    }
-
-    return $success;
-
-// TODO: finish timeend and timestart
-// maybe we could rely on cron job to do the cleaning from time to time
-}
-
-/**
- * Adds a record to the metacourse table and calls sync_metacoures
- *
- * @global object
- * @param int $metacourseid The Metacourse ID for the metacourse to add to
- * @param int $courseid The Course ID of the course to add
- * @return bool Success
- */
-function add_to_metacourse ($metacourseid, $courseid) {
-    global $DB;
-
-    if (!$metacourse = $DB->get_record("course", array("id"=>$metacourseid))) {
-        return false;
-    }
-
-    if (!$course = $DB->get_record("course", array("id"=>$courseid))) {
-        return false;
-    }
-
-    if (!$record = $DB->get_record("course_meta", array("parent_course"=>$metacourseid, "child_course"=>$courseid))) {
-        $rec = new object();
-        $rec->parent_course = $metacourseid;
-        $rec->child_course  = $courseid;
-        $DB->insert_record('course_meta', $rec);
-        return sync_metacourse($metacourseid);
-    }
-    return true;
-
-}
-
-/**
- * Removes the record from the metacourse table and calls sync_metacourse
- *
- * @global object
- * @param int $metacourseid The Metacourse ID for the metacourse to remove from
- * @param int $courseid The Course ID of the course to remove
- * @return bool Success
- */
-function remove_from_metacourse($metacourseid, $courseid) {
-    global $DB;
-
-    if ($DB->delete_records('course_meta', array('parent_course'=>$metacourseid, 'child_course'=>$courseid))) {
-        return sync_metacourse($metacourseid);
-    }
-    return false;
-}
-
 /**
  * Determines if the currently logged in user is in editing mode.
  * Note: originally this function had $userid parameter - it was not usable anyway
@@ -3487,12 +3408,10 @@ function truncate_userinfo($info) {
  * Marks user deleted in internal user database and notifies the auth plugin.
  * Also unenrols user from all roles and does other cleanup.
  *
- * @todo Decide if this transaction is really needed (look for internal TODO:)
+ * Any plugin that needs to purge user data should register the 'user_deleted' event.
  *
- * @global object
- * @global object
- * @param object $user Userobject before delete    (without system magic quotes)
- * @return boolean success
+ * @param object $user User object before delete
+ * @return boolean always true
  */
 function delete_user($user) {
     global $CFG, $DB;
@@ -3500,12 +3419,8 @@ function delete_user($user) {
     require_once($CFG->libdir.'/gradelib.php');
     require_once($CFG->dirroot.'/message/lib.php');
 
-        // delete all grades - backup is kept in grade_grades_history table
-        if ($grades = grade_grade::fetch_all(array('userid'=>$user->id))) {
-            foreach ($grades as $grade) {
-                $grade->delete('userdelete');
-            }
-        }
+    // delete all grades - backup is kept in grade_grades_history table
+    grade_user_delete($user->id);
 
     //move unread messages from this user to read
     message_move_userfrom_unread2read($user->id);
@@ -3516,10 +3431,19 @@ function delete_user($user) {
     // remove from all groups
     $DB->delete_records('groups_members', array('userid'=>$user->id));
 
-    // unenrol from all roles in all contexts
-    role_unassign(0, $user->id); // this might be slow but it is really needed - modules might do some extra cleanup!
+    // brute force unenrol from all courses
+    $DB->delete_records('user_enrolments', array('userid'=>$user->id));
+
+    // purge user preferences
+    $DB->delete_records('user_preferences', array('userid'=>$user->id));
+
+    // purge user extra profile info
+    $DB->delete_records('user_info_data', array('userid'=>$user->id));
+
+    // last course access not necessary either
+    $DB->delete_records('user_lastaccess', array('userid'=>$user->id));
 
-    // now do a final accesslib cleanup - removes all role assignments in user context and context itself
+    // final accesslib cleanup - removes all role assignments in user context and context itself
     delete_context(CONTEXT_USER, $user->id);
 
     require_once($CFG->dirroot.'/tag/lib.php');
@@ -3546,6 +3470,7 @@ function delete_user($user) {
     $authplugin = get_auth_plugin($user->auth);
     $authplugin->user_delete($user);
 
+    // any plugin that needs to cleanup should register this event
     events_trigger('user_deleted', $user);
 
     return true;
@@ -4129,26 +4054,6 @@ function remove_course_contents($courseid, $showfeedback=true) {
         $DB->delete_records($table, array($col=>$course->id));
     }
 
-
-/// Clean up metacourse stuff
-
-    if ($course->metacourse) {
-        $DB->delete_records("course_meta", array("parent_course"=>$course->id));
-        sync_metacourse($course->id); // have to do it here so the enrolments get nuked. sync_metacourses won't find it without the id.
-        if ($showfeedback) {
-            echo $OUTPUT->notification("$strdeleted course_meta");
-        }
-    } else {
-        if ($parents = $DB->get_records("course_meta", array("child_course"=>$course->id))) {
-            foreach ($parents as $parent) {
-                remove_from_metacourse($parent->parent_course,$parent->child_course); // this will do the unenrolments as well.
-            }
-            if ($showfeedback) {
-                echo $OUTPUT->notification("$strdeleted course_meta");
-            }
-        }
-    }
-
 /// Delete questions and question categories
     question_delete_course($course, $showfeedback);
 
@@ -4280,7 +4185,7 @@ function reset_course_userdata($data) {
     if (!empty($data->reset_roles_local)) {
         $children = get_child_contexts($context);
         foreach ($children as $child) {
-            role_unassign(0, 0, 0, $child->id);
+            role_unassign_all(array('contextid'=>$child->id));
         }
         //force refresh for logged in users
         mark_context_dirty($context->path);
@@ -4289,20 +4194,40 @@ function reset_course_userdata($data) {
 
     // First unenrol users - this cleans some of related user data too, such as forum subscriptions, tracking, etc.
     $data->unenrolled = array();
-    if (!empty($data->reset_roles)) {
-        foreach($data->reset_roles as $roleid) {
-            if ($users = get_role_users($roleid, $context, false, 'u.id', 'u.id ASC')) {
-                foreach ($users as $user) {
-                    role_unassign($roleid, $user->id, 0, $context->id);
-                    if (!is_enrolled($context, $user->id)) {
-                        $data->unenrolled[$user->id] = $user->id;
-                    }
+    if (!empty($data->unenrol_users)) {
+        $plugins = enrol_get_plugins(true);
+        $instances = enrol_get_instances($data->courseid, true);
+        foreach ($instances as $key=>$instance) {
+            if (!isset($plugins[$instance->enrol])) {
+                unset($instances[$key]);
+                continue;
+            }
+            if (!$plugins[$instance->enrol]->allow_unenrol($instance)) {
+                unset($instances[$key]);
+            }
+        }
+
+        $sqlempty = $DB->sql_empty();
+        foreach($data->unenrol_users as $withroleid) {
+            $sql = "SELECT DISTINCT ue.userid, ue.enrolid
+                      FROM {user_enrolments} ue
+                      JOIN {enrol} e ON (e.id = ue.enrolid AND e.courseid = :courseid)
+                      JOIN {context} c ON (c.contextlevel = :courselevel AND c.instanceid = e.courseid)
+                      JOIN {role_assignments} ra ON (ra.contextid = c.id AND ra.roleid = :roleid AND ra.userid = ue.userid)";
+            $params = array('courseid'=>$data->courseid, 'roleid'=>$withroleid, 'courselevel'=>CONTEXT_COURSE);
+
+            $rs = $DB->get_recordset_sql($sql, $params);
+            foreach ($rs as $ue) {
+                if (!isset($instances[$ue->enrolid])) {
+                    continue;
                 }
+                $plugins[$instances[$ue->enrolid]->enrol]->unenrol_user($instances[$ue->enrolid], $ue->userid);
+                $data->unenrolled[$ue->userid] = $ue->userid;
             }
         }
     }
     if (!empty($data->unenrolled)) {
-        $status[] = array('component'=>$componentstr, 'item'=>get_string('unenrol').' ('.count($data->unenrolled).')', 'error'=>false);
+        $status[] = array('component'=>$componentstr, 'item'=>get_string('unenrol', 'enrol').' ('.count($data->unenrolled).')', 'error'=>false);
     }
 
 
@@ -5048,58 +4973,6 @@ function email_is_not_allowed($email) {
     return false;
 }
 
-/**
- * Send welcome email to specified user
- *
- * @global object
- * @global object
- * @param object $course
- * @param user $user A {@link $USER} object
- * @return bool
- */
-function email_welcome_message_to_user($course, $user=NULL) {
-    global $CFG, $USER;
-
-    if (isset($CFG->sendcoursewelcomemessage) and !$CFG->sendcoursewelcomemessage) {
-        return;
-    }
-
-    if (empty($user)) {
-        if (!isloggedin()) {
-            return false;
-        }
-        $user = $USER;
-    }
-
-    if (!empty($course->welcomemessage)) {
-        $message = $course->welcomemessage;
-    } else {
-        $a = new object();
-        $a->coursename = $course->fullname;
-        if ($course->id == SITEID) {
-            $a->profileurl = "$CFG->wwwroot/user/profile.php?id=$user->id";
-        } else {
-            $a->profileurl = "$CFG->wwwroot/user/view.php?id=$user->id&course=$course->id";
-        }
-        $message = get_string("welcometocoursetext", "", $a);
-    }
-
-    /// If you don't want a welcome message sent, then make the message string blank.
-    if (!empty($message)) {
-        $subject = get_string('welcometocourse', '', format_string($course->fullname));
-
-         $context = get_context_instance(CONTEXT_COURSE, $course->id);
-        // TODO: replace with $CFG->coursemanager test, 'moodle/course:update' is very wrong!!
-        if ($users = get_users_by_capability($context, 'moodle/course:update', 'u.*', 'u.id ASC','', '', '', '', false, true)) {
-            $users = sort_by_roleassignment_authority($users, $context);
-            $teacher = array_shift($users);
-        } else {
-            $teacher = get_admin();
-        }
-        email_to_user($user, $teacher, $subject, $message);
-    }
-}
-
 /// FILE HANDLING  /////////////////////////////////////////////
 
 /**
@@ -7013,6 +6886,7 @@ function get_core_subsystems() {
             'dock'        => NULL,
             'editor'      => 'lib/editor',
             'edufields'   => NULL,
+            'enrol'       => 'enrol',
             'error'       => NULL,
             'filepicker'  => NULL,
             'filters'     => NULL,
@@ -9338,24 +9212,7 @@ function is_mnet_remote_user($user) {
 }
 
 /**
- * Checks if a given plugin is in the list of enabled enrolment plugins.
- *
- * @global object
- * @param string $auth Enrolment plugin.
- * @return boolean Whether the plugin is enabled.
- */
-function is_enabled_enrol($enrol='') {
-    global $CFG;
-
-    // use the global default if not specified
-    if ($enrol == '') {
-        $enrol = $CFG->enrol;
-    }
-    return in_array($enrol, explode(',', $CFG->enrol_plugins_enabled));
-}
-
-/**
- * This function will search for browser preferred languages, setting Moodle
+ * This function will search for browser prefereed languages, setting Moodle
  * to use the best one available if $SESSION->lang is undefined
  *
  * @global object
@@ -9569,7 +9426,7 @@ function is_primary_admin($userid){
     }
 }
 
- /**
+/**
  * Returns the site identifier
  *
  * @global object
diff --git a/lib/navigationlib.php b/lib/navigationlib.php
index fceab8eee9d7..09549e848efb 100644
--- a/lib/navigationlib.php
+++ b/lib/navigationlib.php
@@ -26,11 +26,6 @@
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
-if (!function_exists('get_all_sections')) {
-    /** Include course lib for its functions */
-    require_once($CFG->dirroot.'/course/lib.php');
-}
-
 /**
  * The name that will be used to separate the navigation cache within SESSION
  */
@@ -933,7 +928,7 @@ public function initialise() {
             $CFG->navshowcategories = false;
         }
 
-        $this->mycourses = get_my_courses($USER->id, 'visible DESC,sortorder ASC', null, false, $limit);
+        $this->mycourses = enrol_get_my_courses(NULL, 'visible DESC,sortorder ASC', $limit);
         $showallcourses = (count($this->mycourses) == 0 || !empty($CFG->navshowallcourses));
         $showcategories = ($showallcourses && !empty($CFG->navshowcategories));
 
@@ -1298,7 +1293,9 @@ protected function load_course_sections(stdClass $course, navigation_node $cours
      * @return array An array of course section nodes
      */
     public function load_generic_course_sections(stdClass $course, navigation_node $coursenode, $courseformat='unknown') {
-        global $DB, $USER;
+        global $CFG, $DB, $USER;
+
+        require_once($CFG->dirroot.'/course/lib.php');
 
         $modinfo = get_fast_modinfo($course);
         $sections = array_slice(get_all_sections($course->id), 0, $course->numsections+1, true);
@@ -1654,7 +1651,7 @@ public function get_extending_users() {
     public function add_course(stdClass $course, $forcegeneric = false) {
         global $CFG;
         $canviewhidden = has_capability('moodle/course:viewhiddencourses', $this->page->context);
-        if ($course->id !== SITEID && !$canviewhidden && (!$course->visible || !course_parent_visible($course))) {
+        if ($course->id !== SITEID && !$canviewhidden && !$course->visible) {
             return false;
         }
 
@@ -2574,12 +2571,8 @@ protected function load_course_settings($forceopen = false) {
 
         $course = $this->page->course;
         $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
-        if (!$this->cache->cached('canviewcourse'.$course->id)) {
-            $this->cache->{'canviewcourse'.$course->id} = has_capability('moodle/course:participate', $coursecontext);
-        }
-        if ($course->id === SITEID || !$this->cache->{'canviewcourse'.$course->id}) {
-            return false;
-        }
+
+        // note: do not test if enrolled or viewing here because we need the enrol link in Course administration section
 
         $coursenode = $this->add(get_string('courseadministration'), null, self::TYPE_COURSE, null, 'courseadmin');
         if ($forceopen) {
@@ -2614,25 +2607,13 @@ protected function load_course_settings($forceopen = false) {
             }
         }
 
-        if (has_capability('moodle/role:assign', $coursecontext)) {
-            // Add assign or override roles if allowed
-            $url = new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$coursecontext->id));
-            $coursenode->add(get_string('assignroles', 'role'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/roles', ''));
-            // Override roles
-            if (has_capability('moodle/role:review', $coursecontext) or count(get_overridable_roles($coursecontext))>0) {
-                $url = new moodle_url('/'.$CFG->admin.'/roles/permissions.php', array('contextid'=>$coursecontext->id));
-                $coursenode->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/permissions', ''));
-            }
-            // Check role permissions
-            if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:assign'), $coursecontext)) {
-                $url = new moodle_url('/'.$CFG->admin.'/roles/check.php', array('contextid'=>$coursecontext->id));
-                $coursenode->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/checkpermissions', ''));
-            }
-            // Manage filters
-            if (has_capability('moodle/filter:manage', $coursecontext) && count(filter_get_available_in_context($coursecontext))>0) {
-                $url = new moodle_url('/filter/manage.php', array('contextid'=>$coursecontext->id));
-                $coursenode->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/filter', ''));
-            }
+        // add enrol nodes
+        enrol_add_course_navigation($coursenode, $course);
+
+        // Manage filters
+        if (has_capability('moodle/filter:manage', $coursecontext) && count(filter_get_available_in_context($coursecontext))>0) {
+            $url = new moodle_url('/filter/manage.php', array('contextid'=>$coursecontext->id));
+            $coursenode->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/filter', ''));
         }
 
         // Add view grade report is permitted
@@ -2663,23 +2644,6 @@ protected function load_course_settings($forceopen = false) {
             $coursenode->add(get_string('outcomes', 'grades'), $url, self::TYPE_SETTING, null, 'outcomes', new pix_icon('i/outcomes', ''));
         }
 
-        // Add meta course links
-        if ($course->metacourse) {
-            if (has_capability('moodle/course:managemetacourse', $coursecontext)) {
-                $url = new moodle_url('/course/importstudents.php', array('id'=>$course->id));
-                $coursenode->add(get_string('childcourses'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/course', ''));
-            } else if (has_capability('moodle/role:assign', $coursecontext)) {
-                $roleassign = $coursenode->add(get_string('childcourses'), null,  self::TYPE_SETTING, null, null, new pix_icon('i/course', ''));
-                $roleassign->hidden = true;
-            }
-        }
-
-        // Manage groups in this course
-        if (($course->groupmode || !$course->groupmodeforce) && has_capability('moodle/course:managegroups', $coursecontext)) {
-            $url = new moodle_url('/group/index.php', array('id'=>$course->id));
-            $coursenode->add(get_string('groups'), $url, self::TYPE_SETTING, null, 'groups', new pix_icon('i/group', ''));
-        }
-
         // Backup this course
         if (has_capability('moodle/backup:backupcourse', $coursecontext)) {
             $url = new moodle_url('/backup/backup.php', array('id'=>$course->id));
@@ -2730,37 +2694,6 @@ protected function load_course_settings($forceopen = false) {
             $coursenode->add(get_string('files'), $url, self::TYPE_SETTING, null, 'coursefiles', new pix_icon('i/files', ''));
         }
 
-        // Authorize hooks
-        if ($course->enrol == 'authorize' || (empty($course->enrol) && $CFG->enrol == 'authorize')) {
-            require_once($CFG->dirroot.'/enrol/authorize/const.php');
-            $url = new moodle_url('/enrol/authorize/index.php', array('course'=>$course->id));
-            $coursenode->add(get_string('payments'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/payment', ''));
-            if (has_capability('enrol/authorize:managepayments', $this->page->context)) {
-                $cnt = $DB->count_records('enrol_authorize', array('status'=>AN_STATUS_AUTH, 'courseid'=>$course->id));
-                if ($cnt) {
-                    $url = new moodle_url('/enrol/authorize/index.php', array('course'=>$course->id,'status'=>AN_STATUS_AUTH));
-                    $coursenode->add(get_string('paymentpending', 'moodle', $cnt), $url, self::TYPE_SETTING, null, null, new pix_icon('i/payment', ''));
-                }
-            }
-        }
-
-        // Unenrol link
-        if (empty($course->metacourse) && ($course->id!==SITEID)) {
-            if (is_enrolled(get_context_instance(CONTEXT_COURSE, $course->id))) {
-                if (has_capability('moodle/role:unassignself', $this->page->context, NULL, false) and get_user_roles($this->page->context, $USER->id, false)) {  // Have some role
-                    $this->content->items[]='<a href="'.$CFG->wwwroot.'/course/unenrol.php?id='.$course->id.'">'.get_string('unenrolme', '', format_string($course->shortname)).'</a>';
-                    $this->content->icons[]='<img src="'.$OUTPUT->pix_url('i/user') . '" class="icon" alt="" />';
-                }
-
-            } else if (is_viewing(get_context_instance(CONTEXT_COURSE, $course->id))) {
-                // inspector, manager, etc. - do not show anything
-            } else {
-                // access because otherwise they would not get into this course at all
-                $this->content->items[]='<a href="'.$CFG->wwwroot.'/course/enrol.php?id='.$course->id.'">'.get_string('enrolme', '', format_string($course->shortname)).'</a>';
-                $this->content->icons[]='<img src="'.$OUTPUT->pix_url('i/user') . '" class="icon" alt="" />';
-            }
-        }
-
         // Switch roles
         $roles = array();
         $assumedrole = $this->in_alternative_role();
@@ -2802,6 +2735,10 @@ protected function load_course_settings($forceopen = false) {
      * @param stdClass $course
      */
     protected function add_course_editing_links($course) {
+        global $CFG;
+
+        require_once($CFG->dirroot.'/course/lib.php');
+
         // Add `add` resources|activities branches
         $structurefile = $CFG->dirroot.'/course/format/'.$course->format.'/lib.php';
         if (file_exists($structurefile)) {
@@ -3065,12 +3002,12 @@ protected function generate_user_settings($courseid, $userid, $gstitle='usercurr
             // Check that the user can view the profile
             $usercontext = get_context_instance(CONTEXT_USER, $user->id);       // User context
             if ($course->id==SITEID) {
-                if ($CFG->forceloginforprofiles && !!has_coursemanager_role($user->id) && !has_capability('moodle/user:viewdetails', $usercontext)) {  // Reduce possibility of "browsing" userbase at site level
+                if ($CFG->forceloginforprofiles && !!has_coursecontact_role($user->id) && !has_capability('moodle/user:viewdetails', $usercontext)) {  // Reduce possibility of "browsing" userbase at site level
                     // Teachers can browse and be browsed at site level. If not forceloginforprofiles, allow access (bug #4366)
                     return false;
                 }
             } else {
-                if ((!has_capability('moodle/user:viewdetails', $coursecontext) && !has_capability('moodle/user:viewdetails', $usercontext)) || !has_capability('moodle/course:participate', $coursecontext, $user->id, false)) {
+                if ((!has_capability('moodle/user:viewdetails', $coursecontext) && !has_capability('moodle/user:viewdetails', $usercontext)) || !is_enrolled($coursecontext, $user->id)) {
                     return false;
                 }
                 if (groups_get_course_groupmode($course) == SEPARATEGROUPS && !has_capability('moodle/site:accessallgroups', $coursecontext)) {
@@ -3356,32 +3293,13 @@ protected function load_front_page_settings($forceopen = false) {
             $frontpage->add(get_string('settings'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/settings', ''));
         }
 
-        //Participants
-        if (has_capability('moodle/site:viewparticipants', $coursecontext)) {
-            $url = new moodle_url('/user/index.php', array('contextid'=>$coursecontext->id));
-            $frontpage->add(get_string('participants'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/users', ''));
-        }
-
-        // Roles
-        if (has_capability('moodle/role:assign', $coursecontext)) {
-            // Add assign or override roles if allowed
-            $url = new moodle_url('/'.$CFG->admin.'/roles/assign.php', array('contextid'=>$coursecontext->id));
-            $frontpage->add(get_string('assignroles', 'role'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/roles', ''));
-            // Override roles
-            if (has_capability('moodle/role:review', $coursecontext) or count(get_overridable_roles($coursecontext))>0) {
-                $url = new moodle_url('/'.$CFG->admin.'/roles/permissions.php', array('contextid'=>$coursecontext->id));
-                $frontpage->add(get_string('permissions', 'role'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/permissions', ''));
-            }
-            // Check role permissions
-            if (has_any_capability(array('moodle/role:assign', 'moodle/role:safeoverride','moodle/role:override', 'moodle/role:assign'), $coursecontext)) {
-                $url = new moodle_url('/'.$CFG->admin.'/roles/check.php', array('contextid'=>$coursecontext->id));
-                $frontpage->add(get_string('checkpermissions', 'role'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/checkpermissions', ''));
-            }
-            // Manage filters
-            if (has_capability('moodle/filter:manage', $coursecontext) && count(filter_get_available_in_context($coursecontext))>0) {
-                $url = new moodle_url('/filter/manage.php', array('contextid'=>$coursecontext->id));
-                $frontpage->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/filter', ''));
-            }
+        // add enrol nodes
+        enrol_add_course_navigation($frontpage, $course);
+
+        // Manage filters
+        if (has_capability('moodle/filter:manage', $coursecontext) && count(filter_get_available_in_context($coursecontext))>0) {
+            $url = new moodle_url('/filter/manage.php', array('contextid'=>$coursecontext->id));
+            $frontpage->add(get_string('filters', 'admin'), $url, self::TYPE_SETTING, null, null, new pix_icon('i/filter', ''));
         }
 
         // Backup this course
diff --git a/lib/outputrequirementslib.php b/lib/outputrequirementslib.php
index 49c9873ff8ce..0dd9942c0f8d 100644
--- a/lib/outputrequirementslib.php
+++ b/lib/outputrequirementslib.php
@@ -378,7 +378,7 @@ protected function find_module($component) {
                                                         array('saving', 'repository'), array('search', 'repository'), array('searching', 'repository'), array('size', 'repository'),
                                                         array('submit', 'repository'), array('sync', 'repository'), array('title', 'repository'), array('upload', 'repository'),
                                                         array('uploading', 'repository'), array('xhtmlerror', 'repository'),
-                                                        array('xhtml', 'quiz'), array('cancel'), array('chooselicense', 'repository'), array('author', 'repository'),
+                                                        array('xhtml', 'quiz'), array('chooselicense', 'repository'), array('author', 'repository'),
                                                         array('ok', 'moodle'), array('error', 'moodle'), array('info', 'moodle')
                                                     ));
                     break;
@@ -702,9 +702,9 @@ public function string_for_js($identifier, $component, $a = NULL) {
         if (!$component) {
             throw new coding_exception('The $module parameter is required for page_requirements_manager::string_for_js.');
         }
-        if (isset($this->stringsforjs[$component][$identifier]) && $this->stringsforjs[$component][$identifier] != $string) {
+        if (isset($this->stringsforjs[$component][$identifier]) && $this->stringsforjs[$component][$identifier] !== $string) {
             throw new coding_exception("Attempt to re-define already required string '$identifier' " .
-                    "from lang file '$component'. Did you already ask for it with a different \$a?");
+                    "from lang file '$component'. Did you already ask for it with a different \$a? {$this->stringsforjs[$component][$identifier]} !== $string");
         }
         $this->stringsforjs[$component][$identifier] = $string;
     }
diff --git a/lib/sessionlib.php b/lib/sessionlib.php
index e6b87f08eb55..baa10e4da8f2 100644
--- a/lib/sessionlib.php
+++ b/lib/sessionlib.php
@@ -74,7 +74,7 @@ public function terminate_current();
      * @return void
      */
     public function write_close();
-    
+
     /**
      * Check for existing session with id $sid
      * @param unknown_type $sid
@@ -159,7 +159,7 @@ public function terminate_current() {
         } catch (Exception $ignored) {
             // probably install/upgrade - ignore this problem
         }
-        
+
         if (NO_MOODLE_COOKIES) {
             return;
         }
@@ -372,7 +372,7 @@ public function session_exists($sid){
         $sessionfile = clean_param("$CFG->dataroot/sessions/sess_$sid", PARAM_FILE);
         return file_exists($sessionfile);
     }
-    
+
 
 }
 
@@ -401,20 +401,20 @@ public function __construct() {
             }
         }
     }
-    
+
     public function session_exists($sid){
         global $CFG;
         try {
             $sql = "SELECT * FROM {sessions} WHERE timemodified < ? AND sid=? AND state=?";
             $params = array(time() + $CFG->sessiontimeout, $sid, 0);
-            
+
             return $this->database->record_exists_sql($sql, $params);
         } catch (dml_exception $ex) {
             error_log('Error checking existance of database session');
             return false;
         }
     }
-    
+
     protected function init_session_storage() {
         global $CFG;
 
@@ -878,7 +878,7 @@ function session_set_user($user) {
     unset($_SESSION['USER']->description); // conserve memory
     if (!isset($_SESSION['USER']->access)) {
         // check enrolments and load caps only once
-        check_enrolment_plugins($_SESSION['USER']);
+        enrol_check_plugins($_SESSION['USER']);
         load_all_capabilities();
     }
     sesskey(); // init session key
diff --git a/lib/setup.php b/lib/setup.php
index 5546ae305039..8d735d6c6176 100644
--- a/lib/setup.php
+++ b/lib/setup.php
@@ -330,6 +330,7 @@
 require_once($CFG->libdir .'/accesslib.php');       // Access control functions
 require_once($CFG->libdir .'/deprecatedlib.php');   // Deprecated functions included for backward compatibility
 require_once($CFG->libdir .'/moodlelib.php');       // Other general-purpose functions
+require_once($CFG->libdir .'/enrollib.php');        // Enrolment related functions
 require_once($CFG->libdir .'/pagelib.php');         // Library that defines the moodle_page class, used for $PAGE
 require_once($CFG->libdir .'/blocklib.php');        // Library for controlling blocks
 require_once($CFG->libdir .'/eventslib.php');       // Events functions
diff --git a/lib/setuplib.php b/lib/setuplib.php
index 34cb14d96c90..1bc9d3fdc119 100644
--- a/lib/setuplib.php
+++ b/lib/setuplib.php
@@ -95,6 +95,17 @@ function __construct($errorcode, $module='', $link='', $a=NULL, $debuginfo=null)
     }
 }
 
+/**
+ * Course/activity access exception.
+ *
+ * This exception is thrown from require_login()
+ */
+class require_login_exception extends moodle_exception {
+    function __construct($debuginfo) {
+        parent::__construct('requireloginerror', 'error', '', NULL, $debuginfo);
+    }
+}
+
 /**
  * Web service parameter exception class
  *
@@ -789,7 +800,7 @@ function get_real_size($size=0) {
  */
 function redirect_if_major_upgrade_required() {
     global $CFG;
-    $lastmajordbchanges = 2010052700;
+    $lastmajordbchanges = 2010061600;
     if (empty($CFG->version) or (int)$CFG->version < $lastmajordbchanges or
             during_initial_install() or !empty($CFG->adminsetuppending)) {
         try {
diff --git a/lib/simpletest/broken_testfilelib.php b/lib/simpletest/broken_testfilelib.php
index d0361c0fdadd..ea25b91cd543 100644
--- a/lib/simpletest/broken_testfilelib.php
+++ b/lib/simpletest/broken_testfilelib.php
@@ -135,7 +135,7 @@ public function setup() {
         $this->user->id = create_user($this->user);
 
         // Assign user to course
-        // role_assign(5, $this->user->id, 0, get_context_instance(CONTEXT_COURSE, $this->course->id)->id);
+        // role_assign(5, $this->user->id, get_context_instance(CONTEXT_COURSE, $this->course->id)->id);
 
         // Create a module
         $module = new stdClass();
diff --git a/lib/simpletest/testaccesslib.php b/lib/simpletest/testaccesslib.php
index ab943274930a..eacfc0dce201 100644
--- a/lib/simpletest/testaccesslib.php
+++ b/lib/simpletest/testaccesslib.php
@@ -261,10 +261,6 @@ function test_get_switchable_roles() {
         $context = $contexts['cou'];
         $context->id = SYSCONTEXTID + 2;
 
-        $this->load_test_data('capabilities',
-                array('name'), array(
-                array('moodle/course:participate')));
-
         $roles = $this->load_test_data('role',
                    array( 'name', 'shortname', 'description', 'sortorder'), array(
         'admin' => array('admin',     'admin',    'not null',          1),
@@ -274,13 +270,7 @@ function test_get_switchable_roles() {
         $adminid = $roles['admin']->id;
         $r1id = $roles['r1']->id;
         $r2id = $roles['r2']->id;
-        $funnyid = $roles['funny']->id; // strange role to test that roles with 'moodle/course:participate' are not returned.
-
-        $this->load_test_data('role_capabilities',
-                array('roleid',             'capability', 'contextid', 'permission'), array(
-                array(   $r1id,     'moodle/course:participate', SYSCONTEXTID + 1, CAP_ALLOW),
-                array(   $r2id,     'moodle/course:participate', SYSCONTEXTID, CAP_ALLOW),
-                array($funnyid,     'moodle/course:participate', SYSCONTEXTID, CAP_ALLOW)));
+        $funnyid = $roles['funny']->id; // strange role
 
         $this->load_test_data('role_assignments',
                 array('userid', 'contextid',   'roleid'), array(
@@ -295,13 +285,6 @@ function test_get_switchable_roles() {
                 array(  $r2id ,        $r2id),
                 array(  $r2id ,     $funnyid)));
 
-        // Admin should be able to switch to any role with 'moodle/course:participate' in any context.
-        $this->switch_global_user_id(1);
-        accesslib_clear_all_caches_for_unit_testing();
-        $this->assert(new ArraysHaveSameValuesExpectation(array($r2id)), array_keys(get_switchable_roles($syscontext)));
-        $this->assert(new ArraysHaveSameValuesExpectation(array($r2id)), array_keys(get_switchable_roles($context)));
-        $this->revert_global_user_id();
-
         // r1 should be able to switch to r2, but this user only has r1 in $context, not $syscontext.
         $this->switch_global_user_id(2);
         accesslib_clear_all_caches_for_unit_testing();
@@ -316,21 +299,5 @@ function test_get_switchable_roles() {
         $this->assert(new ArraysHaveSameValuesExpectation(array($r2id)), array_keys(get_switchable_roles($context)));
     }
 
-    function test_get_allowed_switchable_roles() {
-        $this->create_test_table('role_capabilities', 'lib');
-
-        $this->load_test_data('role_capabilities',
-                array('roleid',            'capability', 'contextid', 'permission'), array(
-                array(      1, 'moodle/forum:replypost', SYSCONTEXTID, CAP_ALLOW),
-                array(      2,     'moodle/course:participate', SYSCONTEXTID, CAP_ALLOW),
-                array(      4,     'moodle/course:participate', SYSCONTEXTID, CAP_ALLOW),
-                array(      5,     'moodle/course:participate', SYSCONTEXTID, CAP_ALLOW),
-                array(      6,     'moodle/course:participate', SYSCONTEXTID, CAP_PREVENT),
-                ));
-
-        $this->switch_to_test_db();
-
-        $this->assert(new ArraysHaveSameValuesExpectation(array(2, 5)), array_keys(get_allowed_switchable_roles()));
-    }
 }
 
diff --git a/lib/upgradelib.php b/lib/upgradelib.php
index a44bb93b49fb..d388203ab195 100644
--- a/lib/upgradelib.php
+++ b/lib/upgradelib.php
@@ -1191,11 +1191,11 @@ function install_core($version, $verbose) {
         // store version
         upgrade_main_savepoint(true, $version, false);
 
-        // Continue with the instalation
+        // Continue with the installation
         events_update_definition('moodle');
         message_update_providers('moodle');
 
-        // Write default settings unconditionlly
+        // Write default settings unconditionally
         admin_apply_default_settings(NULL, true);
 
         print_upgrade_part_end(null, true, $verbose);
@@ -1253,6 +1253,8 @@ function upgrade_core($version, $verbose) {
         // Reset caches again, just to be sure
         upgrade_reset_caches();
         remove_dir($CFG->dataroot . '/cache', true); // flush cache
+        $syscontext = get_context_instance(CONTEXT_SYSTEM);
+        mark_context_dirty($syscontext->path);
 
         print_upgrade_part_end('moodle', false, $verbose);
     } catch (Exception $ex) {
diff --git a/message/lib.php b/message/lib.php
index 20df4e2d1647..267500bf497e 100644
--- a/message/lib.php
+++ b/message/lib.php
@@ -319,7 +319,7 @@ function message_print_search_results($frm) {
 
         if (optional_param('mycourses', 0, PARAM_BOOL)) {
             $users = array();
-            $mycourses = get_my_courses($USER->id);
+            $mycourses = enrol_get_my_courses();
             foreach ($mycourses as $mycourse) {
                 if (is_array($susers = message_search_users($mycourse->id, $frm->name))) {
                     foreach ($susers as $suser) $users[$suser->id] = $suser;
diff --git a/mod/assignment/db/events.php b/mod/assignment/db/events.php
new file mode 100644
index 000000000000..7234b30e4305
--- /dev/null
+++ b/mod/assignment/db/events.php
@@ -0,0 +1,36 @@
+<?php
+
+///////////////////////////////////////////////////////////////////////////
+// Defines core event handlers                                           //
+///////////////////////////////////////////////////////////////////////////
+//                                                                       //
+// NOTICE OF COPYRIGHT                                                   //
+//                                                                       //
+// Moodle - Modular Object-Oriented Dynamic Learning Environment         //
+//          http://moodle.org                                            //
+//                                                                       //
+// Copyright (C) 1999 onwards  Martin Dougiamas  http://moodle.com       //
+//                                                                       //
+// This program 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.                                   //
+//                                                                       //
+// This program 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:                          //
+//                                                                       //
+//          http://www.gnu.org/copyleft/gpl.html                         //
+//                                                                       //
+///////////////////////////////////////////////////////////////////////////
+
+
+$handlers = array();
+
+/* List of events thrown from assignment module
+
+assignment_finalize_sent - object course, object user, object cm, object assignment, fileareaname
+assignment_file_sent     - object course, object user, object cm, object assignment, object file
+
+*/
\ No newline at end of file
diff --git a/mod/choice/view.php b/mod/choice/view.php
index 92c4b3a7d44e..110f91293808 100644
--- a/mod/choice/view.php
+++ b/mod/choice/view.php
@@ -142,7 +142,7 @@
             echo $OUTPUT->box_start('generalbox', 'notice');
             echo '<p align="center">'. get_string('noguestchoose', 'choice') .'</p>';
             echo $OUTPUT->container_start('continuebutton');
-            echo $OUTPUT->single_button(new moodle_url('/course/enrol.php?', array('id'=>$course->id)), get_string('enrolme', '', format_string($course->shortname)));
+            echo $OUTPUT->single_button(new moodle_url('/enrol/index.php?', array('id'=>$course->id)), get_string('enrolme', 'core_enrol', format_string($course->shortname)));
             echo $OUTPUT->container_end();
             echo $OUTPUT->box_end();
 
diff --git a/mod/feedback/show_entries.php b/mod/feedback/show_entries.php
index 9e4d74dab13c..220070a540a7 100644
--- a/mod/feedback/show_entries.php
+++ b/mod/feedback/show_entries.php
@@ -95,7 +95,7 @@
         } else {
             $groupmode = $course->groupmode;
         }
-        
+
         // $groupselect = groups_print_activity_menu($cm, $CFG->wwwroot . '/mod/feedback/show_entries.php?id=' . $cm->id.'&do_show=showentries', true);
         $groupselect = groups_print_activity_menu($cm, $url->out(), true);
         $mygroupid = groups_get_activity_group($cm);
@@ -103,15 +103,15 @@
         // preparing the table for output
         $baseurl = new moodle_url('/mod/feedback/show_entries.php');
         $baseurl->params(array('id'=>$id, 'do_show'=>$do_show, 'showall'=>$showall));
-        
+
         $tablecolumns = array('userpic', 'fullname', 'timemodified');
         $tableheaders = array(get_string('userpic'), get_string('fullnameuser'), get_string('date'));
-        
+
         if(has_capability('mod/feedback:deletesubmissions', $context)) {
             $tablecolumns[] = 'deleteentry';
             $tableheaders[] = '';
         }
-        
+
         $table = new flexible_table('feedback-showentry-list-'.$course->id);
 
         $table->define_columns($tablecolumns);
@@ -153,10 +153,10 @@
         }else {
             $usedgroupid = false;
         }
-        
+
         $matchcount = feedback_count_complete_users($cm, $usedgroupid);
         $table->initialbars(true);
-        
+
         if($showall) {
             $startpage = false;
             $pagecount = false;
@@ -165,7 +165,7 @@
             $startpage = $table->get_page_start();
             $pagecount = $table->get_page_size();
         }
-        
+
         $students = feedback_get_complete_users($cm, $usedgroupid, $where, $sort, $startpage, $pagecount);
 
         $completedFeedbackCount = feedback_get_completeds_group_count($feedback, $mygroupid);
@@ -193,7 +193,7 @@
         // echo '<table><tr><td width="400">';
         if (!$students) {
             if($courseid != SITEID){
-                echo $OUTPUT->notification(get_string('noexistingstudents'));
+                echo $OUTPUT->notification(get_string('noexistingparticipants', 'enrol'));
             }
         } else{
             echo print_string('non_anonymous_entries', 'feedback');
@@ -202,17 +202,17 @@
             foreach ($students as $student){
                 $completedCount = $DB->count_records('feedback_completed', array('userid'=>$student->id, 'feedback'=>$feedback->id, 'anonymous_response'=>FEEDBACK_ANONYMOUS_NO));
                 if($completedCount > 0) {
-                
+
                     //userpicture and link to the profilepage
                     $profilelink = '<strong><a href="'.$CFG->wwwroot.'/user/view.php?id='.$student->id.'&amp;course='.$course->id.'">'.fullname($student).'</a></strong>';
                     $data = array ($OUTPUT->user_picture($student, array('courseid'=>$course->id)), $profilelink);
-                    
+
                     //link to the entry of the user
                     $feedbackcompleted = $DB->get_record('feedback_completed', array('feedback'=>$feedback->id, ' userid'=>$student->id, 'anonymous_response'=>FEEDBACK_ANONYMOUS_NO));
                     $showentryurl = new moodle_url($url, array('userid'=>$student->id, 'do_show'=>'showoneentry'));
                     $showentrylink = '<a href="'.$showentryurl->out().'">'.UserDate($feedbackcompleted->timemodified).'</a>';
                     $data[] = $showentrylink;
-                    
+
                     //link to delete the entry
                     if(has_capability('mod/feedback:deletesubmissions', $context)) {
                         $deleteentryurl = new moodle_url($CFG->wwwroot.'/mod/feedback/delete_completed.php', array('id'=>$cm->id, 'completedid'=>$feedbackcompleted->id, 'do_show'=>'showoneentry'));
@@ -223,9 +223,9 @@
                 }
             }
             $table->print_html();
-            
+
             $allurl = new moodle_url($baseurl);
-            
+
             if ($showall) {
                 $allurl->param('showall', 0);
                 echo $OUTPUT->container(html_writer::link($allurl, get_string('showperpage', '', FEEDBACK_DEFAULT_PAGE_COUNT)), array(), 'showall');
@@ -265,13 +265,13 @@
     if(is_array($feedbackitems)){
         $align = right_to_left() ? 'right' : 'left';
         $usr = $DB->get_record('user', array('id'=>$userid));
-        
+
         if($feedbackcompleted) {
             echo $OUTPUT->heading(UserDate($feedbackcompleted->timemodified).' ('.fullname($usr).')', 3);
         } else {
             echo $OUTPUT->heading(get_string('not_completed_yet','feedback'), 3);
         }
-        
+
         echo $OUTPUT->box_start('feedback_items');
         $itemnr = 0;
         foreach($feedbackitems as $feedbackitem){
diff --git a/mod/feedback/show_nonrespondents.php b/mod/feedback/show_nonrespondents.php
index b90b412a21bc..2600208ca01a 100644
--- a/mod/feedback/show_nonrespondents.php
+++ b/mod/feedback/show_nonrespondents.php
@@ -46,7 +46,7 @@
     if($feedback->anonymous != FEEDBACK_ANONYMOUS_NO OR $feedback->course == SITEID) {
         print_error('error');
     }
-    
+
     $url = new moodle_url('/mod/feedback/show_nonrespondents.php', array('id'=>$cm->id));
 
     $PAGE->set_url($url);
@@ -54,7 +54,7 @@
     if (!$context = get_context_instance(CONTEXT_MODULE, $cm->id)) {
             print_error('badcontext');
     }
-    
+
     //we need the coursecontext to allow sending of mass mails
     if (!$coursecontext = get_context_instance(CONTEXT_COURSE, $course->id)) {
             print_error('badcontext');
@@ -125,22 +125,22 @@
     } else {
         $groupmode = $course->groupmode;
     }
-    
+
     $groupselect = groups_print_activity_menu($cm, $url->out(), true);
     $mygroupid = groups_get_activity_group($cm);
 
     // preparing the table for output
     $baseurl = new moodle_url('/mod/feedback/show_nonrespondents.php');
     $baseurl->params(array('id'=>$id, 'showall'=>$showall));
-    
+
     $tablecolumns = array('userpic', 'fullname', 'status');
     $tableheaders = array(get_string('userpic'), get_string('fullnameuser'), get_string('status'));
-    
+
     if(has_capability('moodle/course:bulkmessaging', $coursecontext)) {
         $tablecolumns[] = 'select';
         $tableheaders[] = get_string('select');
     }
-    
+
     $table = new flexible_table('feedback-shownonrespondents-'.$course->id);
 
     $table->define_columns($tablecolumns);
@@ -157,7 +157,7 @@
                 TABLE_VAR_ILAST   => 'silast',
                 TABLE_VAR_PAGE    => 'spage'
                 ));
-    
+
     $table->no_sorting('select');
     $table->no_sorting('status');
 
@@ -186,10 +186,10 @@
     }else {
         $usedgroupid = false;
     }
-    
+
     $matchcount = feedback_count_incomplete_users($cm, $usedgroupid);
     $table->initialbars(false);
-    
+
     if($showall) {
         $startpage = false;
         $pagecount = false;
@@ -198,7 +198,7 @@
         $startpage = $table->get_page_start();
         $pagecount = $table->get_page_size();
     }
-    
+
     $students = feedback_get_incomplete_users($cm, $usedgroupid, $sort, $startpage, $pagecount);
     //####### viewreports-start
     //print the list of students
@@ -208,7 +208,7 @@
     echo $OUTPUT->box_start('mdl-align');
     // echo '<table><tr><td width="400">';
     if (!$students) {
-        echo $OUTPUT->notification(get_string('noexistingstudents'));
+        echo $OUTPUT->notification(get_string('noexistingparticipants', 'enrol'));
     } else{
         echo print_string('non_respondents_students', 'feedback');
         echo ' ('.$matchcount.')<hr />';
@@ -221,13 +221,13 @@
             //userpicture and link to the profilepage
             $profilelink = '<strong><a href="'.$CFG->wwwroot.'/user/view.php?id='.$user->id.'&amp;course='.$course->id.'">'.fullname($user).'</a></strong>';
             $data = array ($OUTPUT->user_picture($user, array('courseid'=>$course->id)), $profilelink);
-            
+
             if($DB->record_exists('feedback_completedtmp', array('userid'=>$user->id))) {
                 $data[] = get_string('started', 'feedback');
             }else {
                 $data[] = get_string('not_started', 'feedback');
             }
-            
+
             //selections to bulk messaging
             if(has_capability('moodle/course:bulkmessaging', $coursecontext)) {
                 $data[] = '<input type="checkbox" class="usercheckbox" name="messageuser[]" value="'.$user->id.'" />';
@@ -235,9 +235,9 @@
             $table->add_data($data);
         }
         $table->print_html();
-        
+
         $allurl = new moodle_url($baseurl);
-        
+
         if ($showall) {
             $allurl->param('showall', 0);
             echo $OUTPUT->container(html_writer::link($allurl, get_string('showperpage', '', FEEDBACK_DEFAULT_PAGE_COUNT)), array(), 'showall');
@@ -246,7 +246,7 @@
             echo $OUTPUT->container(html_writer::link($allurl, get_string('showall', '', $matchcount)), array(), 'showall');
         }
         if(has_capability('moodle/course:bulkmessaging', $coursecontext)) {
-            $usehtmleditor = can_use_html_editor(); 
+            $usehtmleditor = can_use_html_editor();
             echo '<br /><div class="buttons">';
             echo '<input type="button" id="checkall" value="'.get_string('selectall').'" /> ';
             echo '<input type="button" id="checknone" value="'.get_string('deselectall').'" /> ';
diff --git a/mod/forum/db/events.php b/mod/forum/db/events.php
new file mode 100644
index 000000000000..1e98f7899375
--- /dev/null
+++ b/mod/forum/db/events.php
@@ -0,0 +1,39 @@
+<?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/>.
+
+/**
+ * Meta course enrolment plugin event handler definition.
+ *
+ * @package   enrol_meta
+ * @copyright 2010 Petr Skoda  {@link http://skodak.org}
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+/* List of handlers */
+$handlers = array (
+    'user_enrolled' => array (
+        'handlerfile'      => '/mod/forum/lib.php',
+        'handlerfunction'  => 'forum_user_enrolled',
+        'schedule'         => 'instant'
+    ),
+
+    'user_unenrolled' => array (
+        'handlerfile'      => '/mod/forum/lib.php',
+        'handlerfunction'  => 'forum_user_unenrolled',
+        'schedule'         => 'instant'
+    ),
+);
diff --git a/mod/forum/db/upgrade.php b/mod/forum/db/upgrade.php
index 0d4e178e2b86..1d0435a04eb4 100644
--- a/mod/forum/db/upgrade.php
+++ b/mod/forum/db/upgrade.php
@@ -56,34 +56,6 @@ function xmldb_forum_upgrade($oldversion) {
         upgrade_mod_savepoint($result, 2007101511, 'forum');
     }
 
-    if ($result && $oldversion < 2007101512) {
-
-    /// Cleanup the forum subscriptions
-        echo $OUTPUT->notification('Removing stale forum subscriptions', 'notifysuccess');
-
-        $roles = get_roles_with_capability('moodle/course:participate', CAP_ALLOW);
-        $roles = array_keys($roles);
-
-        list($usql, $params) = $DB->get_in_or_equal($roles);
-        $sql = "SELECT fs.userid, f.id AS forumid
-                  FROM {forum} f
-                       JOIN {course} c                 ON c.id = f.course
-                       JOIN {context} ctx              ON (ctx.instanceid = c.id AND ctx.contextlevel = ".CONTEXT_COURSE.")
-                       JOIN {forum_subscriptions} fs   ON fs.forum = f.id
-                       LEFT JOIN {role_assignments} ra ON (ra.contextid = ctx.id AND ra.userid = fs.userid AND ra.roleid $usql)
-                 WHERE ra.id IS NULL";
-
-        if ($rs = $DB->get_recordset_sql($sql, $params)) {
-            foreach ($rs as $remove) {
-                $DB->delete_records('forum_subscriptions', array('userid'=>$remove->userid, 'forum'=>$remove->forumid));
-                echo '.';
-            }
-            $rs->close();
-        }
-
-        upgrade_mod_savepoint($result, 2007101512, 'forum');
-    }
-
     if ($result && $oldversion < 2008072800) {
     /// Define field completiondiscussions to be added to forum
         $table = new xmldb_table('forum');
diff --git a/mod/forum/lib.php b/mod/forum/lib.php
index 43e4f7254786..0e66475cbf21 100644
--- a/mod/forum/lib.php
+++ b/mod/forum/lib.php
@@ -1428,10 +1428,10 @@ function forum_print_recent_activity($course, $viewfullnames, $timestart) {
  */
 function forum_get_user_grades($forum, $userid=0) {
     global $CFG;
-    
+
     require_once($CFG->dirroot.'/rating/lib.php');
     $rm = new rating_manager();
-    
+
     $ratingoptions = new stdclass();
 
     //need these to work backwards to get a context id. Is there a better way to get contextid from a module instance?
@@ -1800,9 +1800,8 @@ function forum_get_readable_forums($userid, $courseid=0) {
         $courses = $DB->get_records('course', array('id' => $courseid));
     } else {
         // If no course is specified, then the user can see SITE + his courses.
-        // And admins can see all courses, so pass the $doanything flag enabled
         $courses1 = $DB->get_records('course', array('id' => SITEID));
-        $courses2 = get_my_courses($userid, null, null, true);
+        $courses2 = enrol_get_users_courses($userid, true);
         $courses = array_merge($courses1, $courses2);
     }
     if (!$courses) {
@@ -5654,34 +5653,29 @@ function forum_update_subscriptions_button($courseid, $forumid) {
 }
 
 /**
- * This function gets run whenever a role is assigned to a user in a context
+ * This function gets run whenever user is enrolled into course
  *
- * @param integer $userid
- * @param object $context
- * @param itn $roleid
- * @return bool
+ * @param object $cp
+ * @return void
  */
-function forum_role_assign($userid, $context, $roleid) {
-    return forum_add_user_default_subscriptions($userid, $context);
+function forum_user_enrolled($cp) {
+    $context = get_context_instance(CONTEXT_COURSE, $cp->courseid);
+    forum_add_user_default_subscriptions($cp->userid, $context);
 }
 
 
 /**
- * This function gets run whenever a role is assigned to a user in a context
+ * This function gets run whenever user is unenrolled from course
  *
- * @param integer $userid
- * @param object $context
- * @return bool
+ * @param object $cp
+ * @return void
  */
-function forum_role_unassign($userid, $context) {
-    if (empty($context->contextlevel)) {
-        return false;
+function forum_user_unenrolled($cp) {
+    if ($cp->lastenrol) {
+        $context = get_context_instance(CONTEXT_COURSE, $cp->courseid);
+        forum_remove_user_subscriptions($cp->userid, $context);
+        forum_remove_user_tracking($cp->userid, $context);
     }
-
-    forum_remove_user_subscriptions($userid, $context);
-    forum_remove_user_tracking($userid, $context);
-
-    return true;
 }
 
 
@@ -5793,7 +5787,6 @@ function forum_remove_user_subscriptions($userid, $context) {
     switch ($context->contextlevel) {
 
         case CONTEXT_SYSTEM:   // For the whole site
-            //if ($courses = get_my_courses($userid)) {
             // find all courses in which this user has a forum subscription
             if ($courses = $DB->get_records_sql("SELECT c.id
                                                   FROM {course} c,
@@ -7003,7 +6996,7 @@ function forum_reset_userdata($data) {
     // remove all ratings in this course's forums
     if (!empty($data->reset_forum_ratings)) {
         $ratingdeloptions = new stdclass();
-        
+
         if ($forums) {
             foreach ($forums as $forumid=>$unused) {
                 if (!$cm = get_coursemodule_from_instance('forum', $forumid)) {
@@ -7373,7 +7366,7 @@ function forum_get_extra_capabilities() {
  */
 function forum_extend_navigation($navref, $course, $module, $cm) {
     global $CFG, $OUTPUT, $USER;
-    
+
     $limit = 5;
 
     $discussions = forum_get_discussions($cm,"d.timemodified DESC", false, -1, $limit);
diff --git a/mod/forum/post.php b/mod/forum/post.php
index 9d51acc95a28..8a54fbdd793e 100644
--- a/mod/forum/post.php
+++ b/mod/forum/post.php
@@ -111,7 +111,7 @@
             if (!is_enrolled($coursecontext)) {
                 $SESSION->wantsurl = $FULLME;
                 $SESSION->enrolcancel = $_SERVER['HTTP_REFERER'];
-                redirect($CFG->wwwroot.'/course/enrol.php?id='.$course->id, get_string('youneedtoenrol'));
+                redirect($CFG->wwwroot.'/enrol/index.php?id='.$course->id, get_string('youneedtoenrol'));
             }
         }
         print_error('nopostforum', 'forum');
@@ -178,7 +178,7 @@
             if (!is_enrolled($coursecontext)) {  // User is a guest here!
                 $SESSION->wantsurl = $FULLME;
                 $SESSION->enrolcancel = $_SERVER['HTTP_REFERER'];
-                redirect($CFG->wwwroot.'/course/enrol.php?id='.$course->id, get_string('youneedtoenrol'));
+                redirect($CFG->wwwroot.'/enrol/index.php?id='.$course->id, get_string('youneedtoenrol'));
             }
         }
         print_error('nopostforum', 'forum');
diff --git a/notes/index.php b/notes/index.php
index 7f78fb5cd24e..53fed7dcd215 100644
--- a/notes/index.php
+++ b/notes/index.php
@@ -118,7 +118,7 @@
     echo '<a name="coursenotes"></a>';
 
     if (!empty($userid)) {
-        $courses = get_my_courses($userid);
+        $courses = enrol_get_users_courses($userid);
         foreach($courses as $c) {
             $header = '<a href="' . $CFG->wwwroot . '/course/view.php?id=' . $c->id . '">' . $c->fullname . '</a>';
             if (has_capability('moodle/notes:manage', get_context_instance(CONTEXT_COURSE, $c->id))) {
diff --git a/pluginfile.php b/pluginfile.php
index 9ca895015fc9..243bc2ee4e73 100644
--- a/pluginfile.php
+++ b/pluginfile.php
@@ -220,7 +220,7 @@
             if ($USER->id !== $userid) {
                 $usercontext = get_context_instance(CONTEXT_USER, $userid);
                 // The browsing user is not the current user
-                if (!has_coursemanager_role($userid) && !has_capability('moodle/user:viewdetails', $usercontext)) {
+                if (!has_coursecontact_role($userid) && !has_capability('moodle/user:viewdetails', $usercontext)) {
                     send_file_not_found();
                 }
 
@@ -228,7 +228,7 @@
                 if (has_capability('moodle/user:viewdetails', $usercontext)) {
                     $canview = true;
                 } else {
-                    $courses = get_my_courses($USER->id);
+                    $courses = enrol_get_my_courses();
                 }
 
                 while (!$canview && count($courses) > 0) {
@@ -309,28 +309,13 @@
         session_get_instance()->write_close(); // unlock session during fileserving
         send_stored_file($file, 0, 0, true);
 
-    } else if ($filearea === 'course_intro') {
-        if ($CFG->forcelogin) {
-            require_login();
-        }
-
-        $relativepath = '/'.implode('/', $args);
-        $fullpath = $context->id.'course_intro0'.$relativepath;
-
-        if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
-            send_file_not_found();
-        }
-
-        session_get_instance()->write_close(); // unlock session during fileserving
-        send_stored_file($file, 60*60, 0, false); // TODO: change timeout?
-
     } else if ($filearea === 'course_summary') {
-
         if ($CFG->forcelogin) {
             require_login();
         }
 
-        $fullpath = $context->id.$filearea.implode('/', $args);
+        $relativepath = '/'.implode('/', $args);
+        $fullpath = $context->id.'course_summary0'.$relativepath;
 
         if (!$file = $fs->get_file_by_hash(sha1($fullpath)) or $file->is_directory()) {
             send_file_not_found();
@@ -455,7 +440,7 @@
                 print_error('noguest');
             }
 
-            if (!has_coursemanager_role($userid) and !has_capability('moodle/user:viewdetails', $usercontext)) {
+            if (!has_coursecontact_role($userid) and !has_capability('moodle/user:viewdetails', $usercontext)) {
                 print_error('usernotavailable');
             }
             if (!has_capability('moodle/user:viewdetails', $context) &&
diff --git a/question/type/randomsamatch/questiontype.php b/question/type/randomsamatch/questiontype.php
index b3caae8b8707..a774f9ba368c 100644
--- a/question/type/randomsamatch/questiontype.php
+++ b/question/type/randomsamatch/questiontype.php
@@ -97,7 +97,7 @@ function create_session_and_responses(&$question, &$state, $cmoptions, $attempt)
         $count  = count($saquestions);
         $wanted = $question->options->choose;
         $errorstr = '';
-        if ($count < $wanted && has_coursemanager_role()) { //TODO: this teacher test is far from optimal
+        if ($count < $wanted && has_coursecontact_role()) { //TODO: this teacher test is far from optimal
             if ($count >= 2) {
                 $errorstr =  "Error: could not get enough Short-Answer questions!
                  Got $count Short-Answer questions, but wanted $wanted.
diff --git a/repository/lib.php b/repository/lib.php
index 7ad07336f0fb..d12893a43c98 100644
--- a/repository/lib.php
+++ b/repository/lib.php
@@ -549,7 +549,7 @@ public static function check_context($ctx_id) {
         $level = $context->contextlevel;
 
         if ($level == CONTEXT_COURSE) {
-            if (!has_capability('moodle/course:participate', $context)) {
+            if (!is_enrolled($context)) { //TODO: this looks a bit too simple, verify!
                 return false;
             } else {
                 return true;
diff --git a/rss/file.php b/rss/file.php
index e9cae889454b..02640b74cc3c 100644
--- a/rss/file.php
+++ b/rss/file.php
@@ -86,29 +86,21 @@
     if (!in_array(strtolower($modulename), $mods)) {
         rss_not_found();
     }
-    //Get course_module to check it's visible
-    if (!$cm = get_coursemodule_from_instance($modulename,$instance)) {
+    try {
+        $cm = get_coursemodule_from_instance($modulename, $instance, 0, false, MUST_EXIST);
+        require_login($course, false, $cm, false, true);
+    } catch (Exception $e) {
         rss_not_found();
     }
-    $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
-    //will $modcontext always be the same object as $context?
-    $isuser = has_capability('moodle/course:participate', $modcontext);
-} else {
-    $isuser = has_capability('moodle/course:participate', $coursecontext);
-}
 
-//Check if course allows guests
-if ($course->id != SITEID) {
-    if ((!$course->guest || $course->password) && (!$isuser)) {
+} else {
+    try {
+        require_login($course, false, NULL, false, true);
+    } catch (Exception $e) {
         rss_not_found();
     }
 }
 
-//Check for "security" if the course is hidden or the activity is hidden
-if (!$isblog and (!$course->visible || !$cm->visible) && (!has_capability('moodle/course:viewhiddenactivities', $context))) {
-    rss_not_found();
-}
-
 $pathname = null;
 //Work out the filename of the cached RSS file
 if ($isblog) {
diff --git a/search/documents/label_document.php b/search/documents/label_document.php
index 24301bb0e667..86a0faaf94ee 100644
--- a/search/documents/label_document.php
+++ b/search/documents/label_document.php
@@ -165,7 +165,7 @@ function label_check_text_access($path, $itemtype, $this_id, $user, $group_id, $
     $course_context = get_context_instance(CONTEXT_COURSE, $r->course);
 
     //check if englobing course is visible
-    if (!has_capability('moodle/course:participate', $course_context)){
+    if (!is_enrolled($course_context) and !is_viewing($course_context)) {
         return false;
     }
 
diff --git a/search/documents/resource_document.php b/search/documents/resource_document.php
index dd5550e9c447..9f0f8ad3c4ed 100644
--- a/search/documents/resource_document.php
+++ b/search/documents/resource_document.php
@@ -326,7 +326,7 @@ function resource_check_text_access($path, $itemtype, $this_id, $user, $group_id
     }
 
     //check if user is registered in course or course is open to guests
-    if (!$course->guest && !has_capability('moodle/course:participate', $course_context)) {
+    if (!is_enrolled($course_context) and !is_viewing($course_context)) { //TODO: guest course access is gone, this needs a different solution
         return false;
     }
 
diff --git a/search/querylib.php b/search/querylib.php
index 8a3958322492..74ea57dd3e09 100644
--- a/search/querylib.php
+++ b/search/querylib.php
@@ -398,7 +398,7 @@ private function can_display(&$user, $this_id, $doctype, $course_id, $group_id,
       }
             
         // first check course compatibility against user : enrolled users to that course can see. 
-        $myCourses = get_my_courses($user->id);
+        $myCourses = enrol_get_users_courses($user->id, true);
         $unenroled = !in_array($course_id, array_keys($myCourses));
         
         // if guests are allowed, logged guest can see
diff --git a/tag/coursetagslib.php b/tag/coursetagslib.php
index c13300d0c53b..264e56032245 100644
--- a/tag/coursetagslib.php
+++ b/tag/coursetagslib.php
@@ -399,7 +399,8 @@ function coursetag_get_tagged_courses($tagid) {
             //view, but arguably it is best that when clicking on a tag, the tagged course summary should
             //be seen and then if the student clicks on that they will be given the opportunity to join
             //note courses not visible should not have their tagid sent to this function
-            //if (has_capability('moodle/course:participate', get_context_instance(CONTEXT_COURSE, $c->itemid))) {
+            // $context = get_context_instance(CONTEXT_COURSE, $c->itemid);
+            //if (is_enrolled($context) oe is_viewing($context)) {
                 $course = $DB->get_record('course', array('id'=>$c->itemid));
                 $courses[$c->itemid] = $course;
             //}
diff --git a/tag/locallib.php b/tag/locallib.php
index 6a6e82403321..c574c6ede5b9 100644
--- a/tag/locallib.php
+++ b/tag/locallib.php
@@ -322,7 +322,7 @@ function tag_print_user_box($user, $return=false) {
     $usercontext = get_context_instance(CONTEXT_USER, $user->id);
     $profilelink = '';
 
-    if ( has_capability('moodle/user:viewdetails', $usercontext) || has_coursemanager_role($user->id) ) {
+    if ( has_capability('moodle/user:viewdetails', $usercontext) || has_coursecontact_role($user->id) ) {
         $profilelink = $CFG->wwwroot .'/user/view.php?id='. $user->id;
     }
 
diff --git a/user/action_redir.php b/user/action_redir.php
index c64c8e1222ca..21359317a05c 100644
--- a/user/action_redir.php
+++ b/user/action_redir.php
@@ -33,8 +33,6 @@
 // Add every page will be redirected by this script
 $actions = array(
         'messageselect.php',
-        'extendenrol.php',
-        'groupextendenrol.php',
         'addnote.php',
         'groupaddnote.php',
         );
diff --git a/user/extendenrol.php b/user/extendenrol.php
deleted file mode 100644
index 9b384e286153..000000000000
--- a/user/extendenrol.php
+++ /dev/null
@@ -1,188 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file is part of the User section Moodle
- *
- * @copyright 1999 Martin Dougiamas  http://dougiamas.com
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @package user
- */
-
-require_once("../config.php");
-
-$id    = required_param('id', PARAM_INT);              // course id
-$users = optional_param('userid', array(), PARAM_INT); // array of user id
-
-$PAGE->set_url('/user/extendenrol.php', array('id'=>$id));
-
-if (! $course = $DB->get_record('course', array('id'=>$id))) {
-    print_error('invalidcourseid');
-}
-
-$context = get_context_instance(CONTEXT_COURSE, $id);
-require_login($course->id);
-
-// to extend enrolments current user needs to be able to do role assignments
-require_capability('moodle/role:assign', $context);
-$today = time();
-$today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
-if ((count($users) > 0) and ($form = data_submitted()) and confirm_sesskey()) {
-    if (count($form->userid) != count($form->extendperiod) || count($form->userid) != count($form->extendbase)) {
-        print_error('invalidformdata', '', $CFG->wwwroot.'/user/index.php?id='.$id);
-    }
-
-    foreach ($form->userid as $k => $v) {
-        // find all roles this student have in this course
-        if ($students = $DB->get_records_sql("SELECT ra.id, ra.roleid, ra.timestart, ra.timeend
-                                                FROM {role_assignments} ra
-                                               WHERE userid = ?
-                                                     AND contextid = ?", array($v, $context->id))) {
-            // enrol these users again, with time extension
-            // not that this is not necessarily a student role
-            foreach ($students as $student) {
-                // only extend if the user can make role assignments on this role
-                if (user_can_assign($context, $student->roleid)) {
-                    switch($form->extendperiod[$k]) {
-                        case 0: // No change
-                            break;
-                        case -1: // unlimited
-                            $student->timeend = 0;
-                            break;
-                        default: // extend
-                            switch($form->extendbase[$k]) {
-                                case 0: // course start date
-                                    $student->timeend = $course->startdate + $form->extendperiod[$k];
-                                    break;
-                                case 1: // student enrolment start date
-                                    // we check for student enrolment date because Moodle versions before 1.9 did not set this for
-                                    // unlimited enrolment courses, so it might be 0
-                                    if($student->timestart > 0) {
-                                        $student->timeend = $student->timestart + $form->extendperiod[$k];
-                                    }
-                                    break;
-                                case 2: // student enrolment start date
-                                    // enrolment end equals 0 means Unlimited, so adding some time to that will still yield Unlimited
-                                    if($student->timeend > 0) {
-                                        $student->timeend = $student->timeend + $form->extendperiod[$k];
-                                    }
-                                    break;
-                                case 3: // current date
-                                    $student->timeend = $today + $form->extendperiod[$k];
-                                    break;
-                                case 4: // course enrolment start date
-                                    if($course->enrolstartdate > 0) {
-                                        $student->timeend = $course->enrolstartdate + $form->extendperiod[$k];
-                                    }
-                                    break;
-                                case 5: // course enrolment end date
-                                    if($course->enrolenddate > 0) {
-                                        $student->timeend = $course->enrolenddate + $form->extendperiod[$k];
-                                    }
-                                    break;
-                            }
-                    }
-                    role_assign($student->roleid, $v, 0, $context->id, $student->timestart, $student->timeend, 0);
-                }
-            }
-        }
-    }
-
-    redirect("$CFG->wwwroot/user/index.php?id=$id", get_string('changessaved'));
-}
-
-/// Print headers
-
-$PAGE->navbar->add(get_string('extendenrol'));
-$PAGE->set_title("$course->shortname: ".get_string('extendenrol'));
-$PAGE->set_heading( $course->fullname);
-
-echo $OUTPUT->header();
-
-$timeformat = get_string('strftimedate');
-$unlimited = get_string('unlimited');
-$periodmenu[-1] = $unlimited;
-for ($i=1; $i<=365; $i++) {
-    $seconds = $i * 86400;
-    $periodmenu[$seconds] = get_string('numdays', '', $i);
-}
-
-// this will contain all available the based On select options, but we'll disable some on them on a per user basis
-$basemenu[0] = get_string('startdate') . ' (' . userdate($course->startdate, $timeformat) . ')';
-$basemenu[1] = get_string('enrolmentstart');
-$basemenu[2] = get_string('enrolmentend');
-if($course->enrollable != 2 || ($course->enrolstartdate == 0 || $course->enrolstartdate <= $today) && ($course->enrolenddate == 0 || $course->enrolenddate > $today)) {
-    $basemenu[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
-}
-if($course->enrollable == 2) {
-    if($course->enrolstartdate > 0) {
-        $basemenu[4] = get_string('courseenrolstartdate') . ' (' . userdate($course->enrolstartdate, $timeformat) . ')';
-    }
-    if($course->enrolenddate > 0) {
-        $basemenu[5] = get_string('courseenrolenddate') . ' (' . userdate($course->enrolenddate, $timeformat) . ')';
-    }
-}
-
-$title = get_string('extendenrol');
-echo $OUTPUT->heading($title . $OUTPUT->old_help_icon('extendenrol', $title));
-echo "<form method=\"post\" action=\"extendenrol.php\">\n";
-echo '<input type="hidden" name="id" value="'.$course->id.'" />';
-echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
-$table = new html_table();
-$table->head  = array (get_string('fullnameuser'), get_string('enrolmentstart'), get_string('enrolmentend'), get_string('extendperiod'), get_string('startingfrom'));
-$table->align = array ('left', 'center', 'center', 'center');
-$table->width = "600";
-$nochange = get_string('nochange');
-$notavailable = get_string('notavailable');
-foreach ($_POST as $k => $v) {
-    if (preg_match('/^user(\d+)$/',$k,$m)) {
-
-        if (!($user = $DB->get_record_sql("SELECT *
-                                             FROM {user} u
-                                             JOIN {role_assignments} ra ON u.id=ra.userid
-                                            WHERE u.id=? AND ra.contextid = ?", array($m[1], $context->id)))) {
-            continue;
-        }
-        $userbasemenu = $basemenu;
-        if ($user->timestart) {
-            $timestart = userdate($user->timestart, $timeformat);
-        } else {
-            $timestart = $notavailable;
-            unset($userbasemenu[1]);
-        }
-        if ($user->timeend) {
-            $timeend = userdate($user->timeend, $timeformat);
-        } else {
-            $timeend = $unlimited;
-            unset($userbasemenu[2]);
-        }
-
-        $checkbox = html_writer::select($periodmenu, "extendperiod[{$m[1]}]", "0", array('0'=>$nochange));
-        $checkbox2 = html_writer::select($userbasemenu, "extendbase[{$m[1]}]", "2", false);
-        $table->data[] = array(
-                fullname($user, true),
-                $timestart,
-                $timeend,
-                '<input type="hidden" name="userid['.$m[1].']" value="'.$m[1].'" />'.$checkbox,
-                $checkbox2);
-    }
-}
-echo html_writer::table($table);
-echo "\n<div style=\"width:100%;text-align:center;\"><input type=\"submit\" value=\"".get_string('savechanges')."\" /></div>\n</form>\n";
-
-echo $OUTPUT->footer();
-
diff --git a/user/groupextendenrol.php b/user/groupextendenrol.php
deleted file mode 100755
index 20073b128c0b..000000000000
--- a/user/groupextendenrol.php
+++ /dev/null
@@ -1,185 +0,0 @@
-<?php
-
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * This file is part of the User section Moodle
- *
- * @copyright 1999 Martin Dougiamas  http://dougiamas.com
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- * @package user
- */
-
-require_once("../config.php");
-
-$id    = required_param('id', PARAM_INT);              // course id
-$users = optional_param('userid', array(), PARAM_INT); // array of user id
-
-$PAGE->set_url('/user/groupextendenrol.php', array('id'=>$id));
-
-if (! $course = $DB->get_record('course', array('id'=>$id))) {
-    print_error('invalidcourseid');
-}
-
-$context = get_context_instance(CONTEXT_COURSE, $id);
-require_login($course->id);
-
-// to extend enrolments current user needs to be able to do role assignments
-require_capability('moodle/role:assign', $context);
-$today = time();
-$today = make_timestamp(date('Y', $today), date('m', $today), date('d', $today), 0, 0, 0);
-
-if ((count($users) > 0) and ($form = data_submitted()) and confirm_sesskey()) {
-
-    foreach ($form->userid as $k => $v) {
-        // find all roles this student have in this course
-        if ($students = $DB->get_records_sql("SELECT ra.id, ra.roleid, ra.timestart, ra.timeend
-                                                FROM {role_assignments} ra
-                                               WHERE userid = ?
-                                                     AND contextid = ?", array($v, $context->id))) {
-            // enrol these users again, with time extension
-            // not that this is not necessarily a student role
-            foreach ($students as $student) {
-                // only extend if the user can make role assignments on this role
-                if (user_can_assign($context, $student->roleid)) {
-                    switch($form->extendperiod) {
-                        case 0: // No change (currently this option is not available in dropdown list, but it might be ...)
-                            break;
-                        case -1: // unlimited
-                            $student->timeend = 0;
-                            break;
-                        default: // extend
-                            switch($form->extendbase) {
-                                case 0: // course start date
-                                    $student->timeend = $course->startdate + $form->extendperiod;
-                                    break;
-                                case 1: // student enrolment start date
-                                    // we check for student enrolment date because Moodle versions before 1.9 did not set this for
-                                    // unlimited enrolment courses, so it might be 0
-                                    if($student->timestart > 0) {
-                                        $student->timeend = $student->timestart + $form->extendperiod;
-                                    }
-                                    break;
-                                case 2: // student enrolment start date
-                                    // enrolment end equals 0 means Unlimited, so adding some time to that will still yield Unlimited
-                                    if($student->timeend > 0) {
-                                        $student->timeend = $student->timeend + $form->extendperiod;
-                                    }
-                                    break;
-                                case 3: // current date
-                                    $student->timeend = $today + $form->extendperiod;
-                                    break;
-                                case 4: // course enrolment start date
-                                    if($course->enrolstartdate > 0) {
-                                        $student->timeend = $course->enrolstartdate + $form->extendperiod;
-                                    }
-                                    break;
-                                case 5: // course enrolment end date
-                                    if($course->enrolenddate > 0) {
-                                        $student->timeend = $course->enrolenddate + $form->extendperiod;
-                                    }
-                                    break;
-                            }
-                    }
-                    role_assign($student->roleid, $v, 0, $context->id, $student->timestart, $student->timeend, 0);
-                }
-            }
-        }
-    }
-
-    redirect("$CFG->wwwroot/user/index.php?id=$id", get_string('changessaved'));
-}
-
-$PAGE->navbar->add(get_string('extendenrol'));
-$PAGE->set_title("$course->shortname: ".get_string('extendenrol'));
-$PAGE->set_heading($course->fullname);
-
-/// Print headers
-echo $OUTPUT->header();
-
-$timeformat = get_string('strftimedate');
-$unlimited = get_string('unlimited');
-$periodmenu[-1] = $unlimited;
-for ($i=1; $i<=365; $i++) {
-    $seconds = $i * 86400;
-    $periodmenu[$seconds] = get_string('numdays', '', $i);
-}
-
-$basemenu[0] = get_string('startdate') . ' (' . userdate($course->startdate, $timeformat) . ')';
-$basemenu[1] = get_string('enrolmentstart');
-$basemenu[2] = get_string('enrolmentend');
-if($course->enrollable != 2 || ($course->enrolstartdate == 0 || $course->enrolstartdate <= $today) && ($course->enrolenddate == 0 || $course->enrolenddate > $today)) {
-    $basemenu[3] = get_string('today') . ' (' . userdate($today, $timeformat) . ')' ;
-}
-if($course->enrollable == 2) {
-    if($course->enrolstartdate > 0) {
-        $basemenu[4] = get_string('courseenrolstartdate') . ' (' . userdate($course->enrolstartdate, $timeformat) . ')';
-    }
-    if($course->enrolenddate > 0) {
-        $basemenu[5] = get_string('courseenrolenddate') . ' (' . userdate($course->enrolenddate, $timeformat) . ')';
-    }
-}
-
-$title = get_string('groupextendenrol');
-echo $OUTPUT->heading($title . $OUTPUT->old_help_icon('groupextendenrol', $title));
-echo '<form method="post" action="groupextendenrol.php">';
-echo '<input type="hidden" name="id" value="'.$course->id.'" />';
-echo '<input type="hidden" name="sesskey" value="'.sesskey().'" />';
-$table = new html_table();
-$table->head  = array (get_string('fullnameuser'), get_string('enrolmentstart'), get_string('enrolmentend'));
-$table->align = array ('left', 'center', 'center', 'center');
-$table->width = "600";
-$nochange = get_string('nochange');
-$notavailable = get_string('notavailable');
-
-foreach ($_POST as $k => $v) {
-    if (preg_match('/^user(\d+)$/',$k,$m)) {
-
-        if (!($user = $DB->get_record_sql("SELECT *
-                                             FROM {user} u
-                                             JOIN {role_assignments} ra ON u.id=ra.userid
-                                    WHERE u.id=? AND ra.contextid = ?", array($m[1], $context->id)))) {
-            continue;
-        }
-        if ($user->timestart) {
-            $timestart = userdate($user->timestart, $timeformat);
-        } else {
-            $timestart = $notavailable;
-        }
-        if ($user->timeend) {
-            $timeend = userdate($user->timeend, $timeformat);
-        } else {
-            $timeend = $unlimited;
-        }
-        $table->data[] = array(
-        fullname($user, true),
-        $timestart,
-        $timeend . '<input type="hidden" name="userid['.$m[1].']" value="'.$m[1].'" />'
-        );
-    }
-}
-echo html_writer::table($table);
-echo '<div style="width:100%;text-align:center;"><strong>';
-echo get_string('extendperiod') . ' ';
-echo html_writer::select($periodmenu, 'extendperiod');
-echo ' ' . get_string('startingfrom') . ' ';
-echo html_writer::select($basemenu, 'extendbase', '2', false);
-echo '</strong><br />';
-echo '<input type="submit" value="'.get_string('savechanges').'" />';
-echo '</div></form>';
-
-echo $OUTPUT->footer();
-
diff --git a/user/index.php b/user/index.php
index be2162051235..c4eb43c97982 100644
--- a/user/index.php
+++ b/user/index.php
@@ -11,7 +11,6 @@
     define('SHOW_ALL_PAGE_SIZE', 5000);
     define('MODE_BRIEF', 0);
     define('MODE_USERDETAILS', 1);
-    define('MODE_ENROLDETAILS', 2);
 
     $page         = optional_param('page', 0, PARAM_INT);                     // which page to show
     $perpage      = optional_param('perpage', DEFAULT_PAGE_SIZE, PARAM_INT);  // how many per page
@@ -199,7 +198,7 @@
     $controlstable->data[] = new html_table_row();
 
 /// Print my course menus
-    if ($mycourses = get_my_courses($USER->id)) {
+    if ($mycourses = enrol_get_my_courses()) {
         $courselist = array();
         $popupurl = new moodle_url('/user/index.php?roleid='.$roleid.'&sifirst=&silast=');
         foreach ($mycourses as $mycourse) {
@@ -273,27 +272,8 @@
         }
     }
 
-    // Decide wheteher we will fetch extra enrolment/groups data.
-    //
-    // MODE_ENROLDETAILS is expensive, and only suitable where the listing is small
-    // (at or below DEFAULT_PAGE_SIZE) and $USER can enrol/unenrol
-    // (will take 1 extra DB query - 2 on Oracle)
-    //
-    if (!$isfrontpage && ($perpage <= DEFAULT_PAGE_SIZE) && has_capability('moodle/role:assign',$context)) {
-        $allowenroldetails = true;
-    } else {
-        $allowenroldetails = false;
-        if ($mode === MODE_ENROLDETAILS) {
-            // conditions haven't been met - reset
-            $mode = MODE_BRIEF;
-        }
-    }
-
     $formatmenu = array( '0' => get_string('brief'),
                          '1' => get_string('userdetails'));
-    if ($allowenroldetails) {
-        $formatmenu['2']= get_string('enroldetails');
-    }
     $select = new single_select($baseurl, 'mode', $formatmenu, $mode, null, 'formatmenu');
     $select->set_label(get_string('userlist'));
     $userlistcell = new html_table_cell();
@@ -350,22 +330,6 @@
         $tableheaders[] = get_string('lastaccess');
     }
 
-    if ($course->enrolperiod and $roleid) {
-        $tablecolumns[] = 'timeend';
-        $tableheaders[] = get_string('enrolmentend');
-    }
-
-    if ($mode === MODE_ENROLDETAILS) {
-        $tablecolumns[] = 'roles';
-        $tableheaders[] = get_string('roles');
-        if ($groupmode != 0) {
-            $tablecolumns[] = 'groups';
-            $tableheaders[] = get_string('groups');
-            $tablecolumns[] = 'groupings';
-            $tableheaders[] = get_string('groupings', 'group');
-        }
-    }
-
     if ($bulkoperations) {
         $tablecolumns[] = 'select';
         $tableheaders[] = get_string('select');
@@ -403,7 +367,7 @@
     // we are looking for all users with this role assigned in this context or higher
     $contextlist = get_related_contexts_string($context);
 
-    list($esql, $params) = get_enrolled_sql($context, NULL, $currentgroup, 'eu');
+    list($esql, $params) = get_enrolled_sql($context, NULL, $currentgroup);
     $joins = array("FROM {user} u");
     $wheres = array();
 
@@ -428,16 +392,6 @@
         if ($accesssince) {
             $wheres[] = get_course_lastaccess_sql($accesssince);
         }
-
-        if ($course->enrolperiod) {
-            // note: this is extremely tricky now, we do not know which ra assignment
-            //       is the one causing enrolment - better show it onl when filtering by roles
-
-            if ($roleid) {
-                $select .= ", (SELECT MAX(rax.timeend) FROM {role_assignments} rax WHERE rax.userid = u.id AND rax.contextid $contextlist AND rax.roleid = :raxroleid) AS timeend";
-                $params['raxroleid'] = $roleid;
-            }
-        }
     }
 
     // performance hacks - we preload user contexts together with accounts
@@ -495,17 +449,6 @@
     // list of users at the current visible page - paging makes it relatively short
     $userlist = $DB->get_recordset_sql("$select $from $where $sort", $params, $table->get_page_start(), $table->get_page_size());
 
-    //
-    // The SELECT behind get_participants_extra() is cheaper if we pass an array
-    // if IDs. We could pass the SELECT we did before (with the limit bits - tricky!)
-    // but this is much cheaper. And in any case, it is only doable with limited numbers
-    // of rows anyway. On a large course it will explode badly...
-    //
-    if ($mode === MODE_ENROLDETAILS) {
-        $userids = $DB->get_fieldset_sql("SELECT u.id $from $where", $params, $table->get_page_start(),  $table->get_page_size());
-        $userlist_extra = get_participants_extra($userids, $course, $context);
-    }
-
     /// If there are multiple Roles in the course, then show a drop down menu for switching
     if (count($rolenames) > 1) {
         echo '<div class="rolesform">';
@@ -709,10 +652,6 @@
                         $links[] = html_writer::link(new moodle_url('/course/user.php?id='. $course->id .'&user='. $user->id), get_string('activity'));
                     }
 
-                    if (has_capability('moodle/role:assign', $context) and get_user_roles($context, $user->id, false)) {  // I can unassign and user has some role
-                        $links[] = html_writer::link(new moodle_url('/course/unenrol.php?id='. $course->id .'&user='. $user->id), get_string('unenrol'));
-                    }
-
                     if ($USER->id != $user->id && !session_is_loggedinas() && has_capability('moodle/user:loginas', $context) && !is_siteadmin($user->id)) {
                         $links[] = html_writer::link(new moodle_url('/course/loginas.php?id='. $course->id .'&user='. $user->id .'&sesskey='. sesskey()), get_string('loginas'));
                     }
@@ -740,14 +679,6 @@
 
         if ($userlist)  {
 
-            // only show the plugin if multiple enrolment plugins
-            // are enabled...
-            if (strpos($CFG->enrol_plugins_enabled, ',')=== false) {
-                $showenrolplugin = true;
-            } else {
-                $showenrolplugin = false;
-            }
-
             $usersprinted = array();
             foreach ($userlist as $user) {
                 if (in_array($user->id, $usersprinted)) { /// Prevent duplicates by r.hidden - MDL-13935
@@ -794,13 +725,6 @@
                 if (!isset($hiddenfields['lastaccess'])) {
                     $data[] = $lastaccess;
                 }
-                if ($course->enrolperiod and $roleid) {
-                    if ($user->timeend) {
-                        $data[] = userdate($user->timeend, $timeformat);
-                    } else {
-                        $data[] = get_string('unlimited');
-                    }
-                }
 
                 if (isset($userlist_extra) && isset($userlist_extra[$user->id])) {
                     $ras = $userlist_extra[$user->id]['ra'];
@@ -814,11 +738,6 @@
                         } else {
                             $rastring .= $rolename;
                         }
-                        if ($showenrolplugin) {
-                            $rastring .= '<br />';
-                        } else {
-                            $rastring .= ' ('. $ra['enrolplugin'] .')<br />';
-                        }
                     }
                     $data[] = $rastring;
                     if ($groupmode != 0) {
@@ -851,11 +770,6 @@
             $displaylist['groupaddnote.php'] = get_string('groupaddnewnote', 'notes');
         }
 
-        if ($context->id != $frontpagectx->id) {
-            $displaylist['extendenrol.php'] = get_string('extendenrol');
-            $displaylist['groupextendenrol.php'] = get_string('groupextendenrol');
-        }
-
         echo $OUTPUT->old_help_icon("participantswithselectedusers", get_string("withselectedusers"));
         echo html_writer::tag('label', get_string("withselectedusers"), array('for'=>'formactionid'));
         echo html_writer::select($displaylist, 'formaction', '', array(''=>'choosedots'), array('id'=>'formactionid'));
@@ -947,7 +861,6 @@ function get_participants_extra ($userids, $course, $context) {
                    ctx.contextlevel AS ctxlevel, ctx.instanceid AS ctxinstanceid,
                    cc.name  AS ccname,
                    ra.roleid AS roleid,
-                   ra.enrol AS enrolplugin,
                    g.id     AS gid, g.name AS gname
                    $gpselect
               FROM {role_assignments} ra
@@ -981,11 +894,6 @@ function get_participants_extra ($userids, $course, $context) {
     //                                                       ctxname => 'name' (categories only)
     //                                                       ctxinstid =>
     //                                                       roleid => $roleid
-    //                                                       enrol => $pluginname
-    //
-    // Might be interesting to add to RA timestart, timeend, timemodified,
-    // and modifierid (with an outer join to mdl_user!
-    //
 
     foreach ($rs as $rec) {
         $userid = $rec->userid;
@@ -1009,8 +917,7 @@ function get_participants_extra ($userids, $course, $context) {
                                                   'ctxlevel'       => $rec->ctxlevel,
                                                   'ctxinstanceid' => $rec->ctxinstanceid,
                                                   'ccname'        => $rec->ccname,
-                                                  'roleid'        => $rec->roleid,
-                                                  'enrolplugin'   => $rec->enrolplugin);
+                                                  'roleid'        => $rec->roleid);
 
         }
     }
diff --git a/user/lib.php b/user/lib.php
index fe4c08c2b57e..f38be6ed3d0c 100644
--- a/user/lib.php
+++ b/user/lib.php
@@ -98,11 +98,14 @@ function user_delete_user($user) {
     //move unread messages from this user to read
     message_move_userfrom_unread2read($user->id);
 
+    // unconditionally unenrol from all courses
+    enrol_user_delete($user);
+
     // remove from all groups
     $DB->delete_records('groups_members', array('userid'=>$user->id));
 
     // unenrol from all roles in all contexts
-    role_unassign(0, $user->id); // this might be slow but it is really needed - modules might do some extra cleanup!
+    role_unassign_all(array('userid'=>$user->id)); // this might be slow but it is really needed - modules might do some extra cleanup!
 
     // now do a final accesslib cleanup - removes all role assingments in user context and context itself
     delete_context(CONTEXT_USER, $user->id);
diff --git a/user/profile.php b/user/profile.php
index c6d2ea2f1040..4c55951f067b 100644
--- a/user/profile.php
+++ b/user/profile.php
@@ -325,16 +325,17 @@
 
 
 if (!isset($hiddenfields['mycourses'])) {
-    if ($mycourses = get_my_courses($user->id, 'visible DESC,sortorder ASC', null, false, 21)) {
+    if ($mycourses = enrol_get_users_courses($user->id, true, NULL, 'visible DESC,sortorder ASC')) {
         $shown=0;
         $courselisting = '';
         foreach ($mycourses as $mycourse) {
             if ($mycourse->category) {
                 $class = '';
                 if ($mycourse->visible == 0) {
-                    // get_my_courses will filter courses $USER cannot see
-                    // if we get one with visible 0 it just means it's hidden
-                    // ... but not from $USER
+                    $ccontext = get_context_instance(CONTEXT_COURSE, $mycourse->id);
+                    if (!has_capability('moodle/course:viewhiddencourses', $ccontext)) {
+                        continue;
+                    }
                     $class = 'class="dimmed"';
                 }
                 $courselisting .= "<a href=\"{$CFG->wwwroot}/user/view.php?id={$user->id}&amp;course={$mycourse->id}\" $class >" . format_string($mycourse->fullname) . "</a>, ";
diff --git a/user/selector/lib.php b/user/selector/lib.php
index 9bcf7a660de0..e984d4fc361e 100644
--- a/user/selector/lib.php
+++ b/user/selector/lib.php
@@ -53,7 +53,7 @@ abstract class user_selector_base {
     protected $preserveselected = false;
     /** @var boolean If only one user matches the search, should we select them automatically. */
     protected $autoselectunique = false;
-    /** @var boolean When searching, do we only match the starts of fields (better performace)
+    /** @var boolean When searching, do we only match the starts of fields (better performance)
      * or do we match occurrences anywhere? */
     protected $searchanywhere = false;
     /** @var mixed This is used by get selected users */
@@ -62,7 +62,7 @@ abstract class user_selector_base {
     /**  @var boolean Used to ensure we only output the search options for one user selector on
      * each page. */
     private static $searchoptionsoutput = false;
-    
+
     /** @var array JavaScript YUI3 Module definition */
     protected static $jsmodule = array(
                 'name' => 'user_selector',
@@ -643,7 +643,7 @@ protected function initialise_javascript($search) {
     }
 }
 
-// User selectors for managing group memebers ==================================
+// User selectors for managing group members ==================================
 
 /**
  * Base class to avoid duplicating code.
@@ -821,8 +821,9 @@ public function find_users($search) {
                         AND $searchcondition";
         $orderby = "ORDER BY u.lastname, u.firstname";
 
-        $params = array_merge($searchparams, $roleparams, array('groupid'=>$this->groupid), $searchparams);
+        $params = array_merge($searchparams, $roleparams, $enrolparams);
         $params['courseid'] = $this->courseid;
+        $params['groupid']  = $this->groupid;
 
         if (!$this->is_validating()) {
             $potentialmemberscount = $DB->count_records_sql("SELECT COUNT(DISTINCT u.id) $sql", $params);
@@ -835,7 +836,7 @@ public function find_users($search) {
         $roles =  groups_calculate_role_people($rs, $context);
 
         //don't hold onto user IDs if we're doing validation
-        if( empty($this->validatinguserids) ) {
+        if (empty($this->validatinguserids) ) {
             if($roles) {
                 foreach($roles as $k=>$v) {
                     if($v) {
diff --git a/user/view.php b/user/view.php
index 3019c0218e44..648a4a3da71e 100644
--- a/user/view.php
+++ b/user/view.php
@@ -262,7 +262,7 @@
 
 // Show other courses they may be in
 if (!isset($hiddenfields['mycourses'])) {
-    if ($mycourses = get_my_courses($user->id, 'visible DESC,sortorder ASC', null, false, 21)) {
+    if ($mycourses = enrol_get_users_courses($user->id, true, NULL, 'visible DESC,sortorder ASC')) {
         $shown = 0;
         $courselisting = '';
         foreach ($mycourses as $mycourse) {
@@ -270,9 +270,10 @@
                 if ($mycourse->id != $course->id){
                     $class = '';
                     if ($mycourse->visible == 0) {
-                        // get_my_courses will filter courses $USER cannot see
-                        // if we get one with visible 0 it just means it's hidden
-                        // ... but not from $USER
+                        $ccontext = get_context_instance(CONTEXT_COURSE, $mycourse->id);
+                        if (!has_capability('moodle/course:viewhiddencourses', $ccontext)) {
+                            continue;
+                        }
                         $class = 'class="dimmed"';
                     }
                     $courselisting .= "<a href=\"{$CFG->wwwroot}/user/view.php?id={$user->id}&amp;course={$mycourse->id}\" $class >"
diff --git a/version.php b/version.php
index 0db522b7b328..7eed065a6209 100644
--- a/version.php
+++ b/version.php
@@ -6,7 +6,7 @@
 // This is compared against the values stored in the database to determine
 // whether upgrades should be performed (see lib/db/*.php)
 
-    $version = 2010060800.01;  // YYYYMMDD   = date of the last version bump
+    $version = 2010062100;  // YYYYMMDD   = date of the last version bump
                             //         XX = daily increments
 
     $release = '2.0 Preview 3 (Build: 20100621)';  // Human-friendly version name