group.php 50.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
5
 * Copyright (C) 2006-2009 Catalyst IT Ltd and others; see:
 *                         http://wiki.mahara.org/Contributors
6
 *
Francois Marier's avatar
Francois Marier committed
7
8
9
10
 * 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.
11
 *
Francois Marier's avatar
Francois Marier committed
12
13
14
15
 * 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.
16
 *
Francois Marier's avatar
Francois Marier committed
17
18
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19
20
21
 *
 * @package    mahara
 * @subpackage core
22
 * @author     Catalyst IT Ltd
23
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
24
 * @copyright  (C) 2006-2009 Catalyst IT Ltd http://catalyst.net.nz
25
26
27
28
29
 *
 */

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

30
// Role related functions
31

32
33
34
35
36
37
38
39
40
41
42
/**
 * 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
 */
43
function group_user_access($groupid, $userid=null, $refresh=null) {
44
    static $result;
45

46
47
48
49
    if (!is_logged_in()) {
        return false;
    }

50
51
    $groupid = group_param_groupid($groupid);
    $userid  = group_param_userid($userid);
52

53
    if (isset($result[$groupid][$userid]) && !isset($refresh)) {
54
55
56
57
        return $result[$groupid][$userid];
    }

    return $result[$groupid][$userid] = get_field('group_member', 'role', 'group', $groupid, 'member', $userid);
58
59
}

60
61
62
63
64
65
/**
 * Returns whether the given user is the only administrator in the given group.
 *
 * If the user isn't in the group, or they're not an admin, or there is another admin, false 
 * is returned.
 *
66
67
 * @param int $groupid The ID of the group to check
 * @param int $userid  The ID of the user to check
68
69
 * @returns boolean
 */
70
function group_is_only_admin($groupid, $userid=null) {
71
    static $result;
72

73
74
    $groupid = group_param_groupid($groupid);
    $userid  = group_param_userid($userid);
75

76
77
78
79
80
81
    if (isset($result[$groupid][$userid])) {
        return $result[$groupid][$userid];
    }

    return $result[$groupid][$userid] = (group_user_access($groupid, $userid) == 'admin'
        && count_records('group_member', 'group', $groupid, 'role', 'admin') == 1);
82
83
84
85
86
87
88
89
90
}

/**
 * Returns whether the given user is allowed to change their role to the 
 * requested role in the given group.
 *
 * This function is checking whether _role changes_ are allowed, not if a user 
 * is allowed to be added to a group.
 *
91
92
 * @param int $groupid The ID of the group to check
 * @param int $userid  The ID of the user to check
93
94
95
 * @param string $role The role the user wishes to switch to
 * @returns boolean
 */
96
function group_can_change_role($groupid, $userid, $role) {
97
98
    $groupid = group_param_groupid($groupid);
    $userid  = group_param_userid($userid);
99
100

    if (!group_user_access($groupid, $userid)) {
101
102
103
104
        return false;
    }

    // Sole remaining admins can never change their role
105
    if (group_is_only_admin($groupid, $userid)) {
106
107
108
        return false;
    }

109
110
111
112
    // admin role permissions check
    if ($role == 'admin') {
        $group = group_current_group();
        safe_require('grouptype', $group->grouptype);
113
        return call_static_method('GroupType' . $group->grouptype, 'can_become_admin', $userid);
114
    }
115
116
117
118
119
120
121

    return true;
}

/**
 * Changes a user role in a group, if this is allowed.
 *
122
123
 * @param int $groupid The ID of the group
 * @param int $userid  The ID of the user whose role needs changing
124
125
126
127
128
 * @param string $role The role the user wishes to switch to
 * @throws AccessDeniedException If the specified role change is not allowed. 
 *                               Check with group_can_change_role first if you 
 *                               need to.
 */
129
130
131
function group_change_role($groupid, $userid, $role) {
    // group_can_change_role checks whether the group and user parameters are valid
    if (!group_can_change_role($groupid, $userid, $role)) {
132
        throw new AccessDeniedException(get_string('usercannotchangetothisrole', 'group'));
133
134
    }

135
    set_field('group_member', 'role', $role, 'group', $groupid, 'member', $userid);
136
137
}

138
139
140
141
142
143
144
/**
 * Returns whether a user is allowed to edit views in a given group
 *
 * @param int $groupid The ID of the group
 * @param int $userid The ID of the user
 * @returns boolean
 */
145
function group_user_can_edit_views($groupid, $userid=null) {
146
147
148
149
150
151
    // root user can always do whatever it wants
    $sysuser = get_record('usr', 'username', 'root');
    if ($sysuser->id == $userid) {
        return true;
    }

152
153
154
155
    if (!is_logged_in()) {
        return false;
    }

156
157
    $groupid = group_param_groupid($groupid);
    $userid  = group_param_userid($userid);
158
159
160
161
162
163
164
165
166
167
168
169
170

    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));
}

171
172
173
174
175
176
177
178
179
/**
 * Returns whether a user is allowed to assess views that have been submitted 
 * to the given group.
 *
 * @param int $groupid ID of group
 * @param int $userid  ID of user
 * @return boolean
 */
function group_user_can_assess_submitted_views($groupid, $userid) {
180
181
    $groupid = group_param_groupid($groupid);
    $userid  = group_param_userid($userid);
182
183
184
185
186
187
188
189
190
191
192
193
194

    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));
}

195
196
// Functions for creation/deletion of groups, and adding/removing users to groups

197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/**
 * Creates a group.
 *
 * All group creation should be done through this function, as the 
 * implementation of group creation may change over time.
 *
 * @param array $data Data required to create the group. The following 
 * key/value pairs can be specified:
 *
 * - name: The group name [required, must be unique]
 * - description: The group description [optional, defaults to empty string]
 * - grouptype: The grouptype for the new group. Must be an installed grouptype.
 * - jointype: The jointype for the new group. One of 'open', 'invite', 
 *             'request' or 'controlled'
 * - ctime: The unix timestamp of the time the group will be recorded as having 
 *          been created. Defaults to the current time.
 * - members: Array of users who should be in the group, structured like this:
 *            array(
 *                userid => role,
 *                userid => role,
 *                ...
 *            )
219
 * @return int The ID of the created group
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
 */
