/** * More user friendly role permission changing, * it should produce as few overrides as possible. * * @param int $roleid * @param stdClass $context * @param string $capname capability name * @param int $permission * @return void */ function role_change_permission($roleid, $context, $capname, $permission) { global $DB; if ($permission == CAP_INHERIT) { unassign_capability($capname, $roleid, $context->id); $context->mark_dirty(); return; } $ctxids = trim($context->path, '/'); // kill leading slash $ctxids = str_replace('/', ',', $ctxids); $params = array('roleid'=>$roleid, 'cap'=>$capname); $sql = "SELECT ctx.id, rc.permission, ctx.depth FROM {role_capabilities} rc JOIN {context} ctx ON ctx.id = rc.contextid WHERE rc.roleid = :roleid AND rc.capability = :cap AND ctx.id IN ($ctxids) ORDER BY ctx.depth DESC"; if ($existing = $DB->get_records_sql($sql, $params)) { foreach($existing as $e) { if ($e->permission == CAP_PROHIBIT) { // prohibit can not be overridden, no point in changing anything return; } } $lowest = array_shift($existing); if ($lowest->permission == $permission) { // permission already set in this context or parent - nothing to do return; } if ($existing) { $parent = array_shift($existing); if ($parent->permission == $permission) { // permission already set in parent context or parent - just unset in this context // we do this because we want as few overrides as possible for performance reasons unassign_capability($capname, $roleid, $context->id); $context->mark_dirty(); return; } } } else { if ($permission == CAP_PREVENT) { // nothing means role does not have permission return; } } // assign the needed capability assign_capability($capname, $permission, $roleid, $context->id, true); // force cap reloading $context->mark_dirty(); }