user.php 52.1 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();
29
define('MAXLOGINTRIES', 5);
Donal McMullan's avatar
Donal McMullan committed
30
31
$put = array();

32
33

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

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

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

    }

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

Donal McMullan's avatar
Donal McMullan committed
110
111
112
        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
113

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

127
        $user = get_record_sql($sql, array($id));
128

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

Donal McMullan's avatar
Donal McMullan committed
133
        $this->populate($user);
134
        $this->reset_institutions();
135
        $this->reset_grouproles();
Donal McMullan's avatar
Donal McMullan committed
136
137
        return $this;
    }
138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    /**
     * 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') . ',
156
                    ' . db_format_tsfield('lastlastlogin') . ',
157
                    ' . db_format_tsfield('lastaccess') . ',
Richard Mansfield's avatar
Richard Mansfield committed
158
159
                    ' . db_format_tsfield('suspendedctime') . ',
                    ' . db_format_tsfield('ctime') . '
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
                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;
    }

176
    /**
177
178
179
180
181
     * 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.
182
     */
183
    public function find_by_instanceid_username($instanceid, $username, $remoteuser=false) {
184
185
186
187
188

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

189
        $username = strtolower($username);
190
        if ($remoteuser) {
191
192
193
194
195
196
197
            // 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 = '';
198
            if ($parentid = get_field('auth_instance_config', 'value', 'field', 'parent', 'instance', $instanceid)) {
199
200
201
                $parentwhere = '
                            OR
                            (
202
203
204
205
206
207
208
209
                                LOWER(username) = (
                                    SELECT
                                        username
                                    FROM
                                        {usr} us
                                    JOIN
                                        {auth_remote_user} aru ON (us.id = aru.localusr)
                                    WHERE
210
                                        LOWER(aru.remoteusername) = ' . db_quote($username) . '
211
212
213
214
                                        AND us.authinstance = ' . db_quote($parentid) . '
                                )
                                AND
                                u.authinstance = ' . db_quote($parentid) . '
215
216
                            )
                    ';
217
            }
218

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

257
258
259
260
261
262
263
264
        if (false == $user) {
            throw new AuthUnknownUserException("User with username \"$username\" is not known at auth instance \"$instanceid\"");
        }

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

265
    /**
266
     * Populates this object with the user record identified by a mobile 'token'
267
     *
268
     * @throws AuthUnknownUserException If the user cannot be found. 
269
     */
270
    public function find_by_mobileuploadtoken($token, $username) {
271
272
273
274
275
276

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

        $sql = 'SELECT
277
                        u.*,
278
279
280
281
282
283
284
285
286
                        ' . 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
287
                            WHERE p.field=\'mobileuploadtoken\' AND p.value = ? AND u.username = ?
288
289
		';

290
        $user = get_record_sql($sql, array($token, $username));
291
292
293
294
295
296
297
298
299

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

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

300
301
302
303
304
305
306
307
308
309
310
    /**
     * 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;
    }
311

Richard Mansfield's avatar
Richard Mansfield committed
312
313
314
315
316
    /**
     * Set stuff that needs to be initialised once before a user record is created.
     */
    public function create() {
        $this->set('ctime', time());
317
318
319
320
        if (get_config('cleanurls') && is_null($this->urlid)) {
            $desiredurlid = generate_urlid($this->username, get_config('cleanurluserdefault'), 3, 30);
            $this->set('urlid', get_new_profile_urlid($desiredurlid));
        }
Richard Mansfield's avatar
Richard Mansfield committed
321
322
323
    }


Donal McMullan's avatar
Donal McMullan committed
324
325
326
327
328
329
330
331
332
333
334
335
336
    /**
     * 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});
            }
        }
337
    }
Donal McMullan's avatar
Donal McMullan committed
338

339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
    /**
     * Convert from a row object from the usr table
     *
     * @param  object $data  The row data
     */
    public function from_stdclass($data) {
        foreach (array('expiry', 'lastlogin', 'lastlastlogin', 'lastaccess', 'suspendedctime', 'ctime') as $f) {
            if (isset($data->$f) && !is_numeric($data->$f)) {
                $data->$f = strtotime($data->$f);
            }
        }
        $this->populate($data);
        return $this;
    }

354
355
356
357
358
    /**
     * 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
359
     * @throws InvalidArgumentException
360
361
     */
    public function get($key) {
Donal McMullan's avatar
Donal McMullan committed
362
        if (!array_key_exists($key, $this->defaults)) {
363
            throw new InvalidArgumentException($key);
364
        }
Donal McMullan's avatar
Donal McMullan committed
365
366
        if (array_key_exists($key, $this->attributes) && null !== $this->attributes[$key]) {
            return $this->attributes[$key];
367
368
369
370
        }
        return $this->defaults[$key];
    }

Donal McMullan's avatar
Donal McMullan committed
371
372
373
374
375
376
377
378
379
380
381
    /**
     * 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);
    }

382
383
384
    /**
     * Sets the property keyed by $key
     */
Donal McMullan's avatar
Donal McMullan committed
385
386
387
    protected function set($key, $value) {

        if (!array_key_exists($key, $this->defaults)) {
388
            throw new InvalidArgumentException($key);
389
        }
Donal McMullan's avatar
Donal McMullan committed
390
391
392
393

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

        // For now, these fields are saved to the DB elsewhere
394
        if ($key != 'activityprefs' && $key != 'accountprefs' && $key != 'views') {
Donal McMullan's avatar
Donal McMullan committed
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
            $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
418
        $record = $this->to_stdclass();
Donal McMullan's avatar
Donal McMullan committed
419
        if (is_numeric($this->id) && 0 < $this->id) {
Donal McMullan's avatar
Donal McMullan committed
420
421
422
423
424
425
            try {
                update_record('usr', $record, array('id' => $this->id));
            } catch (Exception $e) {
                throw $e;
                //var_dump($e);
            }
Donal McMullan's avatar
Donal McMullan committed
426
        } else {
Donal McMullan's avatar
Donal McMullan committed
427
428
            try {
                $this->set('id', insert_record('usr', $record, 'id', true));
Donal McMullan's avatar
Donal McMullan committed
429
430
            } catch (SQLException $e) {
                throw $e;
Donal McMullan's avatar
Donal McMullan committed
431
            }
Donal McMullan's avatar
Donal McMullan committed
432
433
        }
        $this->changed = false;
434
435
436
437
438
439
    }

    /** 
     * This function returns a method for a particular
     * activity type, or null if it's not set.
     * 
440
     * @param int $key the activity type id
441
442
443
     */
    public function get_activity_preference($key) {
        $activityprefs = $this->get('activityprefs');
444
        return isset($activityprefs[$key]) ? $activityprefs[$key] : null;
445
    }
Donal McMullan's avatar
Donal McMullan committed
446

447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
    /** @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
465

466
467
    /** @todo document this method */
    public function set_account_preference($field, $value) {
468
469
470
        if ($id = $this->get('id')) {
            set_account_preference($id, $field, $value);
        }
471
472
473
        $accountprefs = $this->get('accountprefs');
        $accountprefs[$field] = $value;
        $this->set('accountprefs', $accountprefs);
474
475
    }

476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494

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

495
496
497
498
499
500
501
502
    /**
     * 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() {
503
        return $this->get_view_by_type('profile');
504
505
506
507
508
509
510
    }

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

514
        db_begin();
515
516
517
518
519
520
521
522
        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),
523
            'description' => get_string('profiledescription'),
524
525
            'type'  => 'profile',
        ), $systemprofileviewid, $this->get('id'));
526
527
528
529
530
531
532

        // Add about me block
        $aboutme = new BlockInstance(0, array(
            'blocktype'  => 'profileinfo',
            'title'      => get_string('aboutme', 'blocktype.internal/profileinfo'),
            'view'       => $view->get('id'),
            'column'     => 1,
533
            'order'      => 1,
534
535
536
537
538
539
        ));
        $configdata = array('artefactids' => array());
        if ($intro = get_field('artefact', 'id', 'owner', $this->get('id'), 'artefacttype', 'introduction')) {
            $configdata['artefactids'][] = $intro;
        }
        else {
540
            $configdata['introtext'] = '';
541
542
543
544
545
        }
        if ($this->get('profileicon')) {
            $configdata['profileicon'] = $this->get('profileicon');
        }
        $aboutme->set('configdata', $configdata);
546
        $view->addblockinstance($aboutme);
547

548
        // Set view access
549
550
        $access = array(
            array(
551
552
553
                'type'      => 'loggedin',
                'startdate' => null,
                'stopdate'  => null,
554
555
            ),
        );
556
557
558
559
560
561
562
563
564
565
566
567
        if ($institutions = $this->get('institutions')) {
            foreach ($institutions as $i) {
                $access[] = array(
                    'type'      => 'institution',
                    'id'        => $i->institution,
                    'startdate' => null,
                    'stopdate'  => null,
                );
            }
        }
        $view->set_access($access);

568
        db_commit();
569
570
571
572

        return $view;
    }

573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
    /**
     * 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),
598
            'description' => get_string('dashboarddescription'),
599
600
601
602
603
604
605
            'type'  => 'dashboard',
        ), $systemdashboardviewid, $this->get('id'));

        db_commit();

        return $view;
    }
606

607
608
609
610
    protected function install_view($viewtype) {
        $function = 'install_' . $viewtype . '_view';
        return $this->$function();
    }
611

612
613
614
615
616
617
    // 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
618
            '"owner" = ? AND type IN (' . join(',', array_map('db_quote', $types)) . ')',
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
            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);
    }
636

637
638
639
640
641
642
643
644
    /**
     * 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
645

646
    public function to_stdclass() {
Donal McMullan's avatar
Donal McMullan committed
647
648
649
        $this->stdclass = new StdClass;
        reset($this->defaults);
        foreach (array_keys($this->defaults) as $k) {
Richard Mansfield's avatar
Richard Mansfield committed
650
            if ($k == 'expiry' || $k == 'lastlogin' || $k == 'lastlastlogin' || $k == 'lastaccess' || $k == 'suspendedctime' || $k == 'ctime') {
Donal McMullan's avatar
Donal McMullan committed
651
652
653
654
                $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));
            }
655
656
657
        }
        return $this->stdclass;
    }
658

Martyn Smith's avatar
Martyn Smith committed
659
660
661
662
663
664
665
666
    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
667
668
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
669
670
671
672
673
674
675
676
677
678
    }

    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
679
680
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
681
682
683
684
685
686
687
688
689
    }

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

        return true;
    }
690

691
692
693
694
695
696
697
698
    public function quota_init() {
        if (!$this->get('quota')) {
            if ($defaultquota = get_config_plugin('artefact', 'file', 'defaultquota')) {
                $this->set('quota', $defaultquota);
            }
        }
    }

699
700
701
702
703
704
    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);
    }

705
    public function join_institution($institution) {
706
        if ($institution != 'mahara' && !$this->in_institution($institution)) {
707
            require_once('institution.php');
708
709
            $institution = new Institution($institution);
            $institution->addUserAsMember($this);
710
            $this->reset_institutions();
711
712
713
        }
    }

714
715
716
717
    public function leave_institution($institution) {
        if ($institution != 'mahara' && $this->in_institution($institution)) {
            require_once('institution.php');
            $institution = new Institution($institution);
718
            $institution->removeMember($this->to_stdclass());
719
720
721
        }
    }

722
723
724
725
726
727
    public function in_institution($institution, $role = null) {
        $institutions = $this->get('institutions');
        return isset($institutions[$institution]) 
            && (is_null($role) || $institutions[$institution]->{$role});
    }

728
    public function is_institutional_admin($institution = null) {
729
        $a = $this->get('admininstitutions');
730
731
732
733
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
734
    }
735

736
737
738
739
740
741
742
743
    public function is_institutional_staff($institution = null) {
        $a = $this->get('staffinstitutions');
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
    }

744
745
746
747
    public function can_edit_institution($institution = null) {
        return $this->get('admin') || $this->is_institutional_admin($institution);
    }

748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
    public function institution_allows_public_views($institution = null) {
        $user_institutions = $this->get('institutions');
        if (empty($user_institutions)) {
            // user belongs to no institutions
            return true;
        }
        else if (is_null($institution) || !isset($user_institutions[$institution->institution])) {
            foreach ($user_institutions as $institution) {
                if ($institution->allowinstitutionpublicviews == 1) {
                    return true;
                }
            }
            return false;
        }
        return $user_institutions[$institution->institution]->allowinstitutionpublicviews == 1;
    }

765
766
767
768
769
770
771
772
    /**
     * 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
     */
773
774
775
776
    public function is_admin_for_user($user) {
        if ($this->get('admin')) {
            return true;
        }
777
778
779
        if (!$this->is_institutional_admin()) {
            return false;
        }
780
781

        // Check privileges for institutional admins now
782
        if ($user instanceof User) {
783
            $userobj = $user;
784
785
        }
        else if (is_numeric($user)) {
786
787
            $userobj = new User;
            $userobj->find_by_id($user);
788
789
790
        }
        else if (is_object($user)) {
            // Should be a row from the usr table
791
792
            $userobj = new User;
            $userobj->find_by_id($user->id);
793
        }
794
795
796
797
        else {
            throw new SystemException("Invalid argument pass to is_admin_for_user method");
        }

798
799
800
801
802
        if ($userobj->get('admin')) {
            return false;
        }

        foreach ($userobj->get('institutions') as $i) {
803
804
805
806
807
808
809
            if ($this->is_institutional_admin($i->institution)) {
                return true;
            }
        }
        return false;
    }

810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
    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;
    }

831
    public function add_institution_request($institution, $studentid = null) {
832
833
834
        if (empty($institution) || $institution == 'mahara') {
            return;
        }
835
836
        require_once('institution.php');
        $institution = new Institution($institution);
837
        $institution->addRequestFromUser($this, $studentid);
838
839
    }

840
    public function reset_institutions($nocachecss=false) {
841
842
        $institutions             = load_user_institutions($this->id);
        $admininstitutions = array();
843
        $staffinstitutions = array();
844
845
        $themename = get_config('theme');
        $headerlogo = null;
846
        $stylesheets = array();
847
848
        $themeinstitution = null;
        foreach ($institutions as $name => $i) {
849
850
851
            if ($i->admin) {
                $admininstitutions[$i->institution] = $i->institution;
            }
852
853
854
            if ($i->staff) {
                $staffinstitutions[$i->institution] = $i->institution;
            }
855
856
            if (is_null($themeinstitution)) {
                $themeinstitution = $name;
857
            }
858
        }
859
860
861
        if ($this->authinstance) {
            $authobj = AuthFactory::create($this->authinstance);
            if (isset($institutions[$authobj->institution])) {
862
863
                if ($institutions[$authobj->institution]->theme) {
                    $themeinstitution = $authobj->institution;
864
865
866
                }
            }
        }
867
        if (!is_null($themeinstitution)) {
868
869
            $themename  = $institutions[$themeinstitution]->theme;
            $headerlogo = $institutions[$themeinstitution]->logo;
870
            if ($institutions[$themeinstitution]->style) {
871
872
873
874
875
                $stylesheet = get_config('wwwroot') . 'style.php?id=' . $institutions[$themeinstitution]->style;
                if ($nocachecss) {
                    $stylesheet .= '&time=' . time();
                }
                $stylesheets[] = $stylesheet;
876
            }
877
        }
878
879
880
881
882
        $this->institutiontheme = (object) array(
            'basename'    => $themename,
            'headerlogo'  => $headerlogo,
            'stylesheets' => $stylesheets,
        );
883
884
        $this->institutions       = $institutions;
        $this->admininstitutions  = $admininstitutions;
885
        $this->staffinstitutions  = $staffinstitutions;
886
887
    }

888
    public function get_themedata() {
889
890
891
        if ($preftheme = $this->get_account_preference('theme')) {
            return (object) array('basename' => $preftheme);
        }
892
        return $this->institutiontheme;
893
894
    }

895
896
897
898
899
900
901
902
903
904
905
    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);
    }

906
    public function can_view_artefact($a) {
907
        if ($this->get('admin')
908
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
909
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
910
911
912
913
914
915
            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
916
                WHERE ar.artefact = ? AND g.member = ? AND ar.can_view = 1 AND g.group = ?", array($a->get('id'), $this->get('id'), $a->get('group')))
917
918
919
                || record_exists('artefact_access_usr', 'usr', $this->get('id'), 'artefact', $a->get('id'));
        }
        return false;
920
921
    }

Richard Mansfield's avatar
Richard Mansfield committed
922
923
    public function can_edit_artefact($a) {
        if ($this->get('admin')
924
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
925
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
Richard Mansfield's avatar
Richard Mansfield committed
926
927
            return true;
        }
928
929
930

        if (!$group = $a->get('group')) {
            return false;
Richard Mansfield's avatar
Richard Mansfield committed
931
        }
932
933
934
935
936
937
938
939
940
941
942
943
944

        require_once('group.php');
        if (!$role = group_user_access($group, $this->id)) {
            return false;
        }
        if ($role == 'admin') {
            return true;
        }
        if ($this->id == $a->get('author')) {
            return true;
        }

        return $a->role_has_permission($role, 'edit');
Richard Mansfield's avatar
Richard Mansfield committed
945
946
    }

947
    public function can_publish_artefact($a) {
948
        if (($this->get('id') and $this->get('id') == $a->get('owner'))) {
949
950
            return true;
        }
951

952
953
954
955
956
957
958
        if ($i = $a->get('institution')) {
            if ($i == 'mahara') {
                return $this->get('admin');
            }
            return $this->in_institution($i);
        }

959
960
        if (!$group = $a->get('group')) {
            return false;
961
        }
962
963
964
965
966
967
968
969
970
971
972
973
974

        require_once('group.php');
        if (!$role = group_user_access($group, $this->id)) {
            return false;
        }
        if ($role == 'admin') {
            return true;
        }
        if ($this->id == $a->get('author')) {
            return true;
        }

        return $a->role_has_permission($role, 'republish');
975
976
    }

977
978
    public function can_edit_view($v) {
        $owner = $v->get('owner');
979
        if ($owner > 0 && $owner == $this->get('id')) {
980
981
            return true;
        }
982
        $institution = $v->get('institution');
983
        if ($institution && $this->can_edit_institution($institution)) {
984
985
            return true;
        }
986
987
        $group = $v->get('group');
        if ($group) {
988
            $this->reset_grouproles();
989
            if (!isset($this->grouproles[$group])) {
990
991
                return false;
            }
992
            if (($v->get('type') == 'grouphomepage' || $v->get('locked')) && $this->grouproles[$group] != 'admin') {
993
994
                return false;
            }
995
            require_once('group.php');
996
            return group_role_can_edit_views($group, $this->grouproles[$group]);
997
998
999
1000
        }
        return false;
    }

1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
    /**
     * Function to check if user can moderate (ie; delete) comments in a view
     */
    public function can_moderate_view($v) {
        $owner = $v->get('owner');
        if ($owner > 0 && $owner == $this->get('id')) {
            return true;
        }
        $institution = $v->get('institution');
        if ($institution && $this->can_edit_institution($institution)) {
            return true;
        }
        $group = $v->get('group');
        if ($group) {
            $this->reset_grouproles();
1016
1017
1018
1019
1020
1021
1022
1023
            if (!isset($this->grouproles[$group])) {
                return false;
            }
            if (($v->get('type') == 'grouphomepage' || $v->get('locked')) && $this->grouproles[$group] != 'admin') {
                return false;
            }
            require_once('group.php');
            return group_role_can_moderate_views($group, $this->grouproles[$group]);
1024
1025
1026
1027
        }
        return false;
    }

1028
1029
1030
1031
1032
1033
1034
    /**
     * 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) {
1035
        $owner = $c->get('owner');
1036
1037
1038
1039
1040
        if ($owner > 0 && $owner == $this->get('id')) {
            return true;
        }
        $institution = $c->get('institution');
        if ($institution && $this->can_edit_institution($institution)) {
1041
1042
            return true;
        }
1043
1044
1045
1046
1047
1048
1049
1050
1051
        $group = $c->get('group');
        if ($group) {
            $this->reset_grouproles();
            if (!isset($this->grouproles[$group])) {
                return false;
            }
            require_once('group.php');
            return group_role_can_edit_views($group, $this->grouproles[$group]);
        }
1052
1053
1054
        return false;
    }

1055
1056
    public function can_delete_self() {
        if (!$this->get('admin')) {
1057
1058
1059
1060
1061
1062
1063
1064
            // 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;
1065
1066
1067
1068
1069
        }
        // The last admin user should not be deleted.
        return count_records('usr', 'admin', 1, 'deleted', 0) > 1;
    }

1070
1071
1072
1073
1074
    /**
     * Makes a literal copy of a list of views for this user.
     *
     * @param array $templateids A list of viewids to copy.
     */
1075
    public function copy_views($templateids, $checkviewaccess=true) {
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
        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();
1086
        foreach (get_records_select_array('view', 'id IN (' . implode(', ', db_array_to_ph($templateids)) . ')', $templateids, '', 'id, title, description, type') as $result) {
1087
1088
1089
1090
1091
1092
1093
1094
1095
            $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,
1096
                'type' => $views[$tid]->type == 'profile' && $checkviewaccess ? 'portfolio' : $views[$tid]->type,
1097
            ), $tid, $this->get('id'), $checkviewaccess);
1098
1099
1100
1101
1102
        }
        db_commit();
    }