function group_create($data) {
    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");
    }

    if (!isset($data['name'])) {
        throw new InvalidArgumentException("group_create: must specify a name for the group");
    }

    if (!isset($data['grouptype']) || !in_array($data['grouptype'], group_get_grouptypes())) {
        throw new InvalidArgumentException("group_create: grouptype specified must be an installed grouptype");
    }

235
236
    safe_require('grouptype', $data['grouptype']);

237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
    if (isset($data['jointype'])) {
        if (!in_array($data['jointype'], call_static_method('GroupType' . $data['grouptype'], 'allowed_join_types'))) {
            throw new InvalidArgumentException("group_create: jointype specified is not allowed by the grouptype specified");
        }
    }
    else {
        throw new InvalidArgumentException("group_create: jointype specified must be one of the valid join types");
    }

    if (!isset($data['ctime'])) {
        $data['ctime'] = time();
    }
    $data['ctime'] = db_format_timestamp($data['ctime']);

    if (!is_array($data['members']) || count($data['members']) == 0) {
        throw new InvalidArgumentException("group_create: at least one member must be specified for adding to the group");
    }

255
    $data['public'] = (isset($data['public'])) ? intval($data['public']) : 0;
256
    $data['usersautoadded'] = (isset($data['usersautoadded'])) ? intval($data['usersautoadded']) : 0;
257

258
259
260
261
262
    db_begin();

    $id = insert_record(
        'group',
        (object) array(
263
264
265
            'name'           => $data['name'],
            'description'    => $data['description'],
            'grouptype'      => $data['grouptype'],
266
            'category'       => $data['category'],
267
268
269
270
271
            'jointype'       => $data['jointype'],
            'ctime'          => $data['ctime'],
            'mtime'          => $data['ctime'],
            'public'         => $data['public'],
            'usersautoadded' => $data['usersautoadded'],
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
        ),
        'id',
        true
    );

    foreach ($data['members'] as $userid => $role) {
        insert_record(
            'group_member',
            (object) array(
                'group'  => $id,
                'member' => $userid,
                'role'   => $role,
                'ctime'  => $data['ctime'],
            )
        );
    }

Richard Mansfield's avatar
Richard Mansfield committed
289
290
    // Copy views for the new group
    $templates = get_column('view_autocreate_grouptype', 'view', 'grouptype', $data['grouptype']);
291
292
293
294
295
    $templates = get_records_sql_array("
        SELECT v.id, v.title, v.description 
        FROM {view} v
        INNER JOIN {view_autocreate_grouptype} vag ON vag.view = v.id
        WHERE vag.grouptype = 'standard'", array());
Richard Mansfield's avatar
Richard Mansfield committed
296
297
    if ($templates) {
        require_once(get_config('libroot') . 'view.php');
298
299
        foreach ($templates as $template) {
            list($view) = View::create_from_template(array(
Richard Mansfield's avatar
Richard Mansfield committed
300
                'group'       => $id,
301
302
303
304
                'title'       => $template->title,
                'description' => $template->description,
            ), $template->id);
            $view->set_access(array(array(
Richard Mansfield's avatar
Richard Mansfield committed
305
306
307
308
309
310
311
312
313
                'type'      => 'group',
                'id'        => $id,
                'startdate' => null,
                'stopdate'  => null,
                'role'      => null
            )));
        }
    }

Richard Mansfield's avatar
Richard Mansfield committed
314
    $data['id'] = $id;
315
    // install the homepage
316
    if ($t = get_record('view', 'type', 'grouphomepage', 'template', 1, 'owner', 0)) {
317
318
        require_once('view.php');
        $template = new View($t->id, (array)$t);
319
        list($homepage) = View::create_from_template(array(
320
321
322
323
324
325
            'group' => $id,
            'title' => $template->get('title'),
            'description' => $template->get('description'),
            'type' => 'grouphomepage',
        ), $t->id, 0, false);
    }
326
327
328
329
    insert_record('view_access', (object) array(
        'view' => $homepage->get('id'),
        'accesstype' => $data['public'] ? 'public' : 'loggedin',
    ));
Richard Mansfield's avatar
Richard Mansfield committed
330
    handle_event('creategroup', $data);
331
    db_commit();
332
333

    return $id;
334
335
}

336
337
338
339
340
341
342
343
344
345
346
347
/**
 * Deletes a group.
 *
 * All group deleting should be done through this function, even though it is 
 * simple. What is required to perform group deletion may change over time.
 *
 * @param int $groupid The group to delete
 *
 * {{@internal Maybe later we can have a group_can_be_deleted function if 
 * necessary}}
 */
function group_delete($groupid) {
348
    $groupid = group_param_groupid($groupid);
349
350
351
352
353
354
355
356
357
    update_record('group',
        array(
            'deleted' => 1,
            'name' => get_field('group', 'name', 'id', $groupid) . '.deleted.' . time(),
        ),
        array(
            'id' => $groupid,
        )
    );
358
359
}

360
/**
361
362
363
364
365
 * Adds a member to a group.
 *
 * Doesn't do any jointype checking, that should be handled by the caller.
 *
 * TODO: it should though. We should probably have group_user_can_be_added
366
 *
367
 * @param int $groupid
368
 * @param int $userid
Richard Mansfield's avatar
Richard Mansfield committed
369
 * @param string $role
370
 */
371
function group_add_user($groupid, $userid, $role=null) {
372
373
374
    $groupid = group_param_groupid($groupid);
    $userid  = group_param_userid($userid);

375
376
377
378
    $gm = new StdClass;
    $gm->member = $userid;
    $gm->group = $groupid;
    $gm->ctime =  db_format_timestamp(time());
Richard Mansfield's avatar
Richard Mansfield committed
379
380
381
    if (!$role) {
        $role = get_field_sql('SELECT gt.defaultrole FROM {grouptype} gt, {group} g WHERE g.id = ? AND g.grouptype = gt.name', array($groupid));
    }
382
    $gm->role = $role;
383
384

    db_begin();
385
    insert_record('group_member', $gm);
386
    delete_records('group_member_request', 'group', $groupid, 'member', $userid);
387
    handle_event('userjoinsgroup', $gm);
388
    db_commit();
389
390
}

