Commit 5e4899cf authored by Robert Lyon's avatar Robert Lyon

Bug 1855383: Adding the framework for custom user roles

This patch deals with creating the new profile field class for userroles
and the db table to hold the roles so that we can see the roles listed
against the user when they exist

Also begins the UserRole class to set/unset roles

behatnotneeded

Change-Id: I7b14be1bed97cd470da5f03519597d1679532f3b
Signed-off-by: Robert Lyon's avatarRobert Lyon <robertl@catalyst.net.nz>
parent d9671ec5
......@@ -81,14 +81,15 @@ if (method_exists($authobj, 'change_password')) {
'defaultvalue' => $user->passwordchange,
);
}
$roleelements = array();
if ($USER->get('admin')) {
$elements['staff'] = array(
$roleelements['staff'] = array(
'type' => 'switchbox',
'title' => get_string('sitestaff','admin'),
'defaultvalue' => $user->staff,
'help' => true,
);
$elements['admin'] = array(
$roleelements['admin'] = array(
'type' => 'switchbox',
'title' => get_string('siteadmin','admin'),
'defaultvalue' => $user->admin,
......@@ -105,6 +106,25 @@ $elements['email'] = array(
'email' => true,
),
);
if ($user->roles && isset($user->roles['_site'])) {
foreach ($user->roles['_site'] as $rk => $role) {
$roleelements['userroles_' . $role->id] = array(
'type' => 'switchbox',
'title' => get_string($rk),
'defaultvalue' => $role->active,
);
}
}
if (!empty($roleelements)) {
$elements['roles'] = array(
'type' => 'fieldset',
'collapsible' => true,
'collapsed' => false,
'legend' => get_string('userroles', 'artefact.internal'),
'class' => 'dropdown-group js-dropdown-group',
'elements' => $roleelements,
);
}
$elements['maildisabled'] = array(
'type' => 'switchbox',
'defaultvalue' => get_account_preference($user->id, 'maildisabled'),
......@@ -654,6 +674,16 @@ function edituser_site_submit(Pieform $form, $values) {
);
}
}
$userobj = new User();
$userobj = $userobj->find_by_id($user->id);
foreach ($values as $index => $value) {
if (preg_match('/^userroles_(.*)/', $index, $matches)) {
if ($value != $form->get_element($index)['defaultvalue']) {
$userobj->update_role($matches[1], (int) $value);
}
}
}
unset($userobj);
db_commit();
$SESSION->add_ok_msg(get_string('usersitesettingschanged', 'admin'));
......@@ -806,6 +836,31 @@ $allinstitutions = get_records_assoc('institution', '', '', 'displayname', 'name
$institutionloop = 0;
$institutionlength = count($institutions);
foreach ($institutions as $i) {
$roleelements = array(
$i->institution.'_staff' => array(
'name' => $i->institution.'_staff',
'type' => 'switchbox',
'title' => get_string('institutionstaff','admin'),
'defaultvalue' => $i->staff,
),
$i->institution.'_admin' => array(
'name' => $i->institution.'_admin',
'type' => 'switchbox',
'title' => get_string('institutionadmin','admin'),
'description' => get_string('institutionadmindescription1','admin'),
'defaultvalue' => $i->admin,
),
);
if ($user->roles && isset($user->roles[$i->institution])) {
foreach ($user->roles[$i->institution] as $rk => $role) {
$roleelements[$i->institution . '_roles_' . $role->id] = array(
'name' => $i->institution . '_roles_' . $role->id,
'type' => 'switchbox',
'title' => get_string($rk),
'defaultvalue' => $role->active,
);
}
}
$elements[$i->institution.'_settings'] = array(
'type' => 'fieldset',
'legend' => get_string('institutionsettings', 'admin').' - '.$i->displayname,
......@@ -830,16 +885,13 @@ foreach ($institutions as $i) {
'description' => get_string('institutionstudentiddescription', 'admin'),
'defaultvalue' => $i->studentid,
),
$i->institution.'_staff' => array(
'type' => 'switchbox',
'title' => get_string('institutionstaff','admin'),
'defaultvalue' => $i->staff,
),
$i->institution.'_admin' => array(
'type' => 'switchbox',
'title' => get_string('institutionadmin','admin'),
'description' => get_string('institutionadmindescription1','admin'),
'defaultvalue' => $i->admin,
$i->institution.'_roles' => array(
'type' => 'fieldset',
'collapsible' => true,
'collapsed' => false,
'legend' => get_string('userroles', 'artefact.internal'),
'class' => 'dropdown-group js-dropdown-group',
'elements' => $roleelements,
),
$i->institution.'_submit' => array(
'type' => 'submit',
......@@ -940,6 +992,13 @@ function edituser_institution_submit(Pieform $form, $values) {
db_begin();
delete_records('usr_institution', 'usr', $user->id, 'institution', $i->institution);
insert_record('usr_institution', $newuser);
foreach ($values as $index => $value) {
if (preg_match('/^' . $i->institution . '_roles_(.*)/', $index, $matches)) {
if ($value != $form->get_element($index)['defaultvalue']) {
$user->update_role($matches[1], (int) $value);
}
}
}
if ($newuser->admin) {
activity_add_admin_defaults(array($user->id));
}
......
......@@ -360,6 +360,15 @@ function uploadcsv_validate(Pieform $form, $values) {
$csverrors->add($i, get_string('uploadcsverrorexpirydateinpast', 'admin', $i, $expirydate));
}
}
if (array_key_exists('userroles', $formatkeylookup) && !empty($line[$formatkeylookup['userroles']])) {
$userroles = explode(',', $line[$formatkeylookup['userroles']]);
foreach ($userroles as $roleid => $role) {
$classname = 'UserRole' . ucfirst($role);
if (!class_exists($classname)) {
$csverrors->add($i, get_string('uploadcsverroruserrolemissing', 'admin', $i, $role, ucfirst($role)));
}
}
}
}
// If the admin is trying to overwrite existing users, identified by username,
......@@ -573,6 +582,19 @@ function uploadcsv_submit(Pieform $form, $values) {
}
continue;
}
if ($field == 'userroles') {
if (!empty($record[$formatkeylookup[$field]])) {
$userroles = explode(',', $record[$formatkeylookup[$field]]);
foreach ($userroles as $roleid => $role) {
$userroles[$roleid] = array('role' => $role,
'institution' => '_site',
'active' => 1,
'provisioner' => 'csv');
}
$profilefields->{$field} = $userroles;
}
continue;
}
$profilefields->{$field} = $record[$formatkeylookup[$field]];
}
......
......@@ -118,6 +118,10 @@ foreach ( $element_list as $element => $type ) {
$defaultoption = call_static_method($classname, 'defaultoption');
$items[$element]['defaultvalue'] = $defaultoption;
}
if ($type == 'html' && is_callable(array($classname, 'defaulthtml'))) {
$defaultvalue = call_static_method($classname, 'defaulthtml');
$items[$element]['value'] = $defaultvalue;
}
if ($element == 'socialprofile') {
$items[$element] = ArtefactTypeSocialprofile::render_profile_element();
}
......@@ -209,7 +213,7 @@ $profileform = pieform(array(
function get_desired_fields(&$allfields, $section) {
global $USER;
$desiredfields = array('about' => array('firstname', 'lastname', 'studentid', 'preferredname', 'introduction'),
$desiredfields = array('about' => array('firstname', 'lastname', 'studentid', 'preferredname', 'userroles', 'introduction'),
'contact' => array('email', 'maildisabled', 'officialwebsite', 'personalwebsite', 'blogaddress', 'address', 'town', 'city', 'country', 'homenumber', 'businessnumber', 'mobilenumber', 'faxnumber'),
'social' => array('socialprofile'),
);
......@@ -446,7 +450,7 @@ function profileform_submit(Pieform $form, $values) {
$USER->commit();
}
}
else if (in_array($element, array('maildisabled', 'socialprofile'))) {
else if (in_array($element, array('maildisabled', 'socialprofile', 'userroles'))) {
continue;
}
else {
......
......@@ -76,6 +76,8 @@ $string['pinterest.input'] = 'Pinterest username';
$string['pinterest'] = 'Pinterest';
$string['occupation'] = 'Occupation';
$string['industry'] = 'Industry';
$string['userroles'] = 'User roles';
$string['nospecialroles'] = '<span class="text-midtone">No special roles</span>';
// Field names for view user and search user display
$string['name'] = 'Name';
......
......@@ -39,6 +39,7 @@ class PluginArtefactInternal extends PluginArtefact {
'industry',
'html',
'socialprofile',
'userroles',
);
if (class_exists('PluginArtefactInternalLocal', false)) {
$localtypes = PluginArtefactInternalLocal::get_artefact_types();
......@@ -69,6 +70,7 @@ class PluginArtefactInternal extends PluginArtefact {
'occupation',
'industry',
'socialprofile',
'userroles',
);
if (class_exists('PluginArtefactInternalLocal', false)) {
$localtypes = PluginArtefactInternalLocal::get_profile_artefact_types();
......@@ -92,6 +94,7 @@ class PluginArtefactInternal extends PluginArtefact {
'mobilenumber',
'faxnumber',
'socialprofile',
'userroles',
);
if (class_exists('PluginArtefactInternalLocal', false)) {
$localtypes = PluginArtefactInternalLocal::get_contactinfo_artefact_types();
......@@ -496,6 +499,7 @@ class ArtefactTypeProfile extends ArtefactType {
'occupation' => 'text',
'industry' => 'text',
'maildisabled' => 'html',
'userroles' => 'html',
);
$social = array();
if (get_record('blocktype_installed', 'active', 1, 'name', 'socialprofile')) {
......@@ -1344,3 +1348,46 @@ class ArtefactTypeSocialprofile extends ArtefactTypeProfileField {
}
}
class ArtefactTypeUserroles extends ArtefactTypeProfileField {
public function render_self($options) {
return array('html' => self::defaulthtml());
}
function defaulthtml() {
if ($roles = self::get_multiple()) {
$rolestr = '';
foreach ($roles as $k => $role) {
if ($k !== 0) {
$rolestr .= ', ';
}
$rolestr .= get_string($role, 'artefact.internal');
}
}
else {
$rolestr = get_string('nospecialroles', 'artefact.internal');
}
return $rolestr;
}
function format_result($raw) {
return get_string("userroles.{$raw}");
}
function usersearch_column_structure() {
return array('name' => 'userroles', 'sort' => false, 'template' => 'admin/users/searchuserroles.tpl');
}
function can_be_multiple() {
return true;
}
function get_multiple($userid = null) {
global $USER;
if (!$userid) {
$userid = $USER->get('id');
}
return get_column('usr_roles', 'role', 'usr', $userid);
}
}
......@@ -70,6 +70,7 @@ class User {
'accountprefs' => array(),
'activityprefs' => array(),
'institutions' => array(),
'roles' => array(),
'grouproles' => array(),
'institutiontheme' => null,
'admininstitutions' => array(),
......@@ -118,6 +119,7 @@ class User {
$this->populate($user);
$this->reset_institutions();
$this->reset_roles();
$this->reset_grouproles();
return $this;
}
......@@ -718,6 +720,14 @@ class User {
public function leave_institution($institution) {
if ($institution != 'mahara' && $this->in_institution($institution)) {
// Make inactive any usr_roles for this institution
foreach ($this->roles as $inst => $roles) {
if ($inst == $institution) {
foreach ($roles as $role) {
$this->update_role($role->id, 0);
}
}
}
require_once('institution.php');
$institution = new Institution($institution);
$institution->removeMember($this->to_stdclass());
......@@ -988,6 +998,87 @@ class User {
return $this->institutiontheme;
}
public function get_roletypes() {
$types = array();
foreach (get_declared_classes() as $class) {
if (is_subclass_of($class, 'UserRole')) {
$types[] = $class;
}
}
return $types;
}
public function apply_userrole_method($method, $data) {
$checks = array();
foreach ($this->get_roletypes() as $classname) {
if (method_exists($classname, $method)) {
$ur = new $classname;
$checks[$classname] = $ur->$method($data);
}
}
return $checks;
}
public function reset_roles() {
$sql = "SELECT id, role, usr, provisioner, institution, active FROM {usr_roles} WHERE usr = ?";
$usrroles = get_records_sql_array($sql, array($this->get('id')));
$roles = array();
if ($usrroles) {
foreach ($usrroles as $r) {
if (empty($r->institution)) {
$roles['_site'][$r->role] = $r;
}
else {
$roles[$r->institution][$r->role] = $r;
}
}
}
$this->set('roles', $roles);
}
public function set_roles($roles) {
foreach ($roles as $key => $role) {
$role = (object) $role;
if (isset($role->role) && !empty($role->role)) {
$classname = 'UserRole' . ucfirst($role->role);
$role->usr = $this->id;
$r = new $classname($role);
$r->commit();
}
}
$this->reset_roles();
}
public function update_role($roleid, $state) {
set_field('usr_roles', 'active', $state, 'id', $roleid);
$this->reset_roles();
}
public function get_roles() {
$this->reset_roles();
return $this->roles;
}
public function get_role($role, $institution = '_site', $provisioner = null, $isactive = null) {
$this->reset_roles();
if (isset($this->roles[$institution]) && isset($this->roles[$institution][$role])) {
if ($provisioner !== null && $this->roles[$institution][$role]->provisioner != $provisioner) {
return false;
}
if ($isactive !== null && $this->roles[$institution][$role]->active != (bool)$isactive) {
return false;
}
$classname = 'UserRole' . ucfirst($role);
if (class_exists($classname)) {
return new $classname($this->roles[$institution][$role]);
}
else {
// @TODO - should we remove the role from the db?
}
}
return false;
}
public function reset_grouproles() {
$sql = "SELECT gm.* FROM {group_member} gm
JOIN {group} g ON g.id = gm.group
......@@ -1937,6 +2028,7 @@ class LiveUser extends User {
}
$this->reset_institutions();
$this->reset_roles();
$this->reset_grouproles();
$this->load_views();
$this->store_sessionid();
......@@ -2130,6 +2222,106 @@ class LiveUser extends User {
}
}
abstract class UserRole {
protected $role;
protected $id;
protected $usr;
protected $provisioner='internal';
protected $institution=null;
protected $active;
public function __construct($role, $data=null) {
$this->role = $role;
$id = false;
if (is_object($data) && !empty($data->id)) {
$id = $data->id;
}
else if (is_array($data) && !empty($data['id'])) {
$id = $data['id'];
}
else if (!empty($data) && is_numeric($data)) {
$id = $data;
}
if ($id) {
$data = get_record('usr_roles', 'id', $id);
if (!$data) {
throw new MaharaException('No UserRole with the ID: ' . $id);
}
else if ($data->role != $this->role) {
throw new MaharaException('Fetched data roletype "' . $data->role . '" does not match UserRole class roletype "' . $this->role . '"');
}
}
$data = empty($data) ? array() : (array)$data;
foreach ($data as $field => $value) {
if (property_exists($this, $field)) {
$this->{$field} = $value;
}
}
}
public function get($field) {
if (!property_exists($this, $field)) {
throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
}
return $this->{$field};
}
public function set($field, $value) {
if (property_exists($this, $field)) {
$this->{$field} = $value;
return true;
}
throw new InvalidArgumentException("Field $field wasn't found in class " . get_class($this));
}
public function commit() {
if (empty($this->role)) {
throw new MaharaException('UserRole data needs to contain a role');
}
if (empty($this->usr) || !get_field('usr', 'username', 'deleted', 0, 'id', $this->usr)) {
throw new MaharaException('UserRole data needs to contain a valid usr id');
}
$fordb = new stdClass();
$fordb->usr = $this->usr;
$fordb->role = $this->role;
$fordb->institution = (isset($this->institution) && $this->institution != '_site') ? $this->institution : null;
$fordb->provisioner = isset($this->provisioner) ? $this->provisioner : 'internal';
$fordb->active = isset($this->active) ? $this->active : 1;
if (!empty($this->id)) {
$whereobj = new stdClass();
$whereobj->id = $this->id;
}
else {
$whereobj = clone $fordb;
}
$fordb->ctime = isset($this->ctime) ? $this->ctime : db_format_timestamp(time());
ensure_record_exists('usr_roles', $whereobj, $fordb);
}
public function activate() {
if (!empty($this->id)) {
set_field('usr_roles', 'active', 1, 'id', $this->id);
}
$this->active = 1;
}
public function deactivate() {
if (!empty($this->id)) {
set_field('usr_roles', 'active', 0, 'id', $this->id);
}
$this->active = 0;
}
public function delete() {
if (!empty($this->id)) {
delete_records('usr_roles', 'id', $this->id);
}
}
}
/**
* Indicates whether the site is closed for a user
* @param boolean $isuseradmin Whether the user we're checking for is an admin
......
......@@ -57,7 +57,17 @@ if ($forums) {
foreach ($forums as $forum) {
$forum->feedlink = get_config('wwwroot') . 'interaction/forum/atom.php?type=f&id=' . $forum->id;
$allowunsubscribe = get_config_plugin_instance('interaction_forum', $forum->id, 'allowunsubscribe');
if ($allowunsubscribe) {
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('interaction_unsubscribe', array('forum' => $forum->id, 'userid' => $USER->get('id')));
foreach ($checks as $check) {
if ($check['can_unsubscribe'] === false) {
// A UserRole is stopping us from unsubscribing
$allowunsubscribe = false;
break;
}
}
}
if ($membership) {
$forum->subscribe = pieform(array(
'name' => 'subscribe_forum' . ($i == 0 ? '' : $i),
......
......@@ -120,6 +120,7 @@ Attachments:
%s";
$string['forumsettings'] = 'Forum settings';
$string['forumsuccessfulsubscribe'] = 'Forum subscribed successfully';
$string['forumfailunsubscribe'] = 'You are not allowed to unsubscribe.';
$string['forumsuccessfulunsubscribe'] = 'Forum unsubscribed successfully';
$string['gotoforums'] = 'Go to forums';
$string['groupadmins'] = 'Group administrators';
......
......@@ -244,6 +244,14 @@ EOF;
}
}
// Check if any UserRoles are in play
foreach ($USER->get_roletypes() as $classname) {
if (method_exists($classname, 'interaction_subscribe')) {
$ur = new $classname;
$ur->interaction_subscribe(array('id' => $instance->get('id')));
}
}
// Moderators
delete_records(
'interaction_forum_moderator',
......@@ -1693,12 +1701,27 @@ function subscribe_forum_submit(Pieform $form, $values) {
$SESSION->add_ok_msg(get_string('forumsuccessfulsubscribe', 'interaction.forum'));
}
else {
delete_records(
'interaction_forum_subscription_forum',
'forum', $values['forum'],
'user', $USER->get('id')
);
$SESSION->add_ok_msg(get_string('forumsuccessfulunsubscribe', 'interaction.forum'));
$can_unsubscribe = true;
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('interaction_unsubscribe', array('forum' => $values['forum'], 'userid' => $USER->get('id')));
foreach ($checks as $check) {
if ($check['can_unsubscribe'] === false) {
// A UserRole is stopping us from unsubscribing
$can_unsubscribe = false;
break;
}
}
if ($can_unsubscribe) {
delete_records(
'interaction_forum_subscription_forum',
'forum', $values['forum'],
'user', $USER->get('id')
);
$SESSION->add_ok_msg(get_string('forumsuccessfulunsubscribe', 'interaction.forum'));
}
else {
$SESSION->add_error_msg(get_string('forumfailunsubscribe', 'interaction.forum'));
}
}
if ($values['redirect'] == 'index') {
redirect('/interaction/forum/index.php?group=' . $values['group']);
......
......@@ -686,6 +686,7 @@ $string['uploadcsverrorremoteusertaken'] = 'Line %s of the file specifies a remo
$string['uploadcsverrorusernotininstitution'] = 'Error on line %s: The user "%s" is not a member of the institution %s.';
$string['uploadcsverroruserinaninstitution'] = 'Error on line %s: The user "%s" is a member of the following institutions: %s. You cannot update this user\'s authentication method to "No Institution".';
$string['uploadcsverrorinvalidexpirydate'] = 'Error on line %s: The expiry "%s" is invalid. Please use a valid date format.';
$string['uploadcsverroruserrolemissing'] = 'Error on line %s: The class for the user role "%s" is missing. Please make sure the "UserRole%s" class exists and is accessible.';
$string['uploadcsverrorexpirydateinpast'] = 'Error on line %s: The expiry "%s" cannot be in the past.';
$string['uploadcsvpagedescription6'] = '<p>Here you can upload new users via a <acronym title="Comma Separated Values">CSV</acronym> file.</p>
......
......@@ -1440,5 +1440,20 @@
<KEY NAME="newauthfk" TYPE="foreign" FIELDS="new_authinstance" REFTABLE="auth_instance" REFFIELDS="id"/>
</KEYS>
</TABLE>
<TABLE NAME="usr_roles">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" SEQUENCE="true" NOTNULL="true" />
<FIELD NAME="usr" TYPE="int" LENGTH="10" NOTNULL="true" />
<FIELD NAME="role" TYPE="char" LENGTH="255" NOTNULL="true" />
<FIELD NAME="ctime" TYPE="datetime" NOTNULL="true" />
<FIELD NAME="provisioner" TYPE="char" LENGTH="255" NOTNULL="true"/>
<FIELD NAME="institution" TYPE="char" LENGTH="255" NOTNULL="false"/>
<FIELD NAME="active" TYPE="int" LENGTH="1" NOTNULL="true" DEFAULT="1"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" />
<KEY NAME="usrfk" TYPE="foreign" FIELDS="usr" REFTABLE="usr" REFFIELDS="id"/>
</KEYS>
</TABLE>
</TABLES>
</XMLDB>
......@@ -1614,5 +1614,23 @@ function xmldb_core_upgrade($oldversion=0) {
}
}
if ($oldversion < 2020011700) {
log_debug('Adding in new usr_roles table with different structure');
$table = new XMLDBTable('usr_roles');
if (!table_exists($table)) {
$table->addFieldInfo('id', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL, XMLDB_SEQUENCE);
$table->addFieldInfo('usr', XMLDB_TYPE_INTEGER, 10, null, XMLDB_NOTNULL);
$table->addFieldInfo('role', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL);
$table->addFieldInfo('ctime', XMLDB_TYPE_DATETIME, null, null, XMLDB_NOTNULL);
$table->addFieldInfo('provisioner', XMLDB_TYPE_CHAR, 255, null, XMLDB_NOTNULL);
$table->addFieldInfo('institution', XMLDB_TYPE_CHAR, 255);
$table->addFieldInfo('active', XMLDB_TYPE_INTEGER, 1, null, XMLDB_NOTNULL, null, null, null, 1);
$table->addKeyInfo('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->addKeyInfo('usrfk', XMLDB_KEY_FOREIGN, array('usr'), 'usr', array('id'));
create_table($table);
}
}
return $status;
}
......@@ -319,6 +319,8 @@ function group_user_can_assess_submitted_views($groupid, $userid) {
* AccessDeniedException
*/
function group_create($data) {
global $USER;
if (!is_array($data)) {
throw new InvalidArgumentException("group_create: data must be an array, see the doc comment for this "
. "function for details on its format");
......@@ -491,6 +493,8 @@ function group_create($data) {
)
);
}
// Check if any UserRoles are in play
$USER->apply_userrole_method('group_join', array('groupid' => $id, 'ctime' => $data['ctime']));
// Copy views for the new group
$artefactcopies = array();
......@@ -1072,6 +1076,14 @@ function group_user_can_leave($group, $userid=null) {
return ($result[$group->id][$userid] = false);
}
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('group_leave', array('groupid' => $group->id, 'userid' => $userid));
foreach ($checks as $check) {
if ($check['can_leave'] === false) {
return ($result[$group->id][$userid] = false);
}
}
return ($result[$group->id][$userid] = true);
}
......@@ -1887,6 +1899,13 @@ function group_get_membersearch_data($results, $group, $query, $membershiptype,
$role = group_user_access($group);
$userid = $USER->get('id');
foreach ($results['data'] as &$r) {
// Check if any UserRoles are in play
$checks = $USER->apply_userrole_method('group_leave', array('groupid' => $group, 'userid' => $r['id']));
foreach ($checks as $check) {
if ($check['can_leave'] === false) {
continue 2;
}
}
if ($role == 'admin' && ($r['id'] != $userid || group_user_can_leave($group, $r['id']))) {
$r['removeform'] = group_get_removeuser_form($r['id'], $group);
}
......@@ -3248,3 +3267,67 @@ function get_group_access_roles() {
}
return $data;
}
function group_add_user_to_existing_groups($userid = null, $role = 'member') {
global $USER;
if (empty($userid)) {
$userid = $USER->get('id');
}
// Find all the non-deleted groups where the user is not present
// or is present but with a different group role
if ($groups = get_records_sql_assoc("SELECT g.id, gm.* FROM {group} g
LEFT JOIN {group_member} gm ON (gm.group = g.id AND gm.member = ?)
WHERE (gm.role IS NULL OR gm.role != ?)
AND g.deleted = 0
ORDER BY g.id", array($userid, $role))) {