user.php 43.7 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
 *
 */

defined('INTERNAL') || die();
Donal McMullan's avatar
Donal McMullan committed
29
30
$put = array();

31
32

/**
Donal McMullan's avatar
Donal McMullan committed
33
 * The user class represents any user in the system.
34
35
36
 *
 */
class User {
Donal McMullan's avatar
Donal McMullan committed
37

38
39
40
41
42
    /**
     * Defaults for user information.
     *
     * @var array
     */
Donal McMullan's avatar
Donal McMullan committed
43
44
45
46
47
    protected $defaults;
    protected $stdclass;
    protected $authenticated = false;
    protected $changed       = false;
    protected $attributes    = array();
48
49
50
51
52

    /**
     * Sets defaults for the user object (only because PHP5 does not appear
     * to support private static const arrays), and resumes a session
     */
Donal McMullan's avatar
Donal McMullan committed
53
    public function __construct() {
54
        $this->defaults = array(
55
56
57
58
            'logout_time'      => 0,
            'id'               => 0,
            'username'         => '',
            'password'         => '',
Donal McMullan's avatar
Donal McMullan committed
59
            'salt'             => '',
Donal McMullan's avatar
Donal McMullan committed
60
            'passwordchange'   => 0,
Donal McMullan's avatar
Donal McMullan committed
61
            'active'           => 1,
Donal McMullan's avatar
Donal McMullan committed
62
            'deleted'          => 0,
63
            'expiry'           => null,
64
            'expirymailsent'   => 0,
65
            'lastlogin'        => null,
66
            'lastlastlogin'    => null,
67
            'lastaccess'       => null, /* Is not necessarily updated every request, see accesstimeupdatefrequency config variable */
68
            'inactivemailsent' => 0,
Donal McMullan's avatar
Donal McMullan committed
69
70
            'staff'            => 0,
            'admin'            => 0,
71
72
            'firstname'        => '',
            'lastname'         => '',
Donal McMullan's avatar
Donal McMullan committed
73
            'studentid'        => '',
74
75
            'preferredname'    => '',
            'email'            => '',
Donal McMullan's avatar
Donal McMullan committed
76
77
78
79
            'profileicon'      => null,
            'suspendedctime'   => null,
            'suspendedreason'  => null,
            'suspendedcusr'    => null,
80
            'quota'            => null,
Donal McMullan's avatar
Donal McMullan committed
81
82
83
            'quotaused'        => 0,
            'authinstance'     => 1,
            'sessionid'        => '', /* The real session ID that PHP knows about */
84
85
            'accountprefs'     => array(),
            'activityprefs'    => array(),
86
            'institutions'     => array(),
87
            'grouproles'       => array(),
88
            'theme'            => null,
89
            'admininstitutions' => array(),
90
            'staffinstitutions' => array(),
Richard Mansfield's avatar
Richard Mansfield committed
91
            'parentuser'       => null,
92
            'loginanyway'       => false,
93
            'sesskey'          => '',
Richard Mansfield's avatar
Richard Mansfield committed
94
            'ctime'            => null,
95
            'views'            => array(),
96
            'showhomeinfo'     => 1
97
        );
Donal McMullan's avatar
Donal McMullan committed
98
99
100
101
102
103
104
        $this->attributes = array();

    }