391
392
393
394
395
396
397
398
399
/**
 * Checks whether a user is allowed to leave a group.
 *
 * This checks things like if they're the owner and the group membership type
 *
 * @param mixed $group  DB record or ID of group to check
 * @param int   $userid (optional, will default to logged in user)
 */
function group_user_can_leave($group, $userid=null) {
400
    global $USER;
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
    static $result;

    $userid = optional_userid($userid);

    if (is_numeric($group)) {
        if (!$group = get_record('group', 'id', $group, 'deleted', 0)) {
            return false;
        }
    }

    // Return cached value if we have it
    if (isset($result[$group->id][$userid])) {
        return $result[$group->id][$userid];
    }

416
    if ($group->jointype == 'controlled' && group_user_access($group->id, $USER->get('id')) != 'admin') {
417
418
419
420
421
422
423
424
425
426
427
        return ($result[$group->id][$userid] = false);
    }

    if (group_is_only_admin($group->id, $userid)) {
        return ($result[$group->id][$userid] = false);
    }

    return ($result[$group->id][$userid] = true);
}

/**
428
 * Removes a user from a group.
429
 *
430
431
 * @param int $groupid ID of group
 * @param int $userid  ID of user to remove
432
 */
433
function group_remove_user($groupid, $userid=null, $force=false) {
434
    // group_user_can_leave checks the validity of groupid and userid
435
    if (!$force && !group_user_can_leave($groupid, $userid)) {
436
437
        throw new AccessDeniedException(get_string('usercantleavegroup', 'group'));
    }
438
    delete_records('group_member', 'group', $groupid, 'member', $userid);
439
440

    require_once(get_config('docroot') . 'interaction/lib.php');
441
    $interactions = get_column('interaction_instance', 'id', 'group', $groupid);
442
443
444
445
446
    foreach ($interactions as $interaction) {
        interaction_instance_from_id($interaction)->interaction_remove_user($userid);
    }
}

447
448
449
450
451
452
453
/**
 * Invite a user to a group.
 *
 * @param object $group group
 * @param object $userid  User to invite
 * @param object $userfrom  User sending the invitation
 */
454
function group_invite_user($group, $userid, $userfrom, $role='member', $delay=null) {
455
456
457
458
459
460
461
462
463
464
    $user = optional_userobj($userid);

    $data = new StdClass;
    $data->group = $group->id;
    $data->member= $user->id;
    $data->ctime = db_format_timestamp(time());
    $data->role = $role;
    ensure_record_exists('group_member_invite', $data, $data);
    $lang = get_user_language($user->id);
    require_once('activity.php');
465
    $activitydata = array(
466
467
468
469
470
        'users'   => array($user->id),
        'subject' => get_string_from_language($lang, 'invitetogroupsubject', 'group'),
        'message' => get_string_from_language($lang, 'invitetogroupmessage', 'group', display_name($userfrom, $user), $group->name),
        'url'     => get_config('wwwroot') . 'group/view.php?id=' . $group->id,
        'urltext' => $group->name,
471
472
    );
    activity_occurred('maharamessage', $activitydata, null, null, $delay);
473
474
}

475
476
// Pieforms for various operations on groups

477
/**
478
 * Form for users to join a given group
479
 */
480
function group_get_join_form($name, $groupid, $returnto='view') {
481
482
483
    return pieform(array(
        'name' => $name,
        'successcallback' => 'joingroup_submit',
484
        'autofocus' => false,
485
486
487
488
489
490
491
492
        'elements' => array(
            'join' => array(
                'type' => 'submit',
                'value' => get_string('joingroup', 'group')
            ),
            'group' => array(
                'type' => 'hidden',
                'value' => $groupid
493
494
495
496
497
            ),
            'returnto' => array(
                'type' => 'hidden',
                'value' => $returnto
            ),
498
499
500
501
        )
    ));
}

502
503
504
/**
 * Form for accepting/declining a group invite
 */
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
function group_get_accept_form($name, $groupid, $returnto) {
    return pieform(array(
       'name'     => $name,
       'renderer' => 'oneline',
       'successcallback' => 'group_invite_submit',
       'elements' => array(
            'accept' => array(
                'type'  => 'submit',
                'value' => get_string('acceptinvitegroup', 'group')
            ),
            'decline' => array(
                'type'  => 'submit',
                'value' => get_string('declineinvitegroup', 'group')
            ),
            'group' => array(
                'type' => 'hidden',
                'value' => $groupid
            ),
            'returnto' => array(
                'type' => 'hidden',
                'value' => $returnto
            )
        )
    ));
}

531
532
533
/**
 * Form for adding a user to a group
 */
534
function group_get_adduser_form($userid, $groupid) {
535
    return pieform(array(
536
        'name'                => 'adduser' . $userid,
537
        'successcallback'     => 'group_adduser_submit',
538
539
540
541
542
543
544
545
546
547
548
549
        'renderer'            => 'div',
        'elements'            => array(
            'group' => array(
                'type'    => 'hidden',
                'value' => $groupid,
            ),
            'member' => array(
                'type'  => 'hidden',
                'value' => $userid,
            ),
            'submit' => array(
                'type'  => 'submit',
550
                'value' => get_string('add') . ' ' . display_name($userid),
551
552
553
554
555
            ),
        ),
    ));
}

556
557
558
/**
 * Form for removing a user from a group
 */
