Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to add course category and global roles. #11

Open
wants to merge 13 commits into
base: main
Choose a base branch
from
Open
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.remote-sync.json
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
moodle-availability_role
========================

[![Moodle Plugin CI](https://github.com/moodle-an-hochschulen/moodle-availability_role/workflows/Moodle%20Plugin%20CI/badge.svg?branch=master)](https://github.com/moodle-an-hochschulen/moodle-availability_role/actions?query=workflow%3A%22Moodle+Plugin+CI%22+branch%3Amaster)
[![Moodle Plugin CI](https://github.com/moodle-an-hochschulen/moodle-availability_role/workflows/Moodle%20Plugin%20CI/badge.svg?branch=MOODLE_402_STABLE)](https://github.com/moodle-an-hochschulen/moodle-availability_role/actions?query=workflow%3A%22Moodle+Plugin+CI%22+branch%3AMOODLE_402_STABLE)

Moodle availability plugin which lets users restrict resources, activities and sections based on roles
Moodle availability plugin which lets users restrict resources, activities and sections based on roles.


Requirements
Expand All @@ -15,7 +15,7 @@ This plugin requires Moodle 4.3+
Motivation for this plugin
--------------------------

If your teachers want to restrict activities / resources / sections in their course to a subset of the course participants and these course participants share a common course role, this plugin is for you.
If your teachers want to restrict activities / resources / sections in their course to a subset of the course participants and these course participants share a common course role, a course category role or global role, this plugin is for you.

Have a look at an example:

Expand Down
88 changes: 61 additions & 27 deletions classes/condition.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@
class condition extends \core_availability\condition {
/** @var int ID of role that this condition requires */
protected $roleid = 0;
/** @var int ID of type that this condition requires */
protected $typeid = 0;
public const ROLETYPE_COURSE = 0;
public const ROLETYPE_COURSECAT = 1;
public const ROLETYPE_CORE = 2;

/**
* Constructor.
Expand All @@ -50,6 +55,11 @@ public function __construct($structure) {
} else {
throw new \coding_exception('Invalid ->id for role condition');
}
if (!empty($structure->typeid)) {
$this->typeid = $structure->typeid;
} else {
$this->typeid = self::ROLETYPE_COURSE;
}
}

/**
Expand All @@ -61,6 +71,7 @@ public function save() {
$result = (object)['type' => 'role'];
if ($this->roleid) {
$result->id = $this->roleid;
$result->typeid = $this->typeid;
} else {
$result->activity = true;
}
Expand Down Expand Up @@ -115,40 +126,63 @@ public function update_after_restore($restoreid, $courseid, \base_logger $logger
*/
public function is_available($not, \core_availability\info $info, $grabthelot, $userid) {
global $USER, $CFG;
$context = \context_course::instance($info->get_course()->id);
$allow = false;

// Is the user's course role switched?
if (!empty($USER->access['rsw'][$context->path])) {
// Check only switched role.
if ($USER->access['rsw'][$context->path] == $this->roleid) {
$allow = true;
}
// Or is the user currently having his own role(s)?
} else {
// Check all of the user's course roles.
foreach (get_user_roles($context, $userid) as $role) {
if ($role->roleid == $this->roleid) {
$allow = true;
break;
switch($this->typeid) {
case self::ROLETYPE_CORE:
$context = \context_system::instance();
foreach (get_user_roles($context, $userid) as $role) {
if ($role->roleid == $this->roleid) {
$allow = true;
break;
}
}
}
break;
case self::ROLETYPE_COURSECAT:
$context = \context_coursecat::instance($info->get_course()->category);
// Check all of the user's course roles.
foreach (get_user_roles($context, $userid) as $role) {
if ($role->roleid == $this->roleid) {
$allow = true;
break;
}
}
break;
default:
$context = \context_course::instance($info->get_course()->id);
// Is the user's course role switched?
if (!empty($USER->access['rsw'][$context->path])) {
// Check only switched role.
if ($USER->access['rsw'][$context->path] == $this->roleid) {
$allow = true;
}
// Or is the user currently having his own role(s)?
} else {
// Check all of the user's course roles.
foreach (get_user_roles($context, $userid) as $role) {
if ($role->roleid == $this->roleid) {
$allow = true;
break;
}
}
}
}

// As get_user_roles only returns roles for enrolled users, we have to check whether a user
// is viewing the course as guest or is not logged in separately.
// As get_user_roles only returns roles for enrolled users, we have to check whether a user
// is viewing the course as guest or is not logged in separately.

// Is the user not logged in?
if (empty($userid) || isguestuser($userid)) {
if ($CFG->notloggedinroleid == $this->roleid) {
$allow = true;
}
// Is the user not logged in?
if (empty($userid) || isguestuser($userid)) {
if ($CFG->notloggedinroleid == $this->roleid) {
$allow = true;
}
}

// Is the user viewing the course as guest?
if (is_guest($context, $userid)) {
if (get_guest_role()->id == $this->roleid) {
$allow = true;
}
// Is the user viewing the course as guest?
$context = \context_course::instance($info->get_course()->id);
if (is_guest($context, $userid)) {
if (get_guest_role()->id == $this->roleid) {
$allow = true;
}
}

Expand Down
47 changes: 41 additions & 6 deletions classes/frontend.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,56 @@ class frontend extends \core_availability\frontend {
* @return array
*/
protected function get_javascript_init_params($course, \cm_info $cm = null, \section_info $section = null) {
global $CFG, $DB;
require_once($CFG->dirroot . '/availability/condition/role/classes/condition.php');

// Change to JS array format and return.
$jsarray = [];
$context = \context_course::instance($course->id);
$idcourse = get_string('course');
$idcoursecat = get_string('coursecategory');
$idglobal = get_string('coresystem');
$jsarray = array();

// Get all roles for course.
$context = \context_course::instance($course->id);
$roles = $this->get_course_roles($context);
foreach ($roles as $rec) {
$jsarray[] = (object)array(
'id' => $rec->id,
'name' => (!empty($rec->name) ? $rec->name : $rec->localname),
'type' => $idcourse,
'typeid' => \availability_role\condition::ROLETYPE_COURSE,
);
}

$roles = explode(',', get_config('availability_role', 'coursecatroles'));
foreach ($roles as $rec) {
$rec = $DB->get_record('role', array('id' => $rec));
if (empty($rec->id)) {
continue;
}
$jsarray[] = (object)array(
'id' => $rec->id,
'name' => (!empty($rec->name) ? $rec->name : $rec->shortname),
'type' => $idcoursecat,
'typeid' => \availability_role\condition::ROLETYPE_COURSECAT,
);
}

$roles = explode(',', get_config('availability_role', 'globalroles'));
foreach ($roles as $rec) {
$jsarray[] = (object)[
$rec = $DB->get_record('role', array('id' => $rec));
if (empty($rec->id)) {
continue;
}
$jsarray[] = (object)array(
'id' => $rec->id,
'name' => $rec->localname,
];
'name' => (!empty($rec->name) ? $rec->name : $rec->shortname),
'type' => $idglobal,
'typeid' => \availability_role\condition::ROLETYPE_CORE,
);
}

return [$jsarray];
return array($jsarray);
}

/**
Expand Down
4 changes: 4 additions & 0 deletions lang/en/availability_role.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
$string['privacy:metadata'] = 'The Restriction by course role plugin does not store any personal data.';
$string['requires_role'] = 'You are a(n) <em>{$a}</em>';
$string['requires_notrole'] = 'You are not a(n) <em>{$a}</em>';
$string['setting_coursecatroles'] = 'Supported roles';
$string['setting_coursecatroles:description'] = 'Specify which courecategory roles are selectable by users to restrict access.';
$string['setting_globalroles'] = 'Supported roles';
$string['setting_globalroles:description'] = 'Specify which global roles are selectable by users to restrict access.';
$string['setting_supportedrolesheading'] = 'Supported roles';
$string['setting_supportguestrole'] = 'Guest role';
$string['setting_supportguestrole_desc'] = 'If activated, the availability of activities can be restricted to or forbidden for users that are viewing a course as guest.';
Expand Down
45 changes: 44 additions & 1 deletion settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,48 @@
$description = get_string('setting_supportnotloggedinrole_desc', 'availability_role', null, true);
$setting = new admin_setting_configcheckbox($name, $title, $description, 0);
$settings->add($setting);
}

// Course category roles.
$sql = "SELECT r.*
FROM {role} AS r, {role_context_levels} AS rcl
WHERE r.id=rcl.roleid
AND rcl.contextlevel = ?
ORDER BY r.name ASC";
$roles = $DB->get_records_sql($sql, array(CONTEXT_COURSECAT));
$options = array();
foreach ($roles as $role) {
$options[$role->id] = (!empty($role->name) ? $role->name : $role->shortname);
}

$settings->add(
new admin_setting_configmultiselect(
'availability_role/coursecatroles',
get_string('setting_coursecatroles', 'availability_role'),
get_string('setting_coursecatroles:description', 'availability_role'),
get_config('availability_role', 'coursecatroles'),
$options
)
);

// Global roles.
$sql = "SELECT r.*
FROM {role} AS r, {role_context_levels} AS rcl
WHERE r.id=rcl.roleid
AND rcl.contextlevel = ?
ORDER BY r.name ASC";
$roles = $DB->get_records_sql($sql, array(CONTEXT_SYSTEM));
$options = array();
foreach ($roles as $role) {
$options[$role->id] = (!empty($role->name) ? $role->name : $role->shortname);
}

$settings->add(
new admin_setting_configmultiselect(
'availability_role/globalroles',
get_string('setting_globalroles', 'availability_role'),
get_string('setting_globalroles:description', 'availability_role'),
get_config('availability_role', 'globalroles'),
$options
)
);
}
10 changes: 6 additions & 4 deletions version.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,16 @@
* @package availability_role
* @copyright 2015 Bence Laky, Synergy Learning UK <[email protected]>
* on behalf of Alexander Bias, Ulm University <[email protected]>
* extended to coursecat- and global roles
* by Robert Schrenk, Center for Learningmanagement <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/

defined('MOODLE_INTERNAL') || die();

$plugin->component = 'availability_role';
$plugin->version = 2023102000;
$plugin->release = 'v4.3-r1';
$plugin->requires = 2023100900;
$plugin->supported = [403, 403];
$plugin->version = 2023090100;
$plugin->release = 'v4.2-r1';
$plugin->requires = 2023042400;
$plugin->supported = [402, 402];
$plugin->maturity = MATURITY_STABLE;
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,29 @@ M.availability_role.form.getNode = function(json) {
'<span class="availability-group">' +
'<select name="id" class="custom-select">' +
'<option value="choose">' + M.util.get_string('choosedots', 'moodle') + '</option>';
var curroletypeid = -1;
var optopen = false;
Y.each(this.roles, function(role) {
html += '<option value="' + role.id + '">' + role.name + '</option>';
if (role.typeid != curroletypeid) {
curroletypeid = role.typeid;
if (optopen) {
html += '</optgroup>';
}
html += '<optgroup label="' + role.type + '">';
optopen = true;
}
html += '<option value="' + role.typeid + '_' + role.id + '">' + role.name + '</option>';
});
if (optopen) {
html += '</optgroup>';
}
html += '</select></span></label>';
var node = Y.Node.create('<span>' + html + '</span>');

// Set initial value if specified.
if (json.id !== undefined &&
node.one('select[name=id] > option[value=' + json.id + ']')) {
node.one('select[name=id]').set('value', '' + json.id);
node.one('select[name=id] option[value=' + json.typeid + '_' + json.id + ']')) {
node.one('select[name=id]').set('value', json.typeid + '_' + json.id);
}

// Add event handlers (first time only).
Expand All @@ -59,7 +72,9 @@ M.availability_role.form.fillValue = function(value, node) {
if (selected === 'choose') {
value.id = 'choose';
} else {
value.id = parseInt(selected, 10);
selected = selected.split('_');
value.typeid = parseInt(selected[0], 10);
value.id = parseInt(selected[1], 10);
}
};

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,29 @@ M.availability_role.form.getNode = function(json) {
'<span class="availability-group">' +
'<select name="id" class="custom-select">' +
'<option value="choose">' + M.util.get_string('choosedots', 'moodle') + '</option>';
var curroletypeid = -1;
var optopen = false;
Y.each(this.roles, function(role) {
html += '<option value="' + role.id + '">' + role.name + '</option>';
if (role.typeid != curroletypeid) {
curroletypeid = role.typeid;
if (optopen) {
html += '</optgroup>';
}
html += '<optgroup label="' + role.type + '">';
optopen = true;
}
html += '<option value="' + role.typeid + '_' + role.id + '">' + role.name + '</option>';
});
if (optopen) {
html += '</optgroup>';
}
html += '</select></span></label>';
var node = Y.Node.create('<span>' + html + '</span>');

// Set initial value if specified.
if (json.id !== undefined &&
node.one('select[name=id] > option[value=' + json.id + ']')) {
node.one('select[name=id]').set('value', '' + json.id);
node.one('select[name=id] option[value=' + json.typeid + '_' + json.id + ']')) {
node.one('select[name=id]').set('value', json.typeid + '_' + json.id);
}

// Add event handlers (first time only).
Expand All @@ -59,7 +72,9 @@ M.availability_role.form.fillValue = function(value, node) {
if (selected === 'choose') {
value.id = 'choose';
} else {
value.id = parseInt(selected, 10);
selected = selected.split('_');
value.typeid = parseInt(selected[0], 10);
value.id = parseInt(selected[1], 10);
}
};

Expand Down
Loading