    /**
     * 
     */
Donal McMullan's avatar
Donal McMullan committed
105
    public function find_by_id($id) {
106

Donal McMullan's avatar
Donal McMullan committed
107
108
109
        if (!is_numeric($id) || $id < 0) {
            throw new InvalidArgumentException('parameter must be a positive integer to create a User object');
        }
Donal McMullan's avatar
Donal McMullan committed
110

111
112
113
114
        $sql = 'SELECT
                    *, 
                    ' . db_format_tsfield('expiry') . ', 
                    ' . db_format_tsfield('lastlogin') . ', 
115
116
                    ' . db_format_tsfield('lastlastlogin') . ',
                    ' . db_format_tsfield('lastaccess') . ',
Richard Mansfield's avatar
Richard Mansfield committed
117
118
                    ' . db_format_tsfield('suspendedctime') . ',
                    ' . db_format_tsfield('ctime') . '
119
                FROM
120
                    {usr}
121
122
123
                WHERE
                    id = ?';

124
        $user = get_record_sql($sql, array($id));
125

Donal McMullan's avatar
Donal McMullan committed
126
127
128
129
        if (false == $user) {
            throw new AuthUnknownUserException("User with id \"$id\" is not known");
        }

Donal McMullan's avatar
Donal McMullan committed
130
        $this->populate($user);
131
        $this->reset_institutions();
132
        $this->reset_grouproles();
Donal McMullan's avatar
Donal McMullan committed
133
134
        return $this;
    }
135

136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
    /**
     * Populates this object with the user record identified by the given 
     * username
     *
     * @throws AuthUnknownUserException If the user cannot be found. Note that 
     *                                  deleted users _can_ be found
     */
    public function find_by_username($username) {

        if (!is_string($username)) {
            throw new InvalidArgumentException('username parameter must be a string to create a User object');
        }

        $sql = 'SELECT
                    *,
                    ' . db_format_tsfield('expiry') . ',
                    ' . db_format_tsfield('lastlogin') . ',
153
                    ' . db_format_tsfield('lastlastlogin') . ',
154
                    ' . db_format_tsfield('lastaccess') . ',
Richard Mansfield's avatar
Richard Mansfield committed
155
156
                    ' . db_format_tsfield('suspendedctime') . ',
                    ' . db_format_tsfield('ctime') . '
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
                FROM
                    {usr}
                WHERE
                    username = ?';

        $user = get_record_sql($sql, $username);

        if (false == $user) {
            throw new AuthUnknownUserException("User with username \"$username\" is not known");
        }

        $this->populate($user);
        $this->reset_institutions();
        return $this;
    }

173
    /**
174
175
176
177
178
     * Finds details for a user given a username and their authentication 
     * instance.
     *
     * If the authentication instance is a child or a parent, its relation is 
     * checked too, because the user can enter the system by either method.
179
     */
180
    public function find_by_instanceid_username($instanceid, $username, $remoteuser=false) {
181
182
183
184
185

        if (!is_numeric($instanceid) || $instanceid < 0) {
            throw new InvalidArgumentException('parameter must be a positive integer to create a User object');
        }

186
        if ($remoteuser) {
187
188
189
190
191
192
193
            // See if the user has either the child or the parent authinstance. 
            // Most of the time, it's the parent auth instance that is 
            // stored with the user, but if they were created by (for 
            // example) SSO with no parent, then it will be the child that 
            // is stored. Nevertheless, a parent could be added later, and 
            // that should not matter in finding the user
            $parentwhere = '';
194
            if ($parentid = get_field('auth_instance_config', 'value', 'field', 'parent', 'instance', $instanceid)) {
195
196
197
                $parentwhere = '
                            OR
                            (
198
199
200
201
202
203
204
205
206
207
208
209
210
                                LOWER(username) = (
                                    SELECT
                                        username
                                    FROM
                                        {usr} us
                                    JOIN
                                        {auth_remote_user} aru ON (us.id = aru.localusr)
                                    WHERE
                                        aru.remoteusername = ' . db_quote($username) . '
                                        AND us.authinstance = ' . db_quote($parentid) . '
                                )
                                AND
                                u.authinstance = ' . db_quote($parentid) . '
211
212
                            )
                    ';
213
            }
214

215
216
            $sql = 'SELECT
                        u.*, 
217
218
219
220
                        ' . db_format_tsfield('u.expiry', 'expiry') . ',
                        ' . db_format_tsfield('u.lastlogin', 'lastlogin') . ',
                        ' . db_format_tsfield('u.lastlastlogin', 'lastlastlogin') . ',
                        ' . db_format_tsfield('u.lastaccess', 'lastaccess') . ',
221
                        ' . db_format_tsfield('u.suspendedctime', 'suspendedctime') . ',
Richard Mansfield's avatar
Richard Mansfield committed
222
                        ' . db_format_tsfield('u.ctime', 'ctime') . '
223
                    FROM {usr} u
224
                    LEFT JOIN {auth_remote_user} r ON u.id = r.localusr
225
                    WHERE
226
227
                        (
                            (
228
                                r.remoteusername = ?
229
230
231
232
                                AND r.authinstance = ?
                            )'
                            . $parentwhere
                            . '
233
                        )';
234
235
236
            $user = get_record_sql($sql, array($username, $instanceid));
        }
        else {
237
238
            $sql = 'SELECT
                        *, 
239
240
241
242
                        ' . db_format_tsfield('expiry') . ',
                        ' . db_format_tsfield('lastlogin') . ',
                        ' . db_format_tsfield('lastlastlogin') . ',
                        ' . db_format_tsfield('lastaccess') . ',
Richard Mansfield's avatar
Richard Mansfield committed
243
244
                        ' . db_format_tsfield('suspendedctime') . ',
                        ' . db_format_tsfield('ctime') . '
245
246
247
248
                    FROM
                        {usr}
                    WHERE
                        LOWER(username) = ? AND
249
250
                        authinstance = ?';
            $user = get_record_sql($sql, array($username, $instanceid));
251
252
        }

253
254
255
256
257
258
259
260
        if (false == $user) {
            throw new AuthUnknownUserException("User with username \"$username\" is not known at auth instance \"$instanceid\"");
        }

        $this->populate($user);
        return $this;
    }

261
    /**
262
     * Populates this object with the user record identified by a mobile 'token'
263
     *
264
     * @throws AuthUnknownUserException If the user cannot be found. 
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
     */
    public function find_by_mobileuploadtoken($token) {

        if (!is_string($token)) {
            throw new InvalidArgumentException('Input parameters must be strings to create a User object from token');
        }

        $sql = 'SELECT
                        u.*, 
                        ' . db_format_tsfield('u.expiry', 'expiry') . ',
                        ' . db_format_tsfield('u.lastlogin', 'lastlogin') . ',
                        ' . db_format_tsfield('u.lastlastlogin', 'lastlastlogin') . ',
                        ' . db_format_tsfield('u.lastaccess', 'lastaccess') . ',
                        ' . db_format_tsfield('u.suspendedctime', 'suspendedctime') . ',
                        ' . db_format_tsfield('u.ctime', 'ctime') . '
                FROM
                    {usr} u
                    LEFT JOIN {usr_account_preference} p ON u.id = p.usr
                    		WHERE p.field=\'mobileuploadtoken\' and p.value = ?
		';

        $user = get_record_sql($sql, array($token));

        if (false == $user) {
            throw new AuthUnknownUserException("User with mobile upload token \"$token\" is not known");
        }

        $this->populate($user);
        return $this;
    }

296
297
298
299
300
301
302
303
304
305
306
    /**
     * Refreshes a users mobile 'token' and returns it
     *
     */
    public function refresh_mobileuploadtoken() {
	$new_token = md5( uniqid() );
        $this->set_account_preference('mobileuploadtoken', $new_token);
        $this->set('lastaccess', time());
	$this->commit();
	return $new_token;
    }
307

Richard Mansfield's avatar
Richard Mansfield committed
308
309
310
311
312
313
314
315
    /**
     * Set stuff that needs to be initialised once before a user record is created.
     */
    public function create() {
        $this->set('ctime', time());
    }


Donal McMullan's avatar
Donal McMullan committed
316
317
318
319
320
321
322
323
324
325
326
327
328
    /**
     * Take a row object from the usr table and populate this object with the
     * values
     *
     * @param  object $data  The row data
     */
    protected function populate($data) {
        reset($this->defaults);
        while(list($key, ) = each($this->defaults)) {
            if (property_exists($data, $key)) {
                $this->set($key, $data->{$key});
            }
        }
329
    }
Donal McMullan's avatar
Donal McMullan committed
330

331
332
333
334
335
    /**
     * Gets the user property keyed by $key.
     *
     * @param string $key The key to get the value of
     * @return mixed
Nigel McNie's avatar
Nigel McNie committed
336
     * @throws InvalidArgumentException
337
338
     */
    public function get($key) {
Donal McMullan's avatar
Donal McMullan committed
339
        if (!array_key_exists($key, $this->defaults)) {
340
            throw new InvalidArgumentException($key);
341
        }
Donal McMullan's avatar
Donal McMullan committed
342
343
        if (array_key_exists($key, $this->attributes) && null !== $this->attributes[$key]) {
            return $this->attributes[$key];
344
345
346
347
        }
        return $this->defaults[$key];
    }

Donal McMullan's avatar
Donal McMullan committed
348
349
350
351
352
353
354
355
356
357
358
    /**
     * Gets the user property keyed by $key.
     *
     * @param string $key The key to get the value of
     * @return mixed
     * @throws InvalidArgumentException
     */
    public function __get($key) {
        return $this->get($key);
    }

359
360
361
    /**
     * Sets the property keyed by $key
     */
Donal McMullan's avatar
Donal McMullan committed
362
363
364
    protected function set($key, $value) {

        if (!array_key_exists($key, $this->defaults)) {
365
            throw new InvalidArgumentException($key);
366
        }
Donal McMullan's avatar
Donal McMullan committed
367
368
369
370

        $this->attributes[$key] = $value;

        // For now, these fields are saved to the DB elsewhere
371
        if ($key != 'activityprefs' && $key != 'accountprefs' && $key != 'views') {
Donal McMullan's avatar
Donal McMullan committed
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
            $this->changed = true;
        }
        return $this;
    }

