Obsolete:Enrolment rewrite and role tweaks proposal

Jump to: navigation, search

Note: You are currently viewing documentation for Moodle 1.9. Up-to-date documentation for the latest stable version is available here: Enrolment rewrite and role tweaks proposal.

Main objectives

  • Redesign enrolment infrastructure (multiple instances, guest access, participation status, global groups, etc.).
  • Better performance and new improvement possibilities (SQL used again instead of functions returning arrays of users or courses).
  • Simplify some role and permission aspects.

There are very many ways how to solve all the enrolment and role issues. During 1.7dev phase we made some fundamental decisions before solving of expected enrolment problems. Later it was not possible to make any major design changes mostly due to time constrains during 1.8dev and 1.9dev release cycles. 2.0 is the first and possibly our last chance to solve all known problems.

This document describes a completely different enrolment infrastructure. At present the concept of roles handles most of the enrolment needs, unfortunately there are two major problems, first is performance because we can not use SQL any more to find out who is course participant, second is not enough flexibility needed for all enrolment plugins. Another indirectly related problem is the usability of permission overriding.

Known problems

  1. enrolment configuration is stored in course table – often we need to store different configuration for multiple plugin instances in one course
  2. role assignment table does not have enough room for all course enrolment/participation related data
  3. negative guest capability – capabilities must be always positive, allowing you to do something; problems on frontpage if guest account used as default role there
  4. not possible to temporarily suspend user enrolment or all users enrolled through one plugin
  5. unenrolment removes some user data and settings – it should remove everything or nothing
  6. usability regressions after migrating activity settings into capabilities
  7. very complex permission evaluation
  8. administrator role used for everything, confusing admin role overriding, admin lock-up incidents
  9. parental access not supported in require_login() and other parts
  10. global groups not implemented
  11. graded role in gradebook - role assignment used instead of capability and participation
  12. extremely slow stats processing on large sites
  13. meta courses are slow, lack of group support and often misunderstood
  14. cached role definitions not reused when checking permission of other users
  15. legacy capabilities and functions still used in many places

List of changes

Please note that majority of these changes can not be implemented separately because they are interdependent due to technical, performance or usability reasons.

New course_participants database table

Table course_participants stores users participating in courses (aka enroled users).

Field Type Default Description
id int(10) auto-incrementing
status int(10) 0 0 means active enrolment, anything else means inactive (user can not access course) and is defined by plugin
enrolid int(10) foreign key, references enrol.id
userid int(10) foreign key, references user.id
timestart int(10) 0 time stamp, start of active participation period
timeend int(10) 2147483647 time stamp, end of active participation period, max date used for performance reason
modifierid int(10) last user who modified enrolment, references user.id
timemodified int(10) timestamp, last modification

New enrol plugin instance table

Table enrol stores information about enrolment plugins used in course, includes plugin configuration data and status.

Field Type Default Description
id int(10) auto-incrementing
name char(255) optional plugin instance name, useful when multiple instances of the same plugin used in course
enrol char(20) enrolment plugin name
status int(10) 0 0 means active plugin, anything else means inactive (user can not access course) and is defined by plugin
courseid int(10) foreign key, references course.id
lockedroles int(1) 1 flag indicating role assignments added by this plugin can not be removed manually or by any other plugin
enrolperiod int(10) 0 optional default enrolment period, in seconds
enrolstartdate int(10) 0 timestamp, optional default enrolment start date
enrolenddate int(10) 0 timestamp, optional default enrolment end date
expirynotify int(1) 0 optional flag
expirythreshold int(10) 0 timestamp, optional
notifyall int(1) 0 optional flag
password char(255) optional enrolment password
cost char(20) optional cost field
currency char(3) optional currency field
roleid1 int(10) general optional field
roleid2 int(10) general optional field
customint1 int(10) general optional field
customint2 int(10) general optional field
customchar1 char(255) general optional field
customchar2 char(255) general optional field
customtext1 text general optional field
customtext2 text general optional field
timecreated int(10) timestamp, date when instance created
timemodified int(10) timestamp, date when instance last modified

Course table does not contain any enrolment related fields any more.