1103
1104
1105
}


Donal McMullan's avatar
Donal McMullan committed
1106
1107
class LiveUser extends User {

1108
1109
    protected $SESSION;

Donal McMullan's avatar
Donal McMullan committed
1110
1111
1112
    public function __construct() {

        parent::__construct();
1113
        $this->SESSION = Session::singleton();
Donal McMullan's avatar
Donal McMullan committed
1114
1115
1116
1117
1118
1119
1120
1121
1122

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

1123
    /**
1124
     * Take a username and password and try to authenticate the
1125
1126
1127
1128
1129
1130
     * user
     *
     * @param  string $username
     * @param  string $password
     * @return bool
     */
1131
    public function login($username, $password) {
1132
1133
        $sql = 'SELECT
                    *, 
1134
1135
1136
1137
                    ' . db_format_tsfield('expiry') . ',
                    ' . db_format_tsfield('lastlogin') . ',
                    ' . db_format_tsfield('lastlastlogin') . ',
                    ' . db_format_tsfield('lastaccess') . ',
Richard Mansfield's avatar
Richard Mansfield committed
1138
1139
                    ' . db_format_tsfield('suspendedctime') . ',
                    ' . db_format_tsfield('ctime') . '
1140
1141
1142
1143
1144
                FROM
                    {usr}
                WHERE
                    LOWER(username) = ?';
        $user = get_record_sql($sql, array(strtolower($username)));
1145

1146
1147
        if ($user == false) {
            throw new AuthUnknownUserException("\"$username\" is not known");
1148
1149
        }

1150
1151
1152
1153
1154
1155
        if (isset($user->logintries) && $user->logintries >= MAXLOGINTRIES) {
           global $SESSION;
           $SESSION->add_error_msg(get_string('toomanytries', 'auth'));
           return false;
        }

1156
        if (is_site_closed($user->admin)) {
1157
1158
1159
            return false;
        }

1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
        // Authentication instances that have parents do so because they cannot 
        // use Mahara's normal login mechanism - for example, XMLRPC. If the 
        // user is using one of these authentication instances, we look and try 
        // to use the parent.
        //
        // There's no code here that prevents the authinstance being tried if 
        // it has no parent, mainly because that's an extra database lookup for 
        // the general case, and the authentication will probably just fail 
        // anyway. (XMLRPC, for example, leaves implementation of 
        // authenticate_user_account to the parent Auth class, which says 'not 
        // authorised' by default).
        $instanceid = $user->authinstance;
        if ($parentid = get_field('auth_instance_config', 'value', 'field', 'parent', 'instance', $instanceid)) {
            $instanceid = $parentid;
        }
        $auth = AuthFactory::create($instanceid);
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
        
        // catch the AuthInstanceException that allows authentication plugins to
        // fail but pass onto the next possible plugin
        try {
            if ($auth->authenticate_user_account($user, $password)) {
                $this->authenticate($user, $auth->instanceid);
                // Check for a suspended institution
                $authinstance = get_record_sql('
                    SELECT i.suspended, i.displayname
                    FROM {institution} i JOIN