    /**
     * Sets the property keyed by $key
     */
    public function __set($key, $value) {
        if ($key == 'quotaused') {
            throw new InvalidArgumentException('quotaused should be set via the quota_* methods');
        }

        $this->set($key, $value);
    }

    /**
     * Commit the USR record to the database
     */
    public function commit() {
        if ($this->changed == false) {
            return;
        }
Donal McMullan's avatar
Donal McMullan committed
395
        $record = $this->to_stdclass();
Donal McMullan's avatar
Donal McMullan committed
396
        if (is_numeric($this->id) && 0 < $this->id) {
Donal McMullan's avatar
Donal McMullan committed
397
398
399
400
401
402
            try {
                update_record('usr', $record, array('id' => $this->id));
            } catch (Exception $e) {
                throw $e;
                //var_dump($e);
            }
Donal McMullan's avatar
Donal McMullan committed
403
        } else {
Donal McMullan's avatar
Donal McMullan committed
404
405
            try {
                $this->set('id', insert_record('usr', $record, 'id', true));
Donal McMullan's avatar
Donal McMullan committed
406
407
            } catch (SQLException $e) {
                throw $e;
Donal McMullan's avatar
Donal McMullan committed
408
            }
Donal McMullan's avatar
Donal McMullan committed
409
410
        }
        $this->changed = false;
411
412
413
414
415
416
    }

