user.php 43.8 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
263
264
265
    /**
     * Populates this object with the user record identified by a mobile 'token'
     *
     * @throws AuthUnknownUserException If the user cannot be found.
     */
266
    public function find_by_mobileuploadtoken($token, $username) {
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282

        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
283
                            WHERE p.field=\'mobileuploadtoken\' AND p.value = ? AND u.username = ?
284
285
		';

286
        $user = get_record_sql($sql, array($token, $username));
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307

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

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

    /**
     * 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;
    }

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