559
function group_get_removeuser_form($userid, $groupid) {
560
    require_once('pieforms/pieform.php');
561
562
563
564
565
566
567
568
569
570
571
572
573
574
    return pieform(array(
        'name'                => 'removeuser' . $userid,
        'validatecallback'    => 'group_removeuser_validate',
        'successcallback'     => 'group_removeuser_submit',
        'renderer'            => 'oneline',
        'elements'            => array(
            'group' => array(
                'type'    => 'hidden',
                'value' => $groupid,
            ),
            'member' => array(
                'type'  => 'hidden',
                'value' => $userid,
            ),
575
            'removeuser' => array(
576
577
578
579
580
581
582
                'type'  => 'submit',
                'value' => get_string('removefromgroup', 'group'),
            ),
        ),
    ));
}

583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
/**
 * Form for denying request (request jointype group)
 */
function group_get_denyuser_form($userid, $groupid) {
    require_once('pieforms/pieform.php');
    return pieform(array(
        'name'                => 'denyuser' . $userid,
        'successcallback'     => 'group_denyuser_submit',
        'renderer'            => 'oneline',
        'elements'            => array(
            'group' => array(
                'type'    => 'hidden',
                'value' => $groupid,
            ),
            'member' => array(
                'type'  => 'hidden',
                'value' => $userid,
            ),
            'denyuser' => array(
                'type'  => 'submit',
                'value' => get_string('declinerequest', 'group'),
            ),
        ),
    ));
}

609
// Functions for handling submission of group related forms
610
611
612

function joingroup_submit(Pieform $form, $values) {
    global $SESSION, $USER;
613
    group_add_user($values['group'], $USER->get('id'));
Clare Lenihan's avatar
Clare Lenihan committed
614
    $SESSION->add_ok_msg(get_string('joinedgroup', 'group'));
615
616
617
618
619
620
621
    if (substr($values['returnto'], 0, 1) == '/') {
        $next = $values['returnto'];
    }
    else {
        $next = '/group/view.php?id=' . $values['group'];
    }
    redirect($next);
622
623
624
625
}

function group_invite_submit(Pieform $form, $values) {
    global $SESSION, $USER;
626
627
    $inviterecord = get_record('group_member_invite', 'member', $USER->get('id'), 'group', $values['group']);
    if ($inviterecord) {
628
629
        delete_records('group_member_invite', 'group', $values['group'], 'member', $USER->get('id'));
        if (isset($values['accept'])) {
630
            group_add_user($values['group'], $USER->get('id'), $inviterecord->role);
Clare Lenihan's avatar
Clare Lenihan committed
631
            $SESSION->add_ok_msg(get_string('groupinviteaccepted', 'group'));
632
633
634
635
636
637
638
            if (substr($values['returnto'], 0, 1) == '/') {
                $next = $values['returnto'];
            }
            else {
                $next = '/group/view.php?id=' . $values['group'];
            }
            redirect($next);
639
640
        }
        else {
Clare Lenihan's avatar
Clare Lenihan committed
641
            $SESSION->add_ok_msg(get_string('groupinvitedeclined', 'group'));
642
643
644
645
646
            redirect($values['returnto'] == 'find' ? '/group/find.php' : '/group/mygroups.php');
        }
    }
}

647
function group_adduser_submit(Pieform $form, $values) {
648
649
650
651
652
653
    global $SESSION;
    $group = (int)$values['group'];
    if (group_user_access($group) != 'admin') {
        $SESSION->add_error_msg(get_string('accessdenied', 'error'));
        redirect('/group/members.php?id=' . $group . '&membershiptype=request');
    }
654
    group_add_user($group, $values['member']);
655
656
657
658
659
660
661
    $SESSION->add_ok_msg(get_string('useradded', 'group'));
    if (count_records('group_member_request', 'group', $group)) {
        redirect('/group/members.php?id=' . $group . '&membershiptype=request');
    }
    redirect('/group/members.php?id=' . $group);
}

662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
/**
 * Denying request (request jointype group)
 */
function group_denyuser_submit(Pieform $form, $values) {
    global $SESSION;
    $group = (int)$values['group'];
    if (group_user_access($group) != 'admin') {
        $SESSION->add_error_msg(get_string('accessdenied', 'error'));
        redirect('/group/members.php?id=' . $group . '&membershiptype=request');
    }
    delete_records('group_member_request', 'group', $values['group'], 'member', $values['member']);
    $SESSION->add_ok_msg(get_string('declinerequestsuccess', 'group'));
    if (count_records('group_member_request', 'group', $group)) {
        redirect('/group/members.php?id=' . $group . '&membershiptype=request');
    }
    redirect('/group/members.php?id=' . $group);
}

680
681
682
683
684
685
686
function group_removeuser_validate(Pieform $form, $values) {
    global $user, $group, $SESSION;
    if (!group_user_can_leave($values['group'], $values['member'])) {
        $form->set_error('submit', get_string('usercantleavegroup', 'group'));
    }
}

687
688
689
690
691
692
693
694
695
696
697
698
function group_removeuser_submit(Pieform $form, $values) {
    global $SESSION;
    $group = (int)$values['group'];
    if (group_user_access($group) != 'admin') {
        $SESSION->add_error_msg(get_string('accessdenied', 'error'));
        redirect('/group/members.php?id=' . $group);
    }
    group_remove_user($group, $values['member']);
    $SESSION->add_ok_msg(get_string('userremoved', 'group'));
    redirect('/group/members.php?id=' . $group);
}

699
700
701
702
703
704
/**
 * Form for submitting views to a group
 */
function group_view_submission_form($groupid, $viewdata) {
    $options = array();
    foreach ($viewdata as $view) {
705
706
707
708
709
710
        if (empty($view->submittedgroup) && empty($view->submittedhost)) {
            $options[$view->id] = $view->title;
        }
    }
    if (empty($options)) {
        return;
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
    }
    return pieform(array(
        'name' => 'group_view_submission_form_' . $groupid,
        'method' => 'post',
        'renderer' => 'oneline',
        'autofocus' => false,
        'successcallback' => 'group_view_submission_form_submit',
        'elements' => array(
            'text1' => array(
                'type' => 'html', 'value' => get_string('submit', 'group') . ' ',
            ),
            'options' => array(
                'type' => 'select',
                'collapseifoneoption' => false,
                'options' => $options,
            ),
            'text2' => array(
                'type' => 'html',
                'value' => get_string('forassessment', 'view'),
            ),
            'submit' => array(
                'type' => 'submit',
                'value' => get_string('submit')
            ),
            'group' => array(
                'type' => 'hidden',
                'value' => $groupid
738
739
740
741
742
            ),
            'returnto' => array(
                'type' => 'hidden',
                'value' => get_config('wwwroot') . 'group/view.php?id=' . $groupid,
            ),
743
744
745
746
747
        ),
    ));
}

