user.php 41.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();
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;
    }

Richard Mansfield's avatar
Richard Mansfield committed
261
262
263
264
265
266
267
268
    /**
     * 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
269
270
271
272
273
274
275
276
277
278
279
280
281
    /**
     * 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});
            }
        }
282
    }
Donal McMullan's avatar
Donal McMullan committed
283

284
285
286
287
288
    /**
     * 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
289
     * @throws InvalidArgumentException
290
291
     */
    public function get($key) {
Donal McMullan's avatar
Donal McMullan committed
292
        if (!array_key_exists($key, $this->defaults)) {
293
            throw new InvalidArgumentException($key);
294
        }
Donal McMullan's avatar
Donal McMullan committed
295
296
        if (array_key_exists($key, $this->attributes) && null !== $this->attributes[$key]) {
            return $this->attributes[$key];
297
298
299
300
        }
        return $this->defaults[$key];
    }

Donal McMullan's avatar
Donal McMullan committed
301
302
303
304
305
306
307
308
309
310
311
    /**
     * 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);
    }

312
313
314
    /**
     * Sets the property keyed by $key
     */
Donal McMullan's avatar
Donal McMullan committed
315
316
317
    protected function set($key, $value) {

        if (!array_key_exists($key, $this->defaults)) {
318
            throw new InvalidArgumentException($key);
319
        }
Donal McMullan's avatar
Donal McMullan committed
320
321
322
323

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

        // For now, these fields are saved to the DB elsewhere
324
        if ($key != 'activityprefs' && $key != 'accountprefs' && $key != 'views') {
Donal McMullan's avatar
Donal McMullan committed
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
            $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
348
        $record = $this->to_stdclass();
Donal McMullan's avatar
Donal McMullan committed
349
        if (is_numeric($this->id) && 0 < $this->id) {
Donal McMullan's avatar
Donal McMullan committed
350
351
352
353
354
355
            try {
                update_record('usr', $record, array('id' => $this->id));
            } catch (Exception $e) {
                throw $e;
                //var_dump($e);
            }
Donal McMullan's avatar
Donal McMullan committed
356
        } else {
Donal McMullan's avatar
Donal McMullan committed
357
358
            try {
                $this->set('id', insert_record('usr', $record, 'id', true));
Donal McMullan's avatar
Donal McMullan committed
359
360
            } catch (SQLException $e) {
                throw $e;
Donal McMullan's avatar
Donal McMullan committed
361
            }
Donal McMullan's avatar
Donal McMullan committed
362
363
        }
        $this->changed = false;
364
365
366
367
368
369
    }

    /** 
     * This function returns a method for a particular
     * activity type, or null if it's not set.
     * 
370
     * @param int $key the activity type id
371
372
373
     */
    public function get_activity_preference($key) {
        $activityprefs = $this->get('activityprefs');
374
        return isset($activityprefs[$key]) ? $activityprefs[$key] : null;
375
    }
Donal McMullan's avatar
Donal McMullan committed
376

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
    /** @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
395

396
397
398
399
400
401
    /** @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);
402
403
    }

404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422

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

423
424
425
426
427
428
429
430
    /**
     * 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() {
431
        return $this->get_view_by_type('profile');
432
433
434
435
436
437
438
    }

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

442
        db_begin();
443
444
445
446
447
448
449
450
        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),
451
            'description' => get_string('profiledescription'),
452
453
            'type'  => 'profile',
        ), $systemprofileviewid, $this->get('id'));
454
455
456
457
458
459
460

        // Add about me block
        $aboutme = new BlockInstance(0, array(
            'blocktype'  => 'profileinfo',
            'title'      => get_string('aboutme', 'blocktype.internal/profileinfo'),
            'view'       => $view->get('id'),
            'column'     => 1,
461
            'order'      => 1,
462
463
464
465
466
467
        ));
        $configdata = array('artefactids' => array());
        if ($intro = get_field('artefact', 'id', 'owner', $this->get('id'), 'artefacttype', 'introduction')) {
            $configdata['artefactids'][] = $intro;
        }
        else {
468
            $configdata['introtext'] = get_string('thisistheprofilepagefor', 'mahara', display_name($this, null, true));
469
470
471
472
473
        }
        if ($this->get('profileicon')) {
            $configdata['profileicon'] = $this->get('profileicon');
        }
        $aboutme->set('configdata', $configdata);
474
        $view->addblockinstance($aboutme);
475

476
477
478
479
480
481
482
        $view->set_access(array(
            array(
                'type'      => 'loggedin',
                'startdate' => null,
                'stopdate'  => null,
            ),
        ));
483
        db_commit();
484
485
486
487

        return $view;
    }

488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
    /**
     * 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),
513
            'description' => get_string('dashboarddescription'),
514
515
516
517
518
519
520
            'type'  => 'dashboard',
        ), $systemdashboardviewid, $this->get('id'));

        db_commit();

        return $view;
    }
521

522
523
524
525
    protected function install_view($viewtype) {
        $function = 'install_' . $viewtype . '_view';
        return $this->$function();
    }
526

527
528
529
530
531
532
    // 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
533
            '"owner" = ? AND type IN (' . join(',', array_map('db_quote', $types)) . ')',
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
            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);
    }
551

552
553
554
555
556
557
558
559
    /**
     * 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
560

561
    public function to_stdclass() {
Donal McMullan's avatar
Donal McMullan committed
562
563
564
        $this->stdclass = new StdClass;
        reset($this->defaults);
        foreach (array_keys($this->defaults) as $k) {
Richard Mansfield's avatar
Richard Mansfield committed
565
            if ($k == 'expiry' || $k == 'lastlogin' || $k == 'lastlastlogin' || $k == 'lastaccess' || $k == 'suspendedctime' || $k == 'ctime') {
Donal McMullan's avatar
Donal McMullan committed
566
567
568
569
                $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));
            }
570
571
572
        }
        return $this->stdclass;
    }
573

Martyn Smith's avatar
Martyn Smith committed
574
575
576
577
578
579
580
581
    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
582
583
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
584
585
586
587
588
589
590
591
592
593
    }

    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
594
595
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
596
597
598
599
600
601
602
603
604
    }

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

        return true;
    }
605

606
607
608
609
610
611
612
613
    public function quota_init() {
        if (!$this->get('quota')) {
            if ($defaultquota = get_config_plugin('artefact', 'file', 'defaultquota')) {
                $this->set('quota', $defaultquota);
            }
        }
    }

614
    public function join_institution($institution) {
615
        if ($institution != 'mahara' && !$this->in_institution($institution)) {
616
            require_once('institution.php');
617
618
            $institution = new Institution($institution);
            $institution->addUserAsMember($this);
619
            $this->reset_institutions();
620
621
622
        }
    }

623
624
625
626
    public function leave_institution($institution) {
        if ($institution != 'mahara' && $this->in_institution($institution)) {
            require_once('institution.php');
            $institution = new Institution($institution);
627
            $institution->removeMember($this->to_stdclass());
628
629
630
        }
    }

631
632
633
634
635
636
    public function in_institution($institution, $role = null) {
        $institutions = $this->get('institutions');
        return isset($institutions[$institution]) 
            && (is_null($role) || $institutions[$institution]->{$role});
    }

637
    public function is_institutional_admin($institution = null) {
638
        $a = $this->get('admininstitutions');
639
640
641
642
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
643
    }
644

645
646
647
648
649
650
651
652
    public function is_institutional_staff($institution = null) {
        $a = $this->get('staffinstitutions');
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
    }

653
654
655
656
    public function can_edit_institution($institution = null) {
        return $this->get('admin') || $this->is_institutional_admin($institution);
    }

657
658
659
660
661
662
663
664
    /**
     * 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
     */
665
666
667
668
    public function is_admin_for_user($user) {
        if ($this->get('admin')) {
            return true;
        }
669
670
671
        if (!$this->is_institutional_admin()) {
            return false;
        }
672
673

        // Check privileges for institutional admins now
674
        if ($user instanceof User) {
675
            $userobj = $user;
676
677
        }
        else if (is_numeric($user)) {
678
679
            $userobj = new User;
            $userobj->find_by_id($user);
680
681
682
        }
        else if (is_object($user)) {
            // Should be a row from the usr table
683
684
            $userobj = new User;
            $userobj->find_by_id($user->id);
685
        }
686
687
688
689
        else {
            throw new SystemException("Invalid argument pass to is_admin_for_user method");
        }

690
691
692
693
694
        if ($userobj->get('admin')) {
            return false;
        }

        foreach ($userobj->get('institutions') as $i) {
695
696
697
698
699
700
701
            if ($this->is_institutional_admin($i->institution)) {
                return true;
            }
        }
        return false;
    }

702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
    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;
    }