    /** 
     * This function returns a method for a particular
     * activity type, or null if it's not set.
     * 
417
     * @param int $key the activity type id
418
419
420
     */
    public function get_activity_preference($key) {
        $activityprefs = $this->get('activityprefs');
421
        return isset($activityprefs[$key]) ? $activityprefs[$key] : null;
422
    }
Donal McMullan's avatar
Donal McMullan committed
423

424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
    /** @todo document this method */
    public function set_activity_preference($activity, $method) {
        set_activity_preference($this->get('id'), $activity, $method);
        $activityprefs = $this->get('activityprefs');
        $activityprefs[$activity] = $method;
        $this->set('activityprefs', $activityprefs);
    }

    /** 
     * This function returns a value for a particular
     * account preference, or null if it's not set.
     * 
     * @param string $key the field name
     */
    public function get_account_preference($key) {
        $accountprefs = $this->get('accountprefs');
        return isset($accountprefs[$key]) ? $accountprefs[$key] : null;
    }
Donal McMullan's avatar
Donal McMullan committed
442

443
444
445
446
447
448
    /** @todo document this method */
    public function set_account_preference($field, $value) {
        set_account_preference($this->get('id'), $field, $value);
        $accountprefs = $this->get('accountprefs');
        $accountprefs[$field] = $value;
        $this->set('accountprefs', $accountprefs);
449
450
    }

451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469

    public function get_view_by_type($viewtype) {
        $views = $this->get('views');
        if (isset($views[$viewtype])) {
            $viewid = $views[$viewtype];
        }
        else {
            $viewid = get_field('view', 'id', 'type', $viewtype, 'owner', $this->get('id'));
        }
        if (!$viewid) {
            global $USER;
            if (!$USER->get('id')) {
                return null;
            }
            return $this->install_view($viewtype);
        }
        return new View($viewid);
    }

470
471
472
473
474
475
476
477
    /**
     * Return the profile view object for this user.
     *
     * If the user does not yet have a profile view, one is created for them.
     *
     * @return View
     */
    public function get_profile_view() {
478
        return $this->get_view_by_type('profile');
479
480
481
482
483
484
485
    }