function group_view_submission_form_submit(Pieform $form, $values) {
748
    redirect('/view/submit.php?id=' . $values['options'] . '&group=' . $values['group'] . '&returnto=group');
749
750
}

751
// Miscellaneous group related functions
752
753
754
755
756
757
758
759

/**
 * Returns a list of user IDs who are admins for a group
 *
 * @param int ID of group
 * @return array
 */
function group_get_admin_ids($groupid) {
Francois Marier's avatar
Francois Marier committed
760
    return (array)get_column_sql("SELECT \"member\"
761
762
        FROM {group_member}
        WHERE \"group\" = ?
Francois Marier's avatar
Francois Marier committed
763
        AND \"role\" = 'admin'", $groupid);
764
765
766
767
768
769
770
771
}

/**
 * Gets information about what the roles in a given group are able to do
 *
 * @param int $groupid ID of group to get role information for
 * @return array
 */
772
function group_get_role_info($groupid) {
Francois Marier's avatar
Francois Marier committed
773
    $roles = get_records_sql_assoc('SELECT "role", edit_views, see_submitted_views, gr.grouptype FROM {grouptype_roles} gr
774
775
776
777
        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);
778
        $role->name = $role->role;
779
780
781
782
    }
    return $roles;
}

783
784
785
786
787
788
function group_get_default_artefact_permissions($groupid) {
    $type = get_field('group', 'grouptype', 'id', $groupid);
    safe_require('grouptype', $type);
    return call_static_method('GroupType' . $type, 'default_artefact_rolepermissions');
}

789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
/**
 * Sets up groups for display in mygroups.php and find.php
 *
 * @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
 */
function group_prepare_usergroups_for_display($groups, $returnto='mygroups') {
    if (!$groups) {
        return;
    }

    // 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) {
Francois Marier's avatar
Francois Marier committed
806
        $groupadmins = get_records_sql_array('SELECT "group", "member"
807
808
            FROM {group_member}
            WHERE "group" IN (' . implode(',', db_array_to_ph($groupids)) . ")
Francois Marier's avatar
Francois Marier committed
809
            AND \"role\" = 'admin'", $groupids);
810
811
812
        if (!$groupadmins) {
            $groupadmins = array();
        }
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
    }

    $i = 0;
    foreach ($groups as $group) {
        $group->admins = array();
        foreach ($groupadmins as $admin) {
            if ($admin->group == $group->id) {
                $group->admins[] = $admin->member;
            }
        }
        if ($group->membershiptype == 'member') {
            $group->canleave = group_user_can_leave($group->id);
        }
        else if ($group->jointype == 'open') {
            $group->groupjoin = group_get_join_form('joingroup' . $i++, $group->id);
        }
        else if ($group->membershiptype == 'invite') {
            $group->invite = group_get_accept_form('invite' . $i++, $group->id, $returnto);
        }
832
        $group->settingsdescription = group_display_settings($group);
833
834
835
    }
}

836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
/*
 * Used by admin/groups/groups.php and admin/groups/groups.json.php for listing groups.
 */
function build_grouplist_html($query, $limit, $offset, &$count=null) {

    $groups = search_group($query, $limit, $offset, 'all');
    $count = $groups['count'];

    if ($ids = array_map(create_function('$a', 'return intval($a->id);'), $groups['data'])) {
        $sumsql = "(m.role = 'admin')";
        if (is_postgres()) {
            $sumsql .= '::int';
        }

        // Member & admin counts
        $ids = join(',', $ids);
        $counts = get_records_sql_assoc("
            SELECT m.group, COUNT(m.member) AS members, SUM($sumsql) AS admins
            FROM {group_member} m
            WHERE m.group IN ($ids)
            GROUP BY m.group",
            array()
        );
    }

    foreach ($groups['data'] as &$group) {
        $group->visibility = $group->public ? get_string('Public', 'group') : get_string('Members', 'group');
        $group->admins = empty($counts[$group->id]->admins) ? 0 : $counts[$group->id]->admins;
        $group->members = empty($counts[$group->id]->members) ? 0 : $counts[$group->id]->members;
865
866
867
        if (get_config('allowgroupcategories')) {
            $group->categorytitle = ($group->category) ? get_field('group_category', 'title', 'id', $group->category) : '';
        }
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
    }

    $smarty = smarty_core();
    $smarty->assign('groups', $groups['data']);
    $data = array();
    $data['tablerows'] = $smarty->fetch('admin/groups/groupsresults.tpl');

    $pagination = build_pagination(array(
                'id' => 'admgroupslist_pagination',
                'datatable' => 'admgroupslist',
                'url' => get_config('wwwroot') . 'admin/groups/groups.php' . (!empty($query) ? '?query=' . urlencode($query) : ''),
                'jsonscript' => 'admin/groups/groups.json.php',
                'count' => $count,
                'limit' => $limit,
                'offset' => $offset,
                'resultcounttextsingular' => get_string('group', 'group'),
                'resultcounttextplural' => get_string('groups', 'group'),
            ));

    $data['pagination'] = $pagination['html'];
    $data['pagination_js'] = $pagination['javascript'];

    return $data;
}
892