More enrolment plugins and new UIs

  • guest - Guest user access, temporary user guest access, optional password protection (no entry in course_participant table)
  • self - optional password, enrolment duration, role selection, group keys enabled, new self-enrolments allowed
  • manual - manual enrolments (usually done by teachers and admins that have "enrol/manual:enrol" capability)
  • globalgroups - maps global groups to course groups and enrols users from global groups, assigns roles to users in global groups
  • parent - allows parents to access courses children are participating in, similar to guest access, user is not real participant (no entry in course_participant table)

Enrol fieldsets.png

Screenshot above shows refactored course edit form. Each plugin usually has own field set. Enrol plugins should use the same UI as auth plugins both for general configuration and course presets.

New permission evaluation

Change evaluation algorithm, instead of aggregation caps from all roles in each step, calculate resulting capability for each role and use OR logical operation on the result. The prohibit would work the same way as before.

See Development:Role overrides revisited for more details and examples.

New simplified UI for permissions overview and overriding

New permissions.png


  • Should give overview of all permissions in context without going through several other pages.
  • Hopefully easier to understand for normal teachers.
  • Supports read only mode, safe and normal overrides.
  • All actions require confirmations, it is also possible to display extra explanation. For example using prohibit for Authenticated users. Extra warning before allowing caps with XSS risk.
  • Dangerous overrides in roles with student/guest/authenticated archetypes may be highlighted (major security improvement).

The current override page would be still used in advanced mode because it has more detailed information (especially when inheritance is involved).

New moodle/course:visit capability

User can access course (require_login() function) if there is an active participation record, if enrol/parent plugin grants temporary guest access or when user has moodle/course:visit capability.

The capability replaces often abused doanything capability or hidden role assignments with moodle/course:view capability.

Hidden role assignments not needed

The hidden role feature was introduced because we needed a way to hide managers and inspectors assigned above the course context on the course participation page and user profile.

Replace by combination of course participation and new course inspect capability. Instead of ignoring hidden role assignments we could just display role relevant to course participation (obtained from enrol plugin instances). New capability moodle/role:viewall would enable some users to see all role assignments including those granted above course context or user not participating in course. Another option is to manually select which roles are "public" in course participation and profile (new field in course table).

Questions and potential problems:

  • Are hidden role assignments used for any other purposes?
  • Are there any other places except participation page and user profile where we show roles of participants?

Legacy role concept replaced by role archetypes

Legacy capabilities were originally intended as a temporary solution for backwards compatibility with old code. Internally the code allows allowing multiple legacy caps in one role, it is prevented in UI now.

Later we realised that we need them for other purposes too:

  • new capabilities during upgrade
  • role resetting
  • admin settings

The word "legacy" is often confusing and a bit hard to explain, because ideally it should be used only for non-legacy purposes above.

The proposal here is to use word archetype instead of legacy and store the archetype flag directly in roles table. Benefits are better usability and it also helps fixing of legacy issues too.

Remove all legacy capability tests

Including isguest(), replaced by course participation concept and role archetypes. Major reason for keeping of legacy capability checks were the shortcomings in current enrolment/roles code, all of these should be finally solved.

Admin role restrictions

  • only one admin role allowed - all admin roles are the same because the have do anything
  • doanything capability allowed only in admin role - giving do anything in subcontexts or other roles is restricted in UI already
  • Admin role can not be deleted - there were reported many accidents
  • Admin role can not be overriden - do anything has to win, we need to prevent admin lockouts
  • Admin role can be assigned in system contex only (Manager role used in other contexts)
  • Admin role must be assigned to at least one user
  • We might also use admin role archetype instead of doanything capability


  • simplified code
  • better usability
  • no lock-up accidents

New Editor role partially replacing admin role

Admin role is not suitable for everyday tasks and teaching for the same reasons why we are not using root account everyday in Linux. Any successful XSS attack on admin results in full control including PHP code execution and shell execution. Ideally there should be a way to administer server without opening Moodle session in a browser.

user with Editor role may enter any course and manage it without actually participating in the course. Category admin accounts are currently used for this which is far from optimal.

New global groups tables, capabilities and enrol plugin

  • global groups separate from course groups and groupings
  • enrolment plugin handles synchronisation of global groups with course groups (synchronised later when global group membership changes)
  • global groups used in manual enrolment plugin when enroling groups of people (not synchronised later)
  • global groups used when assigning roles (not synchronised later)

Table globalgroups definition of global groups

