Commit 3a5f0d1c authored by Francois Marier's avatar Francois Marier
Browse files

Add AJAX group membership controls to 'My friends' and 'Find friends'



Additional data on 'controlled' and 'invite only' groups is fetched
and passed to templates for group control pop-up boxes.
Signed-off-by: default avatarRuslan Kabalin <ruslan.kabalin@luns.net.uk>
Signed-off-by: default avatarFrancois Marier <francois@catalyst.net.nz>
parent 8341c92b
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
* http://wiki.mahara.org/Contributors
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage core
* @author Ruslan Kabalin <ruslan.kabalin@luns.net.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
* @copyright (C) 2009 Lancaster University Network Services Limited
* http://www.luns.net.uk
*
*/
define('INTERNAL', 1);
define('JSON', 1);
require(dirname(dirname(__FILE__)) . '/init.php');
require_once('group.php');
$data['error'] = false;
$data['message'] = null;
$initialgroups = param_integer_list('initialgroups', array());
$resultgroups = param_integer_list('resultgroups', array());
$userid = param_integer('userid');
$jointype = param_variable('jointype');
// Prevent group membership changing done by ordinary members, Tutors can only
// add members to group and cannot remove anyone. Group admins can do anything.
// With regard to invitation, both admins and tutors can invite people.
foreach (array_unique(array_merge($initialgroups, $resultgroups)) as $groupid) {
if (!group_user_access($groupid)) {
json_reply('local', get_string('accessdenied', 'error'));
break;
}
switch (group_user_access($groupid)) {
case 'member':
json_reply('local', get_string('accessdenied', 'error'));
break;
case 'tutor':
if ($usertype = group_user_access($groupid, $userid)) {
if (($usertype == 'member') && in_array($groupid, array_diff($initialgroups, $resultgroups))) {
json_reply('local', get_string('cantremovemember', 'group'));
}
elseif ($usertype != 'member' && in_array($groupid, array_diff($initialgroups, $resultgroups))) {
json_reply('local', get_string('cantremoveuserisadmin', 'group'));
}
}
}
}
$groupdata = get_records_select_assoc('group', 'id IN (' . join(',', array_unique(array_merge($initialgroups, $resultgroups))) . ')');
$groupstoremovemail = '';
$groupstoaddmail = '';
if ($jointype == 'controlled') {
db_begin();
//remove group membership
if ($groupstoremove = array_diff($initialgroups, $resultgroups)) {
foreach ($groupstoremove as $groupid) {
group_remove_user($groupid, $userid, $role=null);
$groupstoremovemail .= $groupdata[$groupid]->name . "\n";
}
}
//add group membership
if ($groupstoadd = array_diff($resultgroups, $initialgroups)) {
foreach ($groupstoadd as $groupid) {
group_add_user($groupid, $userid, $role=null);
$groupstoaddmail .= $groupdata[$groupid]->name . "\n";
}
}
db_commit();
// Users notification
$userrecord = get_record('usr', 'id', $userid);
$lang = get_user_language($userid);
$n = new StdClass;
$n->users = array($userid);
$n->subject = get_string_from_language($lang, 'changedgroupmembershipsubject', 'group');
$n->message = '';
if (isset($groupstoaddmail)) {
$n->message .= get_string_from_language($lang, 'addedtogroupsmessage', 'group', display_name($USER, $userrecord), $groupstoaddmail);
}
if (isset($groupstoremovemail)) {
$n->message .= get_string_from_language($lang, 'removedfromgroupsmessage', 'group', display_name($USER, $userrecord), $groupstoremovemail);
}
require_once(get_config('libroot') . 'activity.php');
activity_occurred('maharamessage', $n);
$data['message'] = get_string('changedgroupmembership', 'group');
}
elseif ($jointype == 'invite') {
if ($groupstoadd = array_diff($resultgroups, $initialgroups)) {
foreach ($groupstoadd as $groupid) {
group_invite_user($groupdata[$groupid], $userid);
}
}
$data['message'] = get_string('userinvited', 'group');
}
json_headers();
echo json_encode($data);
exit;
<?php
/**
* Mahara: Electronic portfolio, weblog, resume builder and social networking
* Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
* http://wiki.mahara.org/Contributors
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @package mahara
* @subpackage core
* @author Ruslan Kabalin <ruslan.kabalin@luns.net.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL
* @copyright (C) 2009 Lancaster University Network Services Limited
* http://www.luns.net.uk
*
*/
define('INTERNAL', 1);
define('JSON', 1);
require(dirname(dirname(__FILE__)) . '/init.php');
$userid = param_integer('userid');
$jointype = param_variable('jointype');
if (!in_array($jointype, array('controlled', 'invite'))) {
json_reply('local', get_string('accessdenied', 'error'));
}
if ($jointype == 'controlled') {
/* Get (a) controlled membership groups,
(b) request membership groups where the displayed user has requested membership,
where the logged in user either:
1. is a group admin, or;
2. has a role in the list of roles who are allowed to assess submitted views for the given grouptype
@return array A data structure containing results looking like ...
* $results = array(
* array(
* g* => mixed, //all group data
* role => string, // logged-in user's role
* member => bool, // destination user's group membership
* memberrole => string, // destination user's role in the group
* ),
* array(...),
* );
*/
$request = get_records_sql_array("SELECT g.*, gm.role,
(SELECT 1 FROM {group_member} gm1 WHERE gm1.member = ? AND gm1.group = g.id) AS member,
(SELECT gm1.role FROM {group_member} gm1 WHERE gm1.member = ? AND gm1.group = g.id) AS memberrole
FROM {group} g
JOIN {group_member} gm ON (gm.group = g.id)
JOIN {grouptype_roles} gtr ON (gtr.grouptype = g.grouptype AND gtr.role = gm.role)
LEFT JOIN {group_member_request} gmr ON (gmr.member = ? AND gmr.group = g.id)
WHERE gm.member = ?
AND (g.jointype = 'controlled' OR (g.jointype = 'request' AND gmr.member = ?))
AND (gm.role = 'admin' OR gtr.see_submitted_views = 1)
AND g.deleted = 0", array($userid, $userid, $userid, $USER->get('id'), $userid));
}
elseif ($jointype == 'invite') {
/* Get 'Invite olny' groups where the logged in user is a group admin.
@return array A data structure containing results looking like ...
* $results = array(
* array(
* g* => mixed, //all group data
* role => string, // logged-in user's role
* member => bool, // destination user's group membership
* invited => bool, // destination user's invite status
* ),
* array(...),
* );
*/
$request = get_records_sql_array("SELECT g.*, gm.role,
(SELECT 1 FROM {group_member_invite} gi WHERE gi.member = ? AND gi.group = g.id) AS invited,
(SELECT 1 FROM {group_member} gm1 WHERE gm1.member = ? AND gm1.group = g.id) AS member
FROM {group} g
JOIN {group_member} gm ON (gm.group = g.id)
WHERE gm.member = ?
AND g.jointype = 'invite'
AND gm.role = 'admin'
AND g.deleted = 0", array($userid, $userid, $USER->get('id')));
}
$data['data'] = $request;
$data['error'] = false;
$data['message'] = null;
json_headers();
echo json_encode($data);
exit;
/**
* Provides functionality for pop-up GroupBoxes on Find Friend and My Friends pages.
*
* (C) 2009 Lancaster University Network Services Limited
* http://www.luns.net.uk
* This file is licensed under the same terms as Mahara itself
*/
// array compare method
Array.prototype.compare = function(testArr) {
if (this.length != testArr.length) return false;
for (var i = 0; i < testArr.length; i++) {
if (this[i].compare) {
if (!this[i].compare(testArr[i])) return false;
}
if (this[i] !== testArr[i]) return false;
}
return true;
}
var ul = null;
var initialgroups = new Array();
var reversetypes = {'invite':'controlled','controlled':'invite'};
function showGroupBox(event, user_id, type) {
replaceChildNodes('messages');
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
ul = $(type + 'groupbox_' + user_id).getElementsByTagName('ul')[0];
if (getStyle($(type + 'groupbox_' + user_id), 'display') == 'block'){
hideElement($(type + 'groupbox_' + user_id));
}
else {
hideElement($(reversetypes[type] + 'groupbox_' + user_id));
getitems(user_id, type, function() {
showElement($(type + 'groupbox_' + user_id));
});
}
}
function changemembership(event, user_id, type) {
replaceChildNodes('messages');
if (event.preventDefault) {
event.preventDefault();
} else {
event.returnValue = false;
}
ul = $(type + 'groupbox_' + user_id).getElementsByTagName('ul')[0];
var groups = document.getElementsByName(type+'group_'+user_id);
var resultgroups = new Array();
forEach(groups, function(group) {
if (group.checked == true ) {
resultgroups.push(group.value);
}
});
// apply changes only if something has been changed
if (!initialgroups[user_id].compare(resultgroups)){
sendjsonrequest('../group/changegroupsmembership.json.php',
{
'jointype':type,
'userid':user_id,
'resultgroups':resultgroups.join(','),
'initialgroups':initialgroups[user_id].join(',')
}, 'POST',
function() {
getitems(user_id, type, function() {});
});
}
}
function getitems(user_id, type, successfunction) {
sendjsonrequest('../group/controlledgroups.json.php', {
'userid':user_id,
'jointype':type
}, 'GET',
function(groups) {
var results = new Array();
initialgroups[user_id] = [];
if (groups.data == false) {
results.push(LI(get_string('nogroups')));
}
else {
forEach(groups.data, function(group) {
var li = LI('');
var input = INPUT({
'type':'checkbox',
'class':'checkbox',
'name':type+'group_'+user_id,
'value':group.id
});
if (group.member || group.invited) {
input.checked = true;
initialgroups[user_id].push(group.id);
}
appendChildNodes(li, input, '\u00A0\u00A0', group.name);
if (group.invited || (type == 'invite' && group.member) || (group.role == 'tutor' && ((group.memberrole == 'member' && input.checked) || (group.member && group.memberrole != 'member')))) {
li.setAttribute('class', 'disabled');
input.disabled = true;
}
results.push(li);
});
var a = A({
'href':'#'
}, '\u00A0\u00A0', get_string('applychanges'));
a.setAttribute('onclick', 'changemembership(event, '+user_id+', \''+type+'\');');
results.push(LI({}, a));
}
replaceChildNodes(ul, results);
if (ul.childNodes.length) ul.lastChild.className = 'last';
successfunction();
});
}
......@@ -35,6 +35,7 @@ require_once('searchlib.php');
$query = param_variable('query', '');
$offset = param_integer('offset', 0);
$limit = param_integer('limit', 10);
$filter = param_alpha('filter', 'all');
$page = 'myfriends';
if ($extradata = param_variable('extradata', null)) {
......@@ -44,9 +45,37 @@ if ($extradata = param_variable('extradata', null)) {
}
}
$data = search_user($query, $limit, $offset, array('exclude' => $USER->get('id')));
$data['query'] = $query;
build_userlist_html($data, $page);
if ($page == 'myfriends') {
$data = search_friend($filter, $limit, $offset);
$data['filter'] = $filter;
}
else {
$data = search_user($query, $limit, $offset, array('exclude' => $USER->get('id')));
$data['query'] = $query;
}
$controlledgroups = count_records_sql("SELECT COUNT(g.id)
FROM {group} g
JOIN {group_member} gm ON (gm.group = g.id)
JOIN {grouptype_roles} gtr ON (gtr.grouptype = g.grouptype AND gtr.role = gm.role)
WHERE gm.member = ?
AND g.jointype = 'controlled'
AND (gm.role = 'admin' OR gtr.see_submitted_views = 1)
AND g.deleted = 0", array($USER->get('id')));
$invite = count_records_sql("SELECT COUNT(g.id)
FROM {group} g
JOIN {group_member} gm ON (gm.group = g.id)
WHERE gm.member = ?
AND g.jointype = 'invite'
AND gm.role = 'admin'
AND g.deleted = 0", array($USER->get('id')));
$admingroups = new StdClass;
$admingroups->controlled = $controlledgroups;
$admingroups->invite = $invite;
build_userlist_html($data, $page, $admingroups);
json_reply(false, array('data' => $data));
?>
......@@ -77,6 +77,15 @@ $string['hasbeeninvitedtojoin'] = 'has been invited to join this group';
$string['groupinvitesfrom'] = 'Invited to join:';
$string['requestedmembershipin'] = 'Requested membership in:';
$string['controlledmembership'] = 'Controlled Group membership';
$string['invitemembership'] = 'Invite only membership';
$string['changedgroupmembership'] = 'Control Groups membership has changed sucessfully.';
$string['changedgroupmembershipsubject'] = 'You Control Groups membership has changed';
$string['addedtogroupsmessage'] = "%s has added you to controlled group(s):\n\n%s\n\n";
$string['removedfromgroupsmessage'] = "%s has removed you from controlled group(s):\n\n%s\n\n";
$string['cantremoveuserisadmin'] = "Tutor cannot remove admins and other tutorsmembers.";
$string['cantremovemember'] = "Tutor cannot remove members.";
// Used to refer to all the members of a group - NOT a "member" group role!
$string['member'] = 'member';
$string['members'] = 'members';
......@@ -155,7 +164,8 @@ $string['groupsnotin'] = 'Groups I\'m not in';
$string['allgroups'] = 'All groups';
$string['allgroupmembers'] = 'All group members';
$string['trysearchingforgroups'] = 'Try %ssearching for groups%s to join!';
$string['nogroupsfound'] = 'No groups found :(';
$string['nogroupsfound'] = 'No groups found.';
$string['nogroups'] = 'No groups.';
$string['group'] = 'group';
$string['Group'] = 'Group';
$string['groups'] = 'groups';
......
......@@ -50,6 +50,7 @@ $string['description'] = 'Description';
$string['remove'] = 'Remove';
$string['Close'] = 'Close';
$string['Help'] = 'Help';
$string['applychanges'] = 'Apply changes';
$string['no'] = 'No';
$string['yes'] = 'Yes';
......
......@@ -485,4 +485,76 @@ function get_search_plugins() {
return $searchpluginoptions;
}
?>
/**
* Given a filter string and limits, return an array of matching friends.
*
* @param string The filter string
* @param integer How many results to return
* @param integer What result to start at (0 == first result)
* @return array A data structure containing results looking like ...
* $results = array(
* count => integer, // total number of results
* limit => integer, // how many results are returned
* offset => integer, // starting from which result
* results => array( // the result records
* array(
* id => integer, //user id
* ),
* array(...),
* ),
* );
*/
function search_friend($filter, $limit, $offset) {
global $USER;
$userid = $USER->get('id');
if (!in_array($filter, array('all','current','pending'))) {
throw new Exception();
}
$sql = array();
$count = 0;
if (in_array($filter, array('all', 'current'))) {
$count += count_records_sql('SELECT COUNT(usr1) FROM {usr_friend}
JOIN {usr} u1 ON (u1.id = usr1 AND u1.deleted = 0)
JOIN {usr} u2 ON (u2.id = usr2 AND u2.deleted = 0)
WHERE usr1 = ? OR usr2 = ?',
array($userid, $userid)
);
array_push($sql, 'SELECT usr2 AS id, 2 AS status FROM {usr_friend} WHERE usr1 = ?
');
array_push($sql, 'SELECT usr1 AS id, 2 AS status FROM {usr_friend} WHERE usr2 = ?
');
}
if (in_array($filter, array('all', 'pending'))) {
$count += count_records_sql('SELECT COUNT(owner) FROM {usr_friend_request}
JOIN {usr} u ON (u.id = requester AND u.deleted = 0)
WHERE owner = ?',
array($userid)
);
array_push($sql, 'SELECT requester AS id, 1 AS status FROM {usr_friend_request} WHERE owner = ?
');
}
$data = get_column_sql('SELECT f.id FROM (' . join('UNION ', $sql) . ') f
JOIN {usr} u ON (f.id = u.id AND u.deleted = 0)
ORDER BY status, firstname, lastname, u.id
LIMIT ?
OFFSET ?', array_merge(array_pad($values=array(), count($sql), $userid), array($limit, $offset)));
foreach ($data as &$result) {
$result = array('id' => $result);
}
return array(
'count' => $count,
'limit' => $limit,
'offset' => $offset,
'data' => $data,
);
}
......@@ -1373,7 +1373,7 @@ function get_users_data($userlist, $getviews=true) {
return $ordereddata;
}
function build_userlist_html(&$data, $page) {
function build_userlist_html(&$data, $page, $admingroups) {
if ($data['data']) {
$userlist = join(',', array_map(create_function('$u','return $u[\'id\'];'), $data['data']));
$userdata = get_users_data($userlist, $page == 'myfriends');
......@@ -1381,18 +1381,30 @@ function build_userlist_html(&$data, $page) {
$smarty = smarty_core();
$smarty->assign('data', isset($userdata) ? $userdata : null);
$smarty->assign('page', $page);
$smarty->assign('query', $data['query']);
if (isset($data['query'])) {
$smarty->assign('query', $data['query']);
$params = '?query=' . $data['query'];
$resultcounttextsingular = get_string('user', 'group');
$resultcounttextplural = get_string('users', 'group');
}
elseif (isset($data['filter'])) {
$smarty->assign('filter', $data['filter']);
$params = '?filter=' . $data['filter'];
$resultcounttextsingular = get_string('friend', 'group');
$resultcounttextplural = get_string('friends', 'group');
}
$smarty->assign('admingroups', $admingroups);
$data['tablerows'] = $smarty->fetch('user/userresults.tpl');
$pagination = build_pagination(array(
'id' => 'friendslist_pagination',
'url' => get_config('wwwroot') . 'user/' . $page . '.php?query=' . $data['query'],
'url' => get_config('wwwroot') . 'user/' . $page . '.php' . $params,
'jsonscript' => 'json/friendsearch.php',
'datatable' => 'friendslist',
'count' => $data['count'],
'limit' => $data['limit'],
'offset' => $data['offset'],
'resultcounttextsingular' => get_string('user', 'group'),
'resultcounttextplural' => get_string('users', 'group'),
'resultcounttextsingular' => $resultcounttextsingular,
'resultcounttextplural' => $resultcounttextplural,
'extradata' => array('page' => $page),
));
$data['pagination'] = $pagination['html'];
......
......@@ -1442,6 +1442,13 @@ ul.groupuserstatus {
width: 180px;
font-size: .9em;
}
#friendslist ul.actionlist div.groupbox{
position: relative;
display: none;
background: white;
border: 1px solid black;
z-index: 4;
}
ul.groupuserstatus li,
#friendslist ul.actionlist li {
list-style: none;
......
{include file="header.tpl"}
<div id="friendslistcontainer">
<div id="friendslistcontainer">
{$form}
<table id="friendslist" class="fullwidth listing">
<tbody>
......
{include file="header.tpl"}
<div id="friendslistcontainer">
{$form}
{if $users}
{if $results}
<table id="friendslist" class="fullwidth listing">
{foreach from=$users item=user}
<tr class="{cycle values='r0,r1'}">
{include file="user/user.tpl" user=$user page='myfriends'}
</tr>
{/foreach}
<tbody>
{$results.tablerows}
</tbody>
</table>
<div class="center">
{$pagination}
</div>
{else}