group.php 17.6 KB
Newer Older
1
2
<?php
/**
Francois Marier's avatar
Francois Marier committed
3
 * Mahara: Electronic portfolio, weblog, resume builder and social networking
4
 * Copyright (C) 2006-2008 Catalyst IT Ltd (http://www.catalyst.net.nz)
5
 *
Francois Marier's avatar
Francois Marier committed
6
7
8
9
 * 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.
10
 *
Francois Marier's avatar
Francois Marier committed
11
12
13
14
 * 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.
15
 *
Francois Marier's avatar
Francois Marier committed
16
17
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
20
 *
 * @package    mahara
 * @subpackage core
21
 * @author     Catalyst IT Ltd
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
23
 * @copyright  (C) 2006-2008 Catalyst IT Ltd http://catalyst.net.nz
24
25
26
27
28
29
30
 *
 */

defined('INTERNAL') || die();


/**
31
 * is a user allowed to leave a group? 
32
33
 * checks if they're the owner and the membership type
 *
34
 * @param object $group (corresponds to db record). if an id is given, record will be fetched.
35
36
 * @param int $userid (optional, will default to logged in user)
 */
37
function group_user_can_leave($group, $userid=null) {
38
39

    $userid = optional_userid($userid);
40
    
41
    if (is_numeric($group)) {
42
        if (!$group = get_record('group', 'id', $group, 'deleted', 0)) {
43
44
45
46
            return false;
        }
    }
    
47
    // TODO: disallow users from leaving if they are the only administrator in the group
48
    
49
    if ($group->jointype == 'controlled') {
50
51
52
53
54
55
        return false;
    }
    return true;
}

/**
56
 * removes a user from a group
Clare Lenihan's avatar
Clare Lenihan committed
57
 * removed view access given by the user to the group
58
 *
59
 * @param int $group id of group
60
61
 * @param int $user id of user to remove
 */
62
function group_remove_user($group, $userid) {    
63
    db_begin();
64
    delete_records('group_member', 'group', $group, 'member', $userid);
65
    delete_records_sql(
66
67
68
        'DELETE FROM {view_access_group}
        WHERE "group" = ?
        AND view IN (
69
70
71
72
73
74
            SELECT v.id
            FROM {view} v
            WHERE v.owner = ?
        )',
        array($group, $userid)
    );
75
    db_commit();
76
77
78
79
80
81

    require_once(get_config('docroot') . 'interaction/lib.php');
    $interactions = get_column('interaction_instance', 'id', 'group', $group);
    foreach ($interactions as $interaction) {
        interaction_instance_from_id($interaction)->interaction_remove_user($userid);
    }
82
83
84
}

/**
85
 * all groups the user is a member of
86
87
 * 
 * @param int userid (optional, defaults to $USER id) 
88
 * @return array of group db rows
89
 */