893
function group_get_membersearch_data($results, $group, $query, $membershiptype) {
894
    global $USER;
895
896
897
898
899

    $params = array();
    if (!empty($query)) {
        $params[] = 'query=' . $query;
    }
900
    $params[] = 'limit=' . $results['limit'];
901
902
903
904
905
906
907
    if (!empty($membershiptype)) {
        $params[] = 'membershiptype=' . $membershiptype;
    }
    $searchurl = get_config('wwwroot') . 'group/members.php?id=' . $group . '&amp;' . join('&amp;', $params);

    $smarty = smarty_core();

908
909
    $role = group_user_access($group);
    $userid = $USER->get('id');
910
    foreach ($results['data'] as &$r) {
911
        if ($role == 'admin' && ($r['id'] != $userid || group_user_can_leave($group, $r['id']))) {
912
913
            $r['removeform'] = group_get_removeuser_form($r['id'], $group);
        }
914
915
916
917
918
919
920
921
        // NOTE: this is a quick approximation. We should really check whether, 
        // for each role in the group, that the user can change to it (using 
        // group_can_change_role).  This only controls whether the 'change 
        // role' link appears though, so it doesn't matter too much. If the 
        // user clicks on this link, changerole.php does the full check and 
        // sends them back here saying that the user has no roles they can 
        // change to anyway.
        $r['canchangerole'] = !group_is_only_admin($group, $r['id']);
922
923
    }

924
925
926
    if (!empty($membershiptype)) {
        if ($membershiptype == 'request') {
            foreach ($results['data'] as &$r) {
927
                $r['addform'] = group_get_adduser_form($r['id'], $group);
928
                $r['denyform'] = group_get_denyuser_form($r['id'], $group);
929
930
931
                // TODO: this will suck when there's quite a few on the page, 
                // would be better to grab all the reasons in one go
                $r['reason']  = get_field('group_member_request', 'reason', 'group', $group, 'member', $r['id']);
932
933
934
935
936
            }
        }
        $smarty->assign('membershiptype', $membershiptype);
    }

937
    $results['cdata'] = array_chunk($results['data'], 2);
938
    $results['roles'] = group_get_role_info($group);
939
940
941
    $smarty->assign_by_ref('results', $results);
    $smarty->assign('searchurl', $searchurl);
    $smarty->assign('pagebaseurl', $searchurl);
942
943
    $smarty->assign('caneditroles', group_user_access($group) == 'admin');
    $smarty->assign('group', $group);
944
945
946
947
948
    $html = $smarty->fetch('group/membersearchresults.tpl');

    $pagination = build_pagination(array(
        'id' => 'member_pagination',
        'class' => 'center',
949
        'url' => $searchurl,
950
        'count' => $results['count'],
951
952
        'limit' => $results['limit'],
        'offset' => $results['offset'],
953
        'datatable' => 'membersearchresults',
954
        'jsonscript' => 'group/membersearchresults.json.php',
955
956
957
958
959
        'firsttext' => '',
        'previoustext' => '',
        'nexttext' => '',
        'lasttext' => '',
        'numbersincludefirstlast' => false,
960
961
        'resultcounttextsingular' => get_string('member', 'group'),
        'resultcounttextplural' => get_string('members', 'group'),
962
963
    ));

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

967
968
969

/**
 * Returns a list of available grouptypes
970
971
 *
 * @return array
972
973
974
975
976
 */
function group_get_grouptypes() {
    static $grouptypes = null;

    if (is_null($grouptypes)) {
977
        $grouptypes = get_column('grouptype', 'name');
978
979
980
981
982
    }

    return $grouptypes;
}

983

984
985
986
987
988
989
990
/**
 * Returns a list of grouptype & jointype options to be used in create
 * group/edit group drop-downs.
 * 
 * If there is more than one group type with the same join type,
 * prefix the join types with the group type for display.
 */
991
function group_get_grouptype_options($currentgrouptype=null) {
992
993
    $groupoptions = array();
    $jointypecount = array('open' => 0, 'invite' => 0, 'request' => 0, 'controlled' => 0);
994
    $grouptypes = group_get_grouptypes();
995
    $enabled = array_map(create_function('$a', 'return $a->name;'), plugins_installed('grouptype'));
996
997
998
999
    if (is_null($currentgrouptype) || in_array($currentgrouptype, $enabled)) {
        $grouptypes = array_intersect($enabled, $grouptypes);
    }
    foreach ($grouptypes as $grouptype) {
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
        safe_require('grouptype', $grouptype);
        if (call_static_method('GroupType' . $grouptype, 'can_be_created_by_user')) {
            $grouptypename = get_string('name', 'grouptype.' . $grouptype);
            foreach (call_static_method('GroupType' . $grouptype, 'allowed_join_types') as $jointype) {
                $jointypecount[$jointype]++;
                $groupoptions['jointype']["$grouptype.$jointype"] = get_string('membershiptype.'.$jointype, 'group');
                $groupoptions['grouptype']["$grouptype.$jointype"] = $grouptypename . ': ' . get_string('membershiptype.'.$jointype, 'group');
            }
        }
    }
    $duplicates = array_reduce($jointypecount, create_function('$a, $b', 'return $a || $b > 1;'));
    if ($duplicates) {
        return $groupoptions['grouptype'];
    }
    return $groupoptions['jointype'];
}

1017
1018
1019
1020
1021
1022
/**
 * Returns a datastructure describing the tabs that appear on a group page
 *
 * @param object $group Database record of group to get tabs for
 * @return array
 */
1023
function group_get_menu_tabs() {
Richard Mansfield's avatar
Richard Mansfield committed
1024
1025
    static $menu;

1026
    $group = group_current_group();
1027
1028
1029
    if (!$group) {
        return null;
    }
1030
    $menu = array(
1031
        'info' => array(
1032
            'path' => 'groups/info',
1033
1034
            'url' => 'group/view.php?id='.$group->id,
            'title' => get_string('About', 'group'),
1035
            'weight' => 20
1036
        ),
1037
        'members' => array(
1038
            'path' => 'groups/members',
1039
1040
            'url' => 'group/members.php?id='.$group->id,
            'title' => get_string('Members', 'group'),
1041
            'weight' => 30
1042
        ),
1043
1044
1045
    );
    if ($group->public || group_user_access($group->id)) {
        $menu['forums'] = array(  // @todo: get this from a function in the interaction plugin (or better, make forums an artefact plugin)
1046
1047
1048
            'path' => 'groups/forums',
            'url' => 'interaction/forum/index.php?group='.$group->id,
            'title' => get_string('nameplural', 'interaction.forum'),
1049
1050
1051
1052
1053
1054
1055
1056
            'weight' => 40,
        );
    }
    $menu['views'] = array(
        'path' => 'groups/views',
        'url' => 'view/groupviews.php?group='.$group->id,
        'title' => get_string('Views', 'group'),
        'weight' => 50,
1057
    );
Richard Mansfield's avatar
Richard Mansfield committed
1058
1059
1060
1061
1062
1063
    $menu['share'] = array(
        'path' => 'groups/share',
        'url' => 'group/shareviews.php?group='.$group->id,
        'title' => get_string('share', 'view'),
        'weight' => 60,
    );
1064

1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
    if (group_user_access($group->id)) {
        safe_require('grouptype', $group->grouptype);
        $artefactplugins = call_static_method('GroupType' . $group->grouptype, 'get_group_artefact_plugins');
        if ($plugins = get_records_array('artefact_installed', 'active', 1)) {
            foreach ($plugins as &$plugin) {
                if (!in_array($plugin->name, $artefactplugins)) {
                    continue;
                }
                safe_require('artefact', $plugin->name);
                $plugin_menu = call_static_method(generate_class_name('artefact',$plugin->name), 'group_tabs', $group->id);
                $menu = array_merge($menu, $plugin_menu);
            }
        }
    }

1080
1081
1082
1083
1084
1085
1086
    if (defined('MENUITEM')) {
        $key = substr(MENUITEM, strlen('groups/'));
        if ($key && isset($menu[$key])) {
            $menu[$key]['selected'] = true;
        }
    }

1087
1088
1089
    return $menu;
}

1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
/**
 * Used by this file to perform validation of group ID function arguments
 *
 * @param int $groupid
 * @return int
 * @throws InvalidArgumentException
 */
function group_param_groupid($groupid) {
    $groupid = (int)$groupid;

    if ($groupid == 0) {
        throw new InvalidArgumentException("group_user_access: group argument should be an integer");
    }

    return $groupid;
}

/**
 * Used by this file to perform validation of user ID function arguments
 *
 * @param int $userid
 * @return int
 * @throws InvalidArgumentException
 */
function group_param_userid($userid) {
    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 should be an integer");
    }

    return $userid;
}