    /**
     * Installs a user's profile view.
     *
     * @return View
     */
486
    protected function install_profile_view() {
487
488
        static $systemprofileviewid = null;

489
        db_begin();
490
491
492
493
494
495
496
497
        if (is_null($systemprofileviewid)) {
            $systemprofileviewid = get_field('view', 'id', 'owner', 0, 'type', 'profile');
        }

        require_once(get_config('libroot') . 'view.php');
        list($view) = View::create_from_template(array(
            'owner' => $this->get('id'),
            'title' => get_field('view', 'title', 'id', $systemprofileviewid),
498
            'description' => get_string('profiledescription'),
499
500
            'type'  => 'profile',
        ), $systemprofileviewid, $this->get('id'));
501
502
503
504
505
506
507

        // Add about me block
        $aboutme = new BlockInstance(0, array(
            'blocktype'  => 'profileinfo',
            'title'      => get_string('aboutme', 'blocktype.internal/profileinfo'),
            'view'       => $view->get('id'),
            'column'     => 1,
508
            'order'      => 1,
509
510
511
512
513
514
        ));
        $configdata = array('artefactids' => array());
        if ($intro = get_field('artefact', 'id', 'owner', $this->get('id'), 'artefacttype', 'introduction')) {
            $configdata['artefactids'][] = $intro;
        }
        else {
515
            $configdata['introtext'] = get_string('thisistheprofilepagefor', 'mahara', display_name($this, null, true));
516
517
518
519
520
        }
        if ($this->get('profileicon')) {
            $configdata['profileicon'] = $this->get('profileicon');
        }
        $aboutme->set('configdata', $configdata);
521
        $view->addblockinstance($aboutme);
522

523
524
525
526
527
528
529
        $view->set_access(array(
            array(
                'type'      => 'loggedin',
                'startdate' => null,
                'stopdate'  => null,
            ),
        ));
530
        db_commit();
531
532
533
534

        return $view;
    }

535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
    /**
     * Return the dashboard view object for this user.
     *
     * If the user does not yet have a dashboard view, one is created for them.
     *
     * @return View
     */

    /**
     * Installs a user's dashboard view.
     *
     * @return View
     */
    protected function install_dashboard_view() {
        static $systemdashboardviewid = null;

        db_begin();
        if (is_null($systemdashboardviewid)) {
            $systemdashboardviewid = get_field('view', 'id', 'owner', 0, 'type', 'dashboard');
        }

        require_once(get_config('libroot') . 'view.php');
        list($view) = View::create_from_template(array(
            'owner' => $this->get('id'),
            'title' => get_field('view', 'title', 'id', $systemdashboardviewid),
560
            'description' => get_string('dashboarddescription'),
561
562
563
564
565
566
567
            'type'  => 'dashboard',
        ), $systemdashboardviewid, $this->get('id'));

        db_commit();

        return $view;
    }
568

569
570
571
572
    protected function install_view($viewtype) {
        $function = 'install_' . $viewtype . '_view';
        return $this->$function();
    }
573

574
575
576
577
578
579
    // Store the ids of the user's special views (profile, dashboard).  Users can have only
    // one each of these, so there really should be columns in the user table to store them.
    protected function load_views() {
        $types = array('profile', 'dashboard');
        $views = get_records_select_assoc(
            'view',
Francois Marier's avatar
Francois Marier committed
580
            '"owner" = ? AND type IN (' . join(',', array_map('db_quote', $types)) . ')',
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
            array($this->id),
            '',
            'type,id'
        );

        $specialviews = array();
        foreach ($types as $type) {
            if (!empty($views[$type])) {
                $specialviews[$type] = $views[$type]->id;
            }
            else {
                $view = $this->install_view($type);
                $specialviews[$type] = $view->get('id');
            }
        }
        $this->set('views', $specialviews);
    }
598

599
600
601
602
603
604
605
606
    /**
     * Determines if the user is currently logged in
     *
     * @return boolean
     */
    public function is_logged_in() {
        return ($this->get('logout_time') > 0 ? true : false);
    }
Donal McMullan's avatar
Donal McMullan committed
607

608
    public function to_stdclass() {
Donal McMullan's avatar
Donal McMullan committed
609
610
611
        $this->stdclass = new StdClass;
        reset($this->defaults);
        foreach (array_keys($this->defaults) as $k) {
Richard Mansfield's avatar
Richard Mansfield committed
612
            if ($k == 'expiry' || $k == 'lastlogin' || $k == 'lastlastlogin' || $k == 'lastaccess' || $k == 'suspendedctime' || $k == 'ctime') {
Donal McMullan's avatar
Donal McMullan committed
613
614
615
616
                $this->stdclass->{$k} = db_format_timestamp($this->get($k));
            } else {
                $this->stdclass->{$k} = $this->get($k);//(is_null($this->get($k))? 'NULL' : $this->get($k));
            }
617
618
619
        }
        return $this->stdclass;
    }
620

Martyn Smith's avatar
Martyn Smith committed
621
622
623
624
625
626
627
628
    public function quota_add($bytes) {
        if (!is_numeric($bytes) || $bytes < 0) {
            throw new InvalidArgumentException('parameter must be a positive integer to add to the quota');
        }
        if (!$this->quota_allowed($bytes)) {
            throw new QuotaExceededException('Adding ' . $bytes . ' bytes would exceed the user\'s quota');
        }
        $newquota = $this->get('quotaused') + $bytes;
Donal McMullan's avatar
Donal McMullan committed
629
630
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
631
632
633
634
635
636
637
638
639
640
    }