723
    public function add_institution_request($institution, $studentid = null) {
724
725
726
        if (empty($institution) || $institution == 'mahara') {
            return;
        }
727
728
        require_once('institution.php');
        $institution = new Institution($institution);
729
        $institution->addRequestFromUser($this, $studentid);
730
731
    }

732
    public function reset_institutions() {
733
734
        $institutions             = load_user_institutions($this->id);
        $admininstitutions = array();
735
        $staffinstitutions = array();
736
        $this->theme = get_config('theme');
737
738
739
740
        foreach ($institutions as $i) {
            if ($i->admin) {
                $admininstitutions[$i->institution] = $i->institution;
            }
741
742
743
            if ($i->staff) {
                $staffinstitutions[$i->institution] = $i->institution;
            }
744
745
            if (!empty($i->theme) && $i->theme != get_config('theme')) {
                $this->theme = $i->theme;
746
            }
747
        }
748
749
750
751
752
753
754
755
        if ($this->authinstance) {
            $authobj = AuthFactory::create($this->authinstance);
            if (isset($institutions[$authobj->institution])) {
                if ($t = $institutions[$authobj->institution]->theme) {
                    $this->theme = $t;
                }
            }
        }
756
757
        $this->institutions       = $institutions;
        $this->admininstitutions  = $admininstitutions;
758
        $this->staffinstitutions  = $staffinstitutions;
759
760
    }