1130
1131
1132
1133

function group_current_group() {
    static $group;

1134
1135
1136
1137
1138
1139
1140
    // This function sometimes gets called by the smarty function
    // during the execution of a GroupNotFound exception.  This
    // variable prevents a 2nd exception from being thrown.  Perhaps
    // better achieved with a global in the exception handler?
    static $dying;

    if (defined('GROUP') && !$dying) {
1141
        $group = get_record_select('group', 'id = ? AND deleted = 0', array(GROUP), '*, ' . db_format_tsfield('ctime'));
1142
        if (!$group) {
1143
            $dying = 1;
1144
            throw new GroupNotFoundException(get_string('groupnotfound', 'group', GROUP));
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
        }
    }
    else {
        $group = null;
    }

    return $group;
}


Richard Mansfield's avatar
Richard Mansfield committed
1155
1156
1157
1158
1159
1160
/**
 * creates the group sideblock
 */
function group_sideblock() {
    require_once('group.php');
    $data['group'] = group_current_group();
1161
1162
1163
    if (!$data['group']) {
        return null;
    }
Richard Mansfield's avatar
Richard Mansfield committed
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
    $data['menu'] = group_get_menu_tabs();
    // @todo either: remove this if interactions become group
    // artefacts, or: do this in interaction/lib.php if we leave them
    // as interactions
    $data['forums'] = get_records_select_array(
        'interaction_instance',
        '"group" = ? AND deleted = ? AND plugin = ?',
        array(GROUP, 0, 'forum'),
        'ctime',
        'id, plugin, title'
    );
    if (!$data['forums']) {
        $data['forums'] = array();
    }
    else {
        safe_require('interaction', 'forum');
        $data['forums'] = PluginInteractionForum::sideblock_sort($data['forums']);
    }
    return $data;
}