    public function quota_remove($bytes) {
        if (!is_numeric($bytes) || $bytes < 0) {
            throw new InvalidArgumentException('parameter must be a positive integer to remove from the quota');
        }
        $newquota = $this->get('quotaused') - $bytes;
        if ($newquota < 0) {
            $newquota = 0;
        }
Donal McMullan's avatar
Donal McMullan committed
641
642
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
643
644
645
646
647
648
649
650
651
    }

    public function quota_allowed($bytes) {
        if ($this->get('quotaused') + $bytes > $this->get('quota')) {
            return false;
        }

        return true;
    }
652

653
654
655
656
657
658
659
660
    public function quota_init() {
        if (!$this->get('quota')) {
            if ($defaultquota = get_config_plugin('artefact', 'file', 'defaultquota')) {
                $this->set('quota', $defaultquota);
            }
        }
    }

661
662
663
664
665
666
    public function quota_refresh() {
        $quotadata = get_record_sql('SELECT quota, quotaused FROM {usr} WHERE id = ?', array($this->get('id')));
        $this->set('quota', $quotadata->quota);
        $this->set("quotaused", $quotadata->quotaused);
    }

667
    public function join_institution($institution) {
668
        if ($institution != 'mahara' && !$this->in_institution($institution)) {
669
            require_once('institution.php');
670
671
            $institution = new Institution($institution);
            $institution->addUserAsMember($this);
672
            $this->reset_institutions();
673
674
675
        }
    }

676
677
678
679
    public function leave_institution($institution) {
        if ($institution != 'mahara' && $this->in_institution($institution)) {
            require_once('institution.php');
            $institution = new Institution($institution);
680
            $institution->removeMember($this->to_stdclass());
681
682
683
        }
    }

684
685
686
687
688
689
    public function in_institution($institution, $role = null) {
        $institutions = $this->get('institutions');
        return isset($institutions[$institution]) 
            && (is_null($role) || $institutions[$institution]->{$role});
    }

690
    public function is_institutional_admin($institution = null) {
691
        $a = $this->get('admininstitutions');
692
693
694
695
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
696
    }
697

698
699
700
701
702
703
704
705
    public function is_institutional_staff($institution = null) {
        $a = $this->get('staffinstitutions');
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
    }

706
707
708
709
    public function can_edit_institution($institution = null) {
        return $this->get('admin') || $this->is_institutional_admin($institution);
    }

710
711
712
713
714
715
716
717
    /**
     * Returns whether this user is allowed to perform administration type
     * actions on another user.
     *
     * @param mixed $user The user to check we can perform actions on. Can
     *                    either be a User object, a row from the usr table or
     *                    an ID
     */
718
719
720
721
    public function is_admin_for_user($user) {
        if ($this->get('admin')) {
            return true;
        }
722
723
724
        if (!$this->is_institutional_admin()) {
            return false;
        }
725
726

        // Check privileges for institutional admins now
727
        if ($user instanceof User) {
728
            $userobj = $user;
729
730
        }
        else if (is_numeric($user)) {
731
732
            $userobj = new User;
            $userobj->find_by_id($user);
733
734
735
        }
        else if (is_object($user)) {
            // Should be a row from the usr table
736
737
            $userobj = new User;
            $userobj->find_by_id($user->id);
738
        }
739
740
741
742
        else {
            throw new SystemException("Invalid argument pass to is_admin_for_user method");
        }

743
744
745
746
747
        if ($userobj->get('admin')) {
            return false;
        }

        foreach ($userobj->get('institutions') as $i) {
748
749
750
751
752
753
754
            if ($this->is_institutional_admin($i->institution)) {
                return true;
            }
        }
        return false;
    }

755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
    public function is_staff_for_user($user) {
        if ($this->get('admin') || $this->get('staff')) {
            return true;
        }
        if (!$this->is_institutional_admin() && !$this->is_institutional_staff()) {
            return false;
        }
        if ($user instanceof User) {
            $userinstitutions = $user->get('institutions');
        } else {
            $userinstitutions = load_user_institutions($user->id);
        }
        foreach ($userinstitutions as $i) {
            if ($this->is_institutional_admin($i->institution)
                || $this->is_institutional_staff($i->institution)) {
                return true;
            }
        }
        return false;
    }

776
    public function add_institution_request($institution, $studentid = null) {
777
778
779
        if (empty($institution) || $institution == 'mahara') {
            return;
        }
780
781
        require_once('institution.php');
        $institution = new Institution($institution);
782
        $institution->addRequestFromUser($this, $studentid);
783
784
    }

785
    public function reset_institutions() {
786
787
        $institutions             = load_user_institutions($this->id);
        $admininstitutions = array();
788
        $staffinstitutions = array();
789
        $this->theme = get_config('theme');
790
791
792
793
        foreach ($institutions as $i) {
            if ($i->admin) {
                $admininstitutions[$i->institution] = $i->institution;
            }
794
795
796
            if ($i->staff) {
                $staffinstitutions[$i->institution] = $i->institution;
            }
797
798
            if (!empty($i->theme) && $i->theme != get_config('theme')) {
                $this->theme = $i->theme;
799
            }
800
        }
801
802
803
804
805
806
807
808
        if ($this->authinstance) {
            $authobj = AuthFactory::create($this->authinstance);
            if (isset($institutions[$authobj->institution])) {
                if ($t = $institutions[$authobj->institution]->theme) {
                    $this->theme = $t;
                }
            }
        }
809
810
        $this->institutions       = $institutions;
        $this->admininstitutions  = $admininstitutions;
811
        $this->staffinstitutions  = $staffinstitutions;
812
813
    }

814
815
816
817
818
819
820
821
822
823
824
    public function reset_grouproles() {
        $memberships = get_records_array('group_member', 'member', $this->get('id'));
        $roles = array();
        if ($memberships) {
            foreach ($memberships as $m) {
                $roles[$m->group] = $m->role;
            }
        }
        $this->set('grouproles', $roles);
    }

825
    public function can_view_artefact($a) {
826
        if ($this->get('admin')
827
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
828
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
829
830
831
832
833
834
            return true;
        }
        if ($a->get('group')) {
            // Only group artefacts can have artefact_access_role & artefact_access_usr records
            return (bool) count_records_sql("SELECT COUNT(*) FROM {artefact_access_role} ar
                INNER JOIN {group_member} g ON ar.role = g.role
835
                WHERE ar.artefact = ? AND g.member = ? AND ar.can_view = 1 AND g.group = ?", array($a->get('id'), $this->get('id'), $a->get('group')))
836
837
838
                || record_exists('artefact_access_usr', 'usr', $this->get('id'), 'artefact', $a->get('id'));
        }
        return false;
839
840
    }

Richard Mansfield's avatar
Richard Mansfield committed
841
842
    public function can_edit_artefact($a) {
        if ($this->get('admin')
843
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
844
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
Richard Mansfield's avatar
Richard Mansfield committed
845
846
847
848
            return true;
        }
        $group = $a->get('group');
        if ($group) {
849
850
851
852
            return count_records_sql("SELECT COUNT(*) FROM {artefact_access_role} ar
                INNER JOIN {group_member} g ON ar.role = g.role
                WHERE ar.artefact = ? AND g.member = ? AND ar.can_edit = 1 AND g.group = ?", array($a->get('id'), $this->get('id'), $group));
            /*
853
854
            require_once(get_config('docroot') . 'lib/group.php');
            $role = group_user_access($group, $this->get('id'));
Richard Mansfield's avatar
Richard Mansfield committed
855
856
857
            if ($role) {
                $aperms = $a->get('rolepermissions');
                return $aperms->{$role}->edit;
858
            } */
Richard Mansfield's avatar
Richard Mansfield committed
859
860
861
862
        }
        return false;
    }

863
864
865
866
867
    public function can_edit_view($v) {
        $owner = $v->get('owner');
        if ($owner == $this->get('id')) {
            return true;
        }
868
        $institution = $v->get('institution');
869
        if ($institution && $this->can_edit_institution($institution)) {
870
871
            return true;
        }
872
873
874
        $group = $v->get('group');
        if ($group) {
            $editroles = $v->get('editingroles');
875
            $this->reset_grouproles();
876
877
878
            if ($v->get('type') == 'grouphomepage' && $this->grouproles[$group] != 'admin') {
                return false;
            }
Richard Mansfield's avatar
Richard Mansfield committed
879
            return isset($this->grouproles[$group]) && in_array($this->grouproles[$group], $editroles);
880
881
882
883
        }
        return false;
    }

884
885
886
887
888
889
890
    /**
     * Function to check current user can edit collection
     *
     * This is fairly straightforward at the moment but it might require more
     * if groups are allowed collections and other amendments in the future
     */
    public function can_edit_collection($c) {
891
892
        $owner = $c->get('owner');
        if ($owner == $this->get('id')) {
893
894
895
896
897
            return true;
        }
        return false;
    }

898
899
    public function can_delete_self() {
        if (!$this->get('admin')) {
900
901
902
903
904
905
906
907
            // Users who belong to an institution that doesn't allow
            // registration cannot delete themselves.
            foreach ($this->get('institutions') as $i) {
                if (!$i->registerallowed) {
                    return false;
                }
            }
            return true;
908
909
910
911
912
        }
        // The last admin user should not be deleted.
        return count_records('usr', 'admin', 1, 'deleted', 0) > 1;
    }

913
914
915
916
917
    /**
     * Makes a literal copy of a list of views for this user.
     *
     * @param array $templateids A list of viewids to copy.
     */
918
    public function copy_views($templateids, $checkviewaccess=true) {
919
920
921
922
923
924
925
926
927
928
        if (!$templateids) {
            // Nothing to do
            return;
        }
        if (!is_array($templateids)) {
            throw new SystemException('User->copy_views: templateids must be a list of templates to copy for the user');
        }
        require_once(get_config('libroot') . 'view.php');

        $views = array();
929
        foreach (get_records_select_array('view', 'id IN (' . implode(', ', db_array_to_ph($templateids)) . ')', $templateids, '', 'id, title, description, type') as $result) {
930
931
932
933
934
935
936
937
938
            $views[$result->id] = $result;
        }

        db_begin();
        foreach ($templateids as $tid) {
            View::create_from_template(array(
                'owner' => $this->get('id'),
                'title' => $views[$tid]->title,
                'description' => $views[$tid]->description,
939
                'type' => $views[$tid]->type == 'profile' && $checkviewaccess ? 'portfolio' : $views[$tid]->type,
940
            ), $tid, $this->get('id'), $checkviewaccess);
941
942
943
944
945
        }
        db_commit();
    }


946
947
948
}


Donal McMullan's avatar
Donal McMullan committed
949
950
class LiveUser extends User {

951
952
    protected $SESSION;

Donal McMullan's avatar
Donal McMullan committed
953
954
955
    public function __construct() {

        parent::__construct();
956
        $this->SESSION = Session::singleton();
Donal McMullan's avatar
Donal McMullan committed
957
958
959
960
961
962
963
964
965

        if ($this->SESSION->is_live()) {
            $this->authenticated  = true;
            while(list($key,) = each($this->defaults)) {
                $this->get($key);
            }
        }
    }

966
    /**
967
     * Take a username and password and try to authenticate the
968
969
970
971
972
973
     * user
     *
     * @param  string $username
     * @param  string $password
     * @return bool
     */
974
    public function login($username, $password) {
975
976
        $sql = 'SELECT
                    *, 
977
978
979
980
                    ' . db_format_tsfield('expiry') . ',
                    ' . db_format_tsfield('lastlogin') . ',
                    ' . db_format_tsfield('lastlastlogin') . ',
                    ' . db_format_tsfield('lastaccess') . ',
Richard Mansfield's avatar
Richard Mansfield committed
981
982
                    ' . db_format_tsfield('suspendedctime') . ',
                    ' . db_format_tsfield('ctime') . '
983
984
985
986
987
                FROM
                    {usr}
                WHERE
                    LOWER(username) = ?';
        $user = get_record_sql($sql, array(strtolower($username)));
988

989
990
        if ($user == false) {
            throw new AuthUnknownUserException("\"$username\" is not known");
991
992
        }

993
994
995
        $siteclosedforupgrade = get_config('siteclosed');
        if ($siteclosedforupgrade && get_config('disablelogin')) {
            global $SESSION;
996
            $SESSION->add_error_msg(get_string('siteclosedlogindisabled', 'mahara', get_config('wwwroot') . 'admin/upgrade.php'), false);
997
998
999
1000
            return false;
        }
        if (!$user->admin && ($siteclosedforupgrade || get_config('siteclosedbyadmin'))) {
            global $SESSION;
For faster browsing, not all history is shown. View entire blame