761
762
763
764
765
766
767
768
769
770
771
    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);
    }

772
    public function can_view_artefact($a) {
773
        if ($this->get('admin')
774
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
775
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
776
777
778
779
780
781
            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
782
                WHERE ar.artefact = ? AND g.member = ? AND ar.can_view = 1 AND g.group = ?", array($a->get('id'), $this->get('id'), $a->get('group')))
783
784
785
                || record_exists('artefact_access_usr', 'usr', $this->get('id'), 'artefact', $a->get('id'));
        }
        return false;
786
787
    }

Richard Mansfield's avatar
Richard Mansfield committed
788
789
    public function can_edit_artefact($a) {
        if ($this->get('admin')
790
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
791
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
Richard Mansfield's avatar
Richard Mansfield committed
792
793
794
795
            return true;
        }
        $group = $a->get('group');
        if ($group) {
796
797
798
799
            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));
            /*
800
801
            require_once(get_config('docroot') . 'lib/group.php');
            $role = group_user_access($group, $this->get('id'));
Richard Mansfield's avatar
Richard Mansfield committed
802
803
804
            if ($role) {
                $aperms = $a->get('rolepermissions');
                return $aperms->{$role}->edit;
805
            } */
Richard Mansfield's avatar
Richard Mansfield committed
806
807
808
809
        }
        return false;
    }

810
811
812
813
814
    public function can_edit_view($v) {
        $owner = $v->get('owner');
        if ($owner == $this->get('id')) {
            return true;
        }
815
        $institution = $v->get('institution');
816
        if ($institution && $this->can_edit_institution($institution)) {
817
818
            return true;
        }
819
820
821
        $group = $v->get('group');
        if ($group) {
            $editroles = $v->get('editingroles');
822
            $this->reset_grouproles();
Richard Mansfield's avatar
Richard Mansfield committed
823
            return isset($this->grouproles[$group]) && in_array($this->grouproles[$group], $editroles);
824
825
826
827
        }
        return false;
    }

828
829
    public function can_delete_self() {
        if (!$this->get('admin')) {
830
831
832
833
834
835
836
837
            // 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;
838
839
840
841
842
        }
        // The last admin user should not be deleted.
        return count_records('usr', 'admin', 1, 'deleted', 0) > 1;
    }

843
844
845
846
847
    /**
     * Makes a literal copy of a list of views for this user.
     *
     * @param array $templateids A list of viewids to copy.
     */