Dan Marsden's avatar
Dan Marsden committed
1186
function group_get_associated_groups($userid, $filter='all', $limit=20, $offset=0, $category='') {
Richard Mansfield's avatar
Richard Mansfield committed
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201

    // Strangely, casting is only needed for invite, request and admin and only in 
    // postgres
    if (is_mysql()) {
        $invitesql  = "'invite'";
        $requestsql = "'request'";
        $adminsql   = "'admin'";
        $empty      = "''";
    }
    else {
        $invitesql  = "CAST('invite' AS TEXT)";
        $requestsql = "CAST('request' AS TEXT)";
        $adminsql   = "CAST('admin' AS TEXT)";
        $empty      = "CAST('' AS TEXT)";
    }
1202
    // TODO: make it work on other databases?
Richard Mansfield's avatar
Richard Mansfield committed
1203
1204
1205
1206
1207

    // Different filters join on the different kinds of association
    if ($filter == 'admin') {
        $sql = "
            INNER JOIN (
1208
                SELECT g.id, $adminsql AS membershiptype, $empty AS reason, $adminsql AS role
Richard Mansfield's avatar
Richard Mansfield committed
1209
1210
1211
1212
1213
1214
1215
1216
                FROM {group} g
                INNER JOIN {group_member} gm ON (gm.group = g.id AND gm.member = ? AND gm.role = 'admin')
            ) t ON t.id = g.id";
        $values = array($userid);
    }
    else if ($filter == 'member') {
        $sql = "
            INNER JOIN (
1217
                SELECT g.id, 'admin' AS membershiptype, $empty AS reason, $adminsql AS role
Richard Mansfield's avatar
Richard Mansfield committed
1218
1219
1220
                FROM {group} g
                INNER JOIN {group_member} gm ON (gm.group = g.id AND gm.member = ? AND gm.role = 'admin')
                UNION
1221
                SELECT g.id, 'member' AS type, $empty AS reason, gm.role AS role
Richard Mansfield's avatar
Richard Mansfield committed
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
                FROM {group} g
                INNER JOIN {group_member} gm ON (gm.group = g.id AND gm.member = ? AND gm.role != 'admin')
            ) t ON t.id = g.id";
        $values = array($userid, $userid);
    }
    else if ($filter == 'invite') {
        $sql = "
            INNER JOIN (
                SELECT g.id, $invitesql AS membershiptype, gmi.reason, gmi.role
                FROM {group} g
                INNER JOIN {group_member_invite} gmi ON (gmi.group = g.id AND gmi.member = ?)
            ) t ON t.id = g.id";
        $values = array($userid);
    }
    else if ($filter == 'request') {
        $sql = "
            INNER JOIN (
                SELECT g.id, $requestsql AS membershiptype, gmr.reason, $empty AS role
                FROM {group} g
                INNER JOIN {group_member_request} gmr ON (gmr.group = g.id AND gmr.member = ?)
            ) t ON t.id = g.id";
        $values = array($userid);
    }
    else { // all or some other text
        $filter = 'all';
        $sql = "
            INNER JOIN (
1249
                SELECT g.id, 'admin' AS membershiptype, '' AS reason, 'admin' AS role
Richard Mansfield's avatar
Richard Mansfield committed
1250
1251
1252
                FROM {group} g
                INNER JOIN {group_member} gm ON (gm.group = g.id AND gm.member = ? AND gm.role = 'admin')
                UNION
1253
                SELECT g.id, 'member' AS membershiptype, '' AS reason, gm.role AS role
Richard Mansfield's avatar
Richard Mansfield committed
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
                FROM {group} g
                INNER JOIN {group_member} gm ON (g.id = gm.group AND gm.member = ? AND gm.role != 'admin')
                UNION
                SELECT g.id, 'invite' AS membershiptype, gmi.reason, gmi.role
                FROM {group} g
                INNER JOIN {group_member_invite} gmi ON (gmi.group = g.id AND gmi.member = ?)
                UNION SELECT g.id, 'request' AS membershiptype, gmr.reason, '' AS role
                FROM {group} g
                INNER JOIN {group_member_request} gmr ON (gmr.group = g.id AND gmr.member = ?)
            ) t ON t.id = g.id";
        $values = array($userid, $userid, $userid, $userid);
    }
    
    $values[] = 0;
    
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
    $catsql = '';
    if (!empty($category)) {
        if ($category == -1) { //find unassigned groups
            $catsql = ' AND g.category IS NULL';
        } else {
            $catsql = ' AND g.category = ?';
            $values[] = $category;
        }
    }

Dan Marsden's avatar
Dan Marsden committed
1279
    $count = count_records_sql('SELECT COUNT(*) FROM {group} g ' . $sql . ' WHERE g.deleted = ?'.$catsql, $values);
Richard Mansfield's avatar
Richard Mansfield committed
1280
1281
1282
1283
1284
    
    // almost the same as query used in find - common parts should probably be pulled out
    // gets the groups filtered by above
    // and the first three members by id
    
1285
    $sql = 'SELECT g1.id, g1.name, g1.description, g1.public, g1.jointype, g1.grouptype, g1.membershiptype, g1.reason, g1.role, g1.membercount, COUNT(gmr.member) AS requests
1286
        FROM (
1287
        SELECT g.id, g.name, g.description, g.public, g.jointype, g.grouptype, t.membershiptype, t.reason, t.role, COUNT(gm.member) AS membercount
1288
1289
1290
            FROM {group} g
            LEFT JOIN {group_member} gm ON (gm.group = g.id)' .
            $sql . '
Dan Marsden's avatar
Dan Marsden committed
1291
1292
            WHERE g.deleted = ?' .
            $catsql . '
1293
            GROUP BY g.id, g.name, g.description, g.public, g.jointype, g.grouptype, t.membershiptype, t.reason, t.role
1294
1295
1296
            ORDER BY g.name
        ) g1
        LEFT JOIN {group_member_request} gmr ON (gmr.group = g1.id)
1297
        GROUP BY g1.id, g1.name, g1.description, g1.public, g1.jointype, g1.grouptype, g1.membershiptype, g1.reason, g1.role, g1.membercount';
Dan Marsden's avatar
Dan Marsden committed
1298

Richard Mansfield's avatar
Richard Mansfield committed
1299
1300
1301
    $groups = get_records_sql_assoc($sql, $values, $offset, $limit);
    
    if ($groups) {
1302
1303
1304
1305
1306
1307
1308
        // Get a few random members from each group. We've tried this with one 
        // query before but it's painfully slow, databases don't do random rows 
        // efficiently.
        foreach (array_keys($groups) as $groupid) {
            $members = get_records_sql_array("
                SELECT u.*
                FROM {group_member} gm
1309
                INNER JOIN {usr} u ON (gm.member = u.id AND u.deleted = 0)
1310
1311
1312
1313
1314
1315
                WHERE gm.group = ?
                ORDER BY " . db_random() . "
                LIMIT 3", array($groupid));
            foreach ($members as $m) {
                $groups[$groupid]->members[] = (object) array('id' => $m->id, 'name' => display_name($m));
            }
Richard Mansfield's avatar
Richard Mansfield committed
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
        }
        $groups = array_values($groups);
    }
    else {
        $groups = array();
    }

    return array('groups' => $groups, 'count' => $count);

}

1327

1328
function group_get_user_groups($userid=null, $roles=null) {
1329
1330
    static $usergroups = array();

1331
1332
1333
1334
    if (is_null($userid)) {
        global $USER;
        $userid = $USER->get('id');
    }
1335
1336
1337
1338
1339
1340
1341

    if (empty($roles) && isset($usergroups[$userid])) {
        return $usergroups[$userid];
    }

    if (!$groups = get_records_sql_array(
        "SELECT g.id, g.name, gm.role, g.jointype, g.grouptype, gtr.see_submitted_views
1342
1343
        FROM {group} g
        JOIN {group_member} gm ON (gm.group = g.id)