90
function get_member_groups($userid=0, $offset=0, $limit=0) {
91

92
93
    $userid = optional_userid($userid);

94
    return get_records_sql_array('SELECT g.id, g.name, g.description, g.jointype, g.owner, g.ctime, g.mtime, gm.ctime, gm.tutor, COUNT(v.view) AS hasviews
95
96
97
              FROM {group} g 
              JOIN {group_member} gm ON gm.group = g.id
              LEFT JOIN {view_access_group} v ON v.group = g.id
98
99
              WHERE g.owner != ? AND gm.member = ? AND g.deleted = ?
              GROUP BY 1, 2, 3, 4, 5, 6, 7, 8, 9', array($userid, $userid, 0), $offset, $limit);
100
101
102
}


103
/**
104
 * all groups the user has pending invites to
105
106
 * 
 * @param int userid (optional, defaults to $USER id)
107
 * @return array of group db rows
108
 */
109
function get_invited_groups($userid=0) {
110
111
112

    $userid = optional_userid($userid);

113
    return get_records_sql_array('SELECT g.*, gmi.ctime, gmi.reason
114
115
             FROM {group} g 
             JOIN {group_member_invite} gmi ON gmi.group = g.id
116
             WHERE gmi.member = ? AND g.deleted = ?', array($userid, 0));
117
118
119
}

/**
120
 * all groups the user has pending requests for 
121
122
 * 
 * @param int $userid (optional, defaults to $USER id)
123
 * @return array of group db rows
124
125
 */

126
function get_requested_group($userid=0) {
127
128
129

    $userid = optional_userid($userid);

130
    return get_records_sql_array('SELECT g.*, gmr.ctime, gmr.reason 
131
              FROM {group} g 
132
              JOIN {group_member_request} gmr ON gmr.group = g.id
133
              WHERE gmr.member = ? AND g.deleted = ?', array($userid, 0));
134
135
}

136

137
138
139
140
141
142
// constants for group membership type
define('GROUP_MEMBERSHIP_ADMIN', 1);
define('GROUP_MEMBERSHIP_STAFF', 2);
define('GROUP_MEMBERSHIP_OWNER', 4);
define('GROUP_MEMBERSHIP_TUTOR', 8);
define('GROUP_MEMBERSHIP_MEMBER', 16);
143

144

145
/**
146
 * Can a user access a given group?
147
 * 
148
 * @param mixed $group id of group or db record (object)
149
150
151
152
 * @param mixed $user optional (object or id), defaults to logged in user
 *
 * @returns constant access level or FALSE
 */
153
function user_can_access_group($group, $user=null) {
154
    log_warn("user_can_access_group is deprecated: please use group_user_access instead");
155
156
157
158
159
160
161
162
163
164
165
166
167

    if (empty($userid)) {
        global $USER;
        $user = $USER;
    }
    else if (is_int($user)) {
        $user = get_user($user);
    }
    else if (is_object($user) && !$user instanceof User) {
        $user = get_user($user->get('id'));
    }

    if (!$user instanceof User) {
168
        throw new InvalidArgumentException("not useful user arg given to user_can_access_group: $user");
169
170
    }

171
    if (is_int($group)) {
172
        $group = get_record('group', 'id', $group, 'deleted', 0);
173
174
    }

175
176
    if (!is_object($group)) {
        throw new InvalidArgumentException("not useful group arg given to user_can_access_group: $group");
177
178
    }

179
    $membertypes = 0;
180
    // admins/staff/owners can do whatever tutors can do
181
    if ($user->get('admin')) {
182
        $membertypes = GROUP_MEMBERSHIP_ADMIN | GROUP_MEMBERSHIP_TUTOR;
183
    }
184
    if ($user->get('staff') || $user->is_institutional_admin() || $user->is_institutional_staff()) {
185
        $membertypes = $membertypes | GROUP_MEMBERSHIP_STAFF | GROUP_MEMBERSHIP_TUTOR;
186
    }
187
    if ($group->owner == $user->get('id')) {
188
        $membertypes = $membertypes | GROUP_MEMBERSHIP_OWNER | GROUP_MEMBERSHIP_TUTOR;
189
190
    }

191
    if (!$membership = get_record('group_member', 'group', $group->id, 'member', $user->get('id'))) {
192
        return $membertypes;
193
194
195
    }

    if ($membership->tutor) {
196
        $membertypes = $membertypes | GROUP_MEMBERSHIP_TUTOR;
197
198
    }
    
199
    return ($membertypes | GROUP_MEMBERSHIP_MEMBER);
200
201
}

202
203
204
205
206
207
208
209
210
211
212
213
214
215
/**
 * Establishes what role a user has in a given group.
 *
 * If the user is not in the group, this returns false.
 *
 * @param mixed $groupid  ID of the group to check
 * @param mixed $userid   ID of the user to check. Defaults to the logged in 
 *                        user.
 * @return mixed          The role the user has in the group, or false if they 
 *                        have no role in the group
 */
function group_user_access($groupid, $userid=null) {
    // TODO: caching

216
217
218
    $groupid = (int)$groupid;

    if ($groupid == 0) {
219
220
221
222
223
224
225
        throw new InvalidArgumentException("group_user_access: group argument appears to be invalid: $groupid");
    }

    if (is_null($userid)) {
        global $USER;
        $userid = (int)$USER->get('id');
    }
226
227
228
    else {
        $userid = (int)$userid;
    }
229

230
    if ($userid == 0) {
231
232
233
        throw new InvalidArgumentException("group_user_access: user argument appears to be invalid: $userid");
    }

234
    return get_field('group_member', 'role', 'group', $groupid, 'member', $userid);
235
236
}

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
function group_user_can_edit_views($groupid, $userid=null) {
    $groupid = (int)$groupid;

    if ($groupid == 0) {
        throw new InvalidArgumentException("group_user_access: group argument appears to be invalid: $groupid");
    }

    if (is_null($userid)) {
        global $USER;
        $userid = (int)$USER->get('id');
    }
    else {
        $userid = (int)$userid;
    }

    if ($userid == 0) {
        throw new InvalidArgumentException("group_user_access: user argument appears to be invalid: $userid");
    }

    return get_field_sql('
        SELECT
            r.edit_views
        FROM
            {group_member} m
            INNER JOIN {group} g ON (m.group = g.id AND g.deleted = 0)
            INNER JOIN {grouptype_roles} r ON (g.grouptype = r.grouptype AND m.role = r.role)
        WHERE
            m.group = ?
            AND m.member = ?', array($groupid, $userid));
}

268
/**
269
 * function to add a member to a group
270
271
 * doesn't do any jointype checking, that should be handled by the caller
 *
272
 * @param int $groupid
273
274
 * @param int $userid
 */
275
function group_add_member($groupid, $userid) {
276
277
    $cm = new StdClass;
    $cm->member = $userid;
278
    $cm->group = $groupid;
279
280
    $cm->ctime =  db_format_timestamp(time());
    $cm->tutor = 0;
281
    insert_record('group_member', $cm);
Penny Leach's avatar
Penny Leach committed
282
    $user = optional_userobj($userid);
283
284
}

285
286
287
288
289
290
291
292
293
function group_has_members($groupid) {
    $sql = 'SELECT (
        (SELECT COUNT(*) FROM {group_member} WHERE "group" = ?)
        +
        (SELECT COUNT(*) FROM {group_member_request} WHERE "group" = ?)
    )';
    return count_records_sql($sql, array($groupid, $groupid));
}

294
295
296
297
function delete_group($groupid) {
    update_record('group', array('deleted' => 1), array('id' => $groupid));
}

298
299
300
301
302
303
304
305
306
307
308
309
310
/**
 * Returns a list of user IDs who are admins for a group
 *
 * @param int
 * @return array
 */
function group_get_admin_ids($group) {
    return (array)get_column_sql("SELECT member
        FROM {group_member}
        WHERE \"group\" = ?
        AND role = 'admin'", $group);
}

311
/**
312
 * Sets up groups for display in mygroups.php and find.php
313
 *
314
315
316
317
 * @param array $groups    Initial group data, including the current user's 
 *                         membership type in each group. See mygroups.php for
 *                         the query to build this information.
 * @param string $returnto Where forms generated for display should be told to return to
318
 */
319
function group_prepare_usergroups_for_display($groups, $returnto='mygroups') {
320
321
322
    if (!$groups) {
        return;
    }
323
324
325
326
327

    // Retrieve a list of all the group admins, for placing in each $group object
    $groupadmins = array();
    $groupids = array_map(create_function('$a', 'return $a->id;'), $groups);
    if ($groupids) {
328
329
330
        $groupadmins = (array)get_records_sql_array('SELECT "group", member
            FROM {group_member}
            WHERE "group" IN (' . implode(',', db_array_to_ph($groupids)) . ")
331
            AND role = 'admin'", $groupids);
332
333
    }

334
335
    $i = 0;
    foreach ($groups as $group) {
336
337
338
339
340
341
        $group->admins = array();
        foreach ($groupadmins as $admin) {
            if ($admin->group == $group->id) {
                $group->admins[] = $admin->member;
            }
        }
342
        $group->description = str_shorten($group->description, 100, true);
343
        if ($group->membershiptype == 'member') {
344
345
346
347
348
349
350
351
352
            $group->canleave = group_user_can_leave($group->id);
        }
        else if ($group->jointype == 'open') {
            $group->groupjoin = pieform(array(
                'name' => 'joingroup' . $i++,
                'successcallback' => 'joingroup_submit',
                'elements' => array(
                    'join' => array(
                        'type' => 'submit',
Clare Lenihan's avatar
Clare Lenihan committed
353
                        'value' => get_string('joingroup', 'group')
354
355
356
357
358
359
360
361
                    ),
                    'group' => array(
                        'type' => 'hidden',
                        'value' => $group->id
                    )
                )
            ));
        }
362
        else if ($group->membershiptype == 'invite') {
363
364
365
366
367
368
369
           $group->invite = pieform(array(
               'name'     => 'invite' . $i++,
               'renderer' => 'oneline',
               'successcallback' => 'group_invite_submit',
               'elements' => array(
                    'accept' => array(
                        'type'  => 'submit',
Clare Lenihan's avatar
Clare Lenihan committed
370
                        'value' => get_string('acceptinvitegroup', 'group')
371
372
373
                    ),
                    'decline' => array(
                        'type'  => 'submit',
Clare Lenihan's avatar
Clare Lenihan committed
374
                        'value' => get_string('declineinvitegroup', 'group')
375
376
377
378
379
380
381
382
383
384
385
386
                    ),
                    'group' => array(
                        'type' => 'hidden',
                        'value' => $group->id
                    ),
                    'returnto' => array(
                        'type' => 'hidden',
                        'value' => $returnto
                    )
                )
            ));
        }
387
        else if ($group->membershiptype == 'admin' && $group->requests > 1) {
388
389
390
391
392
393
394
395
            $group->requests = array($group->requests);
        }
    }
}

function joingroup_submit(Pieform $form, $values) {
    global $SESSION, $USER;
    group_add_member($values['group'], $USER->get('id'));
Clare Lenihan's avatar
Clare Lenihan committed
396
    $SESSION->add_ok_msg(get_string('joinedgroup', 'group'));
397
398
399
400
401
402
403
404
405
    redirect('/group/view.php?id=' . $values['group']);
}

function group_invite_submit(Pieform $form, $values) {
    global $SESSION, $USER;
    if (get_record('group_member_invite', 'member', $USER->get('id'), 'group', $values['group'])) {
        delete_records('group_member_invite', 'group', $values['group'], 'member', $USER->get('id'));
        if (isset($values['accept'])) {
            group_add_member($values['group'], $USER->get('id'));
Clare Lenihan's avatar
Clare Lenihan committed
406
            $SESSION->add_ok_msg(get_string('groupinviteaccepted', 'group'));
407
408
409
            redirect('/group/view.php?id=' . $values['group']);
        }
        else {
Clare Lenihan's avatar
Clare Lenihan committed
410
            $SESSION->add_ok_msg(get_string('groupinvitedeclined', 'group'));
411
412
413
414
415
            redirect($values['returnto'] == 'find' ? '/group/find.php' : '/group/mygroups.php');
        }
    }
}

416
417
418
419
420
421
422
423
424
425
function group_get_role_info($groupid) {
    $roles = get_records_sql_assoc('SELECT role, edit_views, see_submitted_views, gr.grouptype FROM {grouptype_roles} gr
        INNER JOIN {group} g ON g.grouptype = gr.grouptype
        WHERE g.id = ?', array($groupid));
    foreach ($roles as $role) {
        $role->display = get_string($role->role, 'grouptype.'.$role->grouptype);
    }
    return $roles;
}

426
427
428
429
430
431
432
433
434
function group_get_membersearch_data($group, $query, $offset, $limit) {
    $results = get_group_user_search_results($group, $query, $offset, $limit);

    $params = array();
    if (!empty($query)) {
        $params[] = 'query=' . $query;
    }
    $params[] = 'limit=' . $limit;
    $searchurl = get_config('wwwroot') . 'group/view.php?' . join('&amp;', $params);
435
    $results['cdata'] = array_chunk($results['data'], 2);
436
    $results['roles'] = group_get_role_info($group);
437
438
439
440
    $smarty = smarty_core();
    $smarty->assign_by_ref('results', $results);
    $smarty->assign('searchurl', $searchurl);
    $smarty->assign('pagebaseurl', $searchurl);
441
442
    $smarty->assign('caneditroles', group_user_access($group) == 'admin');
    $smarty->assign('group', $group);
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
    $html = $smarty->fetch('group/membersearchresults.tpl');

    $pagination = build_pagination(array(
        'id' => 'member_pagination',
        'class' => 'center',
        'url' => get_config('wwwroot') . 'group/view.php?id=' . $group,
        'count' => $results['count'],
        'limit' => $limit,
        'offset' => $offset,
        'datatable' => 'membersearchresults',
        'jsonscript' => 'group/membersearchresults.php',
        'firsttext' => '',
        'previoustext' => '',
        'nexttext' => '',
        'lasttext' => '',
        'numbersincludefirstlast' => false,
459
460
        'resultcounttextsingular' => get_string('member', 'group'),
        'resultcounttextplural' => get_string('members', 'group'),
461
462
463
464
465
    ));

    return array($html, $pagination, $results['count'], $offset);
}

466
467
468
469
470
/**
 * Where is the syntax error?
 */
abstract class GroupType {

471
472
473
    public function install() {
        $classname = get_class($this);
        $type = strtolower(substr($classname, strlen('GroupType')));
474
        $assessingroles = $this->get_view_assessing_roles();
475
476
        insert_record('grouptype', (object) array(
            'name' => $type,
477
            'submittableto' => !empty($assessingroles),
478
479
        ));
        $roles = $this->get_roles();
480
        if (!in_array('admin', $roles)) {
481
482
            $roles[] = 'admin';
        }
483
        $editingroles = $this->get_view_editing_roles();
484
485
486
487
        foreach ($roles as $r) {
            insert_record('grouptype_roles', (object) array(
                'grouptype' => $type,
                'role' => $r,
488
489
                'edit_views' => (int)in_array($r, $editingroles),
                'see_submitted_views' => (int)in_array($r, $assessingroles),
490
491
492
493
            ));
        }
    }

494
495
496
497
498
499
500
501
502
503
    public static abstract function allowed_join_types();

    /**
     * Returns whether the currently logged in user can create a group of this 
     * grouptype
     */
    public static function can_be_created_by_user() {
        return true;
    }

504
    /**
505
     * Returns the roles this group type implements
506
     */
507
    public static abstract function get_roles();
508

509
510
    public static abstract function get_view_editing_roles();

511
    public static abstract function get_view_assessing_roles();
512

513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
}

/**
 * Returns a list of available grouptypes
 */
function group_get_grouptypes() {
    static $grouptypes = null;

    if (is_null($grouptypes)) {
        $grouptypes = array();
        $grouptypedir = get_config('libroot') . 'grouptype/';

        if ($dh = opendir($grouptypedir)) {
            while (false !== ($file = readdir($dh))) {
                if (!preg_match('/^[a-zA-Z0-9-]+\.php$/', $file)) {
                    continue;
                }
                if (is_file("$grouptypedir$file")) {
                    $grouptypes[] = substr($file, 0, -4);
                }
            }
        }
    }

    return $grouptypes;
}

540
541
542
543
544
545
546
547
548
549
550
551
552
553
554

function can_assess_submitted_views($userid, $groupid) {
    return get_field_sql('
        SELECT
            r.see_submitted_views
        FROM
            {group_member} m 
            INNER JOIN {group} g ON (m.group = g.id AND g.deleted = 0)
            INNER JOIN {grouptype_roles} r ON (g.grouptype = r.grouptype AND r.role = m.role)
        WHERE
            m.member = ?
            AND m.group = ?', array($userid, $groupid));
}


555
?>