848
    public function copy_views($templateids, $checkviewaccess=true) {
849
850
851
852
853
854
855
856
857
858
        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();
859
        foreach (get_records_select_array('view', 'id IN (' . implode(', ', db_array_to_ph($templateids)) . ')', $templateids, '', 'id, title, description, type') as $result) {
860
861
862
863
864
865
866
867
868
            $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,
869
                'type' => $views[$tid]->type == 'profile' && $checkviewaccess ? 'portfolio' : $views[$tid]->type,
870
            ), $tid, $this->get('id'), $checkviewaccess);
871
872
873
874
875
        }
        db_commit();
    }


876
877
878
}


Donal McMullan's avatar
Donal McMullan committed
879
880
class LiveUser extends User {

881
882
    protected $SESSION;

Donal McMullan's avatar
Donal McMullan committed
883
884
885
    public function __construct() {

        parent::__construct();
886
        $this->SESSION = Session::singleton();
Donal McMullan's avatar
Donal McMullan committed
887
888
889
890
891
892
893
894
895

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

896
    /**
897
     * Take a username and password and try to authenticate the
898
899
900
901
902
903
     * user
     *
     * @param  string $username
     * @param  string $password
     * @return bool
     */
904
    public function login($username, $password) {
905
906
        $sql = 'SELECT
                    *, 
907
908
909
910
                    ' . db_format_tsfield('expiry') . ',
                    ' . db_format_tsfield('lastlogin') . ',
                    ' . db_format_tsfield('lastlastlogin') . ',
                    ' . db_format_tsfield('lastaccess') . ',
Richard Mansfield's avatar
Richard Mansfield committed
911
912
                    ' . db_format_tsfield('suspendedctime') . ',
                    ' . db_format_tsfield('ctime') . '
913
914
915
916
917
                FROM
                    {usr}
                WHERE
                    LOWER(username) = ?';
        $user = get_record_sql($sql, array(strtolower($username)));
918

919
920
        if ($user == false) {
            throw new AuthUnknownUserException("\"$username\" is not known");
921
922
        }

923
924
925
        $siteclosedforupgrade = get_config('siteclosed');
        if ($siteclosedforupgrade && get_config('disablelogin')) {
            global $SESSION;
926
            $SESSION->add_error_msg(get_string('siteclosedlogindisabled', 'mahara', get_config('wwwroot') . 'admin/upgrade.php'), false);
927
928
929
930
931
932
933
934
            return false;
        }
        if (!$user->admin && ($siteclosedforupgrade || get_config('siteclosedbyadmin'))) {
            global $SESSION;
            $SESSION->add_error_msg(get_string('siteclosed'));
            return false;
        }

935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
        // 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);
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
        
        // 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 {auth_instance} a ON a.institution = i.name
                    WHERE a.id = ?', array($instanceid));
                if ($authinstance->suspended) {
                    $sitename = get_config('sitename');
                    throw new AccessTotallyDeniedException(get_string('accesstotallydenied_institutionsuspended', 'mahara', $authinstance->displayname, $sitename));
                    return false;
                }
967

968
                return true;
969
            }
970
971
        }
        catch (AuthInstanceException $e) {
972
            return false;
973
        }
974
        
975
976
977
        // Display a message to users who are only allowed to login via their
        // external application.
        if ($auth->authloginmsg != '') {
Elliot Pahl's avatar
Elliot Pahl committed
978
979
            global $SESSION;
            $SESSION->add_info_msg(clean_html($auth->authloginmsg), false);
980
981
        }

982
983
984
        return false;
    }

985
986
987
988
989
990
991
992
993
994
995
    /**
     * Logs the current user out
     */
    public function logout () {
        if ($this->changed == true) {
            log_debug('Destroying user with un-committed changes');
        }
        $this->set('logout_time', 0);
        if ($this->authenticated === true) {
            $this->SESSION->set('messages', array());
        }
996

997
998
        // Unset session variables related to authentication
        $this->SESSION->set('authinstance', null);
999
1000
        if ($this->get('sessionid') && table_exists('usr_session')) {
            delete_records('usr_session', 'session', $this->get('sessionid'));