Field Type Default Description
id int(10) auto-incrementing
contextid int(10) foreign key, references context.id
name char(255) name of global group
description text longer description
modifierid int(10) last user who modified membership, references user.id
timecreated int(10) timestamp, creation date
timemodified int(10) timestamp, last modification

Context specified can be system or course category level, defining of global groups at course context does not make much sense.

Table globalgroups_members stores global group members

Field Type Default Description
id int(10) auto-incrementing
globalgroupid int(10) foreign key, references globalgroups.id
userid int(10) foreign key, references user.id
modifierid int(10) who added membership, references user.id
timeadded int(10) timestamp, when added

New capabilities:

  • moodle/globalgroups:assign (add/delete members)
  • moodle/globalgroups:manage (add/delete/move global groups)
  • moodle/globalgroups:view (view membership and use global groups elsewhere)

Meta courses not needed

Meta courses are often used as a way to synchronise course enrolment in multiple courses or as a workaround for missing global groups. Internally metacourses try to replicate role assignments from one course into another course. If we decide to reimplement enrolments we would have to synchronise course participation too.

Current major problems are performance, missing groups support, minor bugs and usability. New global groups should theoretically solve all these problems.

Questions and potential problems:

  • Can global groups fully replace meta courses?
  • If not what are the use cases?

Role assignment time restrictions moved to course participation table and plugins

Enrolment plugins can now implement advanced role handling code either from cron or at run time. The enrolment duration flags solve course access issues (equivalent to time restricting of role with moodle/course:view capability). Other uses would be easily implemented in enrolment plugins.

For example enrolment plugin may assign read only role before the course starts, then switch to normal student role, after course end it may switch to read only role again and after one year forbid access to course completely by setting status flag in course_participant table. All this time user would be participant (would appear in gradebook, assignments, groups, etc.), but his/here could change and access to course could be limited in any way.

Potential problems:

  • Current roles system does not allow multiple assignment of the same role in the same context - this might be a problem for advanced enrolment plugins when manual enrolments used at the same time.

Change unique index in role_assignments

At present the index

 <INDEX NAME="contextid-roleid-userid" UNIQUE="true" FIELDS="contextid, roleid, userid" NEXT="sortorder"/>

collides with concept of multiple enrol plugins and role assignment locking in new enrol plugin instances, it should be more flexible if changed to:

 <INDEX NAME="contextid-roleid-userid-enrolid" UNIQUE="true" FIELDS="contextid, roleid, userid, enrolid" NEXT="sortorder"/>

New $USER->participating and $USER->visiting caching

These flags will be used in require_login() and may also be used elsewhere.

Move role definitions cache from $USER to $SESSION

The current caching is not efficient in cron script, we might also use this cache in other new functions. $SESSION is not affected when switching users in cron or other cli scripts.

It might be also interesting to find out if there are some better compression routines for rdef arrays, for example using capability id instead of name because we are loading the list of caps from db anyway.

New functions returning SQL instead of arrays of users and courses

 function get_participating_courses_sql($userid, $activeonly=false, array $fields=null, array $sorts=null, $prefix=, $extrajoin=null, $extrawhere=null, array $extraparams=null)
 function get_participants_sql($courseid, $groupid=null, $activeonly=false, array $fields=null, array $sorts=null, $prefix=, $withcapability=null, $context=null, $extrajoin=null, $extrawhere=null, array $extraparams=null)

Rewrite stats sql

Current emulated course participation joins are the most expensive part of stats processing, simple joins with course_participant will make it multiple times faster, more cross db compatible and also more accurate.

Use cases and examples

  1. Give students forum moderator rights
  2. Enable students to grade assignment submissions
  3. Give students the rights to approve database module entries
  4. Allow students to clean up saved chat sessions
  5. Create an archive forum
  6. Enable students to rate forum posts
  7. Allow students to unenrol themselves from a course
  8. Hide a block from guests
  9. Give students unlimited time to complete timed quizzes
  10. Enable students to create questions
  11. Assign a student the role of calendar editor
  12. Provide temporary read-only access to naughty students

See also: Roles_use_cases

Key areas that will benefit

My Moodle

My Moodle and "My courses" queries using course_participants will be greatly improved in speed, allowing us to push it more as a standard customisable portal-type interface for users.


See also