user.php 34 KB
Newer Older
1
2
<?php
/**
Francois Marier's avatar
Francois Marier committed
3
 * Mahara: Electronic portfolio, weblog, resume builder and social networking
4
 * Copyright (C) 2006-2008 Catalyst IT Ltd (http://www.catalyst.net.nz)
5
 *
Francois Marier's avatar
Francois Marier committed
6
7
8
9
 * 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.
10
 *
Francois Marier's avatar
Francois Marier committed
11
12
13
14
 * 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.
15
 *
Francois Marier's avatar
Francois Marier committed
16
17
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
19
20
 *
 * @package    mahara
 * @subpackage core
21
 * @author     Catalyst IT Ltd
22
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
23
 * @copyright  (C) 2006-2008 Catalyst IT Ltd http://catalyst.net.nz
24
25
26
27
 *
 */

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

30
31

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

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

    /**
     * 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
52
    public function __construct() {
53
        $this->defaults = array(
54
55
56
57
            'logout_time'      => 0,
            'id'               => 0,
            'username'         => '',
            'password'         => '',
Donal McMullan's avatar
Donal McMullan committed
58
            'salt'             => '',
Donal McMullan's avatar
Donal McMullan committed
59
            'passwordchange'   => 0,
Donal McMullan's avatar
Donal McMullan committed
60
            'active'           => 1,
Donal McMullan's avatar
Donal McMullan committed
61
            'deleted'          => 0,
62
            'expiry'           => null,
63
            'expirymailsent'   => 0,
64
            'lastlogin'        => null,
65
            'lastlastlogin'    => null,
66
            'lastaccess'       => null, /* Is not necessarily updated every request, see accesstimeupdatefrequency config variable */
67
            'lastauthinstance' => null,
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'          => '',
94
        );
Donal McMullan's avatar
Donal McMullan committed
95
96
97
98
99
100
101
        $this->attributes = array();

    }

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

Donal McMullan's avatar
Donal McMullan committed
104
105
106
        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
107

108
109
110
111
        $sql = 'SELECT
                    *, 
                    ' . db_format_tsfield('expiry') . ', 
                    ' . db_format_tsfield('lastlogin') . ', 
112
113
                    ' . db_format_tsfield('lastlastlogin') . ',
                    ' . db_format_tsfield('lastaccess') . ',
114
115
                    ' . db_format_tsfield('suspendedctime') . '
                FROM
116
                    {usr}
117
118
119
                WHERE
                    id = ?';

120
        $user = get_record_sql($sql, array($id));
121

Donal McMullan's avatar
Donal McMullan committed
122
123
124
125
        if (false == $user) {
            throw new AuthUnknownUserException("User with id \"$id\" is not known");
        }

Donal McMullan's avatar
Donal McMullan committed
126
        $this->populate($user);
127
        $this->reset_institutions();
128
        $this->reset_grouproles();
Donal McMullan's avatar
Donal McMullan committed
129
130
        return $this;
    }
131

132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
    /**
     * 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') . ',
149
                    ' . db_format_tsfield('lastlastlogin') . ',
150
                    ' . db_format_tsfield('lastaccess') . ',
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
                    ' . db_format_tsfield('suspendedctime') . '
                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;
    }

168
    /**
169
170
171
172
173
     * 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.
174
     */
175
    public function find_by_instanceid_username($instanceid, $username, $remoteuser=false) {
176
177
178
179
180

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

181
        if ($remoteuser) {
182
183
184
185
186
187
188
            // 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 = '';
189
            if ($parentid = get_field('auth_instance_config', 'value', 'field', 'parent', 'instance', $instanceid)) {
190
191
192
                $parentwhere = '
                            OR
                            (
193
194
195
196
197
198
199
200
201
202
203
204
205
                                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) . '
206
207
                            )
                    ';
208
            }
209

210
211
            $sql = 'SELECT
                        u.*, 
212
213
214
215
                        ' . db_format_tsfield('u.expiry', 'expiry') . ',
                        ' . db_format_tsfield('u.lastlogin', 'lastlogin') . ',
                        ' . db_format_tsfield('u.lastlastlogin', 'lastlastlogin') . ',
                        ' . db_format_tsfield('u.lastaccess', 'lastaccess') . ',
216
217
                        ' . db_format_tsfield('u.suspendedctime', 'suspendedctime') . '
                    FROM {usr} u
218
                    LEFT JOIN {auth_remote_user} r ON u.id = r.localusr
219
                    WHERE
220
221
222
223
224
225
226
                        (
                            (
                                LOWER(r.remoteusername) = ?
                                AND r.authinstance = ?
                            )'
                            . $parentwhere
                            . '
227
                        )';
228
229
230
            $user = get_record_sql($sql, array($username, $instanceid));
        }
        else {
231
232
            $sql = 'SELECT
                        *, 
233
234
235
236
                        ' . db_format_tsfield('expiry') . ',
                        ' . db_format_tsfield('lastlogin') . ',
                        ' . db_format_tsfield('lastlastlogin') . ',
                        ' . db_format_tsfield('lastaccess') . ',
237
238
239
240
241
                        ' . db_format_tsfield('suspendedctime') . '
                    FROM
                        {usr}
                    WHERE
                        LOWER(username) = ? AND
242
                        authinstance = ' . db_quote($instanceid);
243
            $user = get_record_sql($sql, array($username));
244
245
        }

246
247
248
249
250
251
252
253
        if (false == $user) {
            throw new AuthUnknownUserException("User with username \"$username\" is not known at auth instance \"$instanceid\"");
        }

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

Donal McMullan's avatar
Donal McMullan committed
254
255
256
257
258
259
260
261
262
263
264
265
266
    /**
     * 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});
            }
        }
267
    }
Donal McMullan's avatar
Donal McMullan committed
268

269
270
271
272
273
    /**
     * 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
274
     * @throws InvalidArgumentException
275
276
     */
    public function get($key) {
Donal McMullan's avatar
Donal McMullan committed
277
        if (!array_key_exists($key, $this->defaults)) {
278
            throw new InvalidArgumentException($key);
279
        }
Donal McMullan's avatar
Donal McMullan committed
280
281
        if (array_key_exists($key, $this->attributes) && null !== $this->attributes[$key]) {
            return $this->attributes[$key];
282
283
284
285
        }
        return $this->defaults[$key];
    }

Donal McMullan's avatar
Donal McMullan committed
286
287
288
289
290
291
292
293
294
295
296
    /**
     * 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);
    }

297
298
299
    /**
     * Sets the property keyed by $key
     */
Donal McMullan's avatar
Donal McMullan committed
300
301
302
    protected function set($key, $value) {

        if (!array_key_exists($key, $this->defaults)) {
303
            throw new InvalidArgumentException($key);
304
        }
Donal McMullan's avatar
Donal McMullan committed
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332

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

        // For now, these fields are saved to the DB elsewhere
        if ($key != 'activityprefs' && $key !=  'accountprefs') {
            $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
333
        $record = $this->to_stdclass();
Donal McMullan's avatar
Donal McMullan committed
334
        if (is_numeric($this->id) && 0 < $this->id) {
Donal McMullan's avatar
Donal McMullan committed
335
336
337
338
339
340
            try {
                update_record('usr', $record, array('id' => $this->id));
            } catch (Exception $e) {
                throw $e;
                //var_dump($e);
            }
Donal McMullan's avatar
Donal McMullan committed
341
        } else {
Donal McMullan's avatar
Donal McMullan committed
342
343
            try {
                $this->set('id', insert_record('usr', $record, 'id', true));
Donal McMullan's avatar
Donal McMullan committed
344
345
            } catch (SQLException $e) {
                throw $e;
Donal McMullan's avatar
Donal McMullan committed
346
            }
Donal McMullan's avatar
Donal McMullan committed
347
348
        }
        $this->changed = false;
349
350
351
352
353
354
    }

    /** 
     * This function returns a method for a particular
     * activity type, or null if it's not set.
     * 
355
     * @param int $key the activity type id
356
357
358
     */
    public function get_activity_preference($key) {
        $activityprefs = $this->get('activityprefs');
359
        return isset($activityprefs[$key]) ? $activityprefs[$key] : null;
360
    }
Donal McMullan's avatar
Donal McMullan committed
361

362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    /** @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
380

381
382
383
384
385
386
    /** @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);
387
388
    }

389
390
391
392
393
394
395
396
397
398
    /**
     * 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() {
        $viewid = get_field('view', 'id', 'type', 'profile', 'owner', $this->get('id'));
        if (!$viewid) {
399
400
401
402
            global $USER;
            if (!$USER->get('id')) {
                return null;
            }
403
404
405
406
407
408
409
410
411
412
413
414
415
            return $this->install_profile_view();
        }
        return new View($viewid);
    }

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

416
        db_begin();
417
418
419
420
421
422
423
424
425
426
        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),
            'type'  => 'profile',
        ), $systemprofileviewid, $this->get('id'));
427
428
429
430
431
432
433

        // Add about me block
        $aboutme = new BlockInstance(0, array(
            'blocktype'  => 'profileinfo',
            'title'      => get_string('aboutme', 'blocktype.internal/profileinfo'),
            'view'       => $view->get('id'),
            'column'     => 1,
434
            'order'      => 1,
435
436
437
438
439
440
        ));
        $configdata = array('artefactids' => array());
        if ($intro = get_field('artefact', 'id', 'owner', $this->get('id'), 'artefacttype', 'introduction')) {
            $configdata['artefactids'][] = $intro;
        }
        else {
441
            $configdata['introtext'] = get_string('thisistheprofilepagefor', 'mahara', display_name($this, null, true));
442
443
444
445
446
        }
        if ($this->get('profileicon')) {
            $configdata['profileicon'] = $this->get('profileicon');
        }
        $aboutme->set('configdata', $configdata);
447
        $view->addblockinstance($aboutme);
448

449
450
451
452
453
454
455
        $view->set_access(array(
            array(
                'type'      => 'loggedin',
                'startdate' => null,
                'stopdate'  => null,
            ),
        ));
456
        db_commit();
457
458
459
460
461

        return $view;
    }


462

463
464
465
466
467
468
469
470
    /**
     * 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
471

472
    public function to_stdclass() {
Donal McMullan's avatar
Donal McMullan committed
473
474
475
        $this->stdclass = new StdClass;
        reset($this->defaults);
        foreach (array_keys($this->defaults) as $k) {
476
            if ($k == 'expiry' || $k == 'lastlogin' || $k == 'lastlastlogin' || $k == 'lastaccess' || $k == 'suspendedctime') {
Donal McMullan's avatar
Donal McMullan committed
477
478
479
480
                $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));
            }
481
482
483
        }
        return $this->stdclass;
    }
484

Martyn Smith's avatar
Martyn Smith committed
485
486
487
488
489
490
491
492
    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
493
494
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
495
496
497
498
499
500
501
502
503
504
    }

    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
505
506
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
507
508
509
510
511
512
513
514
515
    }

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

        return true;
    }
516
517

    public function join_institution($institution) {
518
        if ($institution != 'mahara' && !$this->in_institution($institution)) {
519
            require_once('institution.php');
520
521
            $institution = new Institution($institution);
            $institution->addUserAsMember($this);
522
            $this->reset_institutions();
523
524
525
        }
    }

526
527
528
529
    public function leave_institution($institution) {
        if ($institution != 'mahara' && $this->in_institution($institution)) {
            require_once('institution.php');
            $institution = new Institution($institution);
530
            $institution->removeMember($this->to_stdclass());
531
532
533
        }
    }

534
535
536
537
538
539
    public function in_institution($institution, $role = null) {
        $institutions = $this->get('institutions');
        return isset($institutions[$institution]) 
            && (is_null($role) || $institutions[$institution]->{$role});
    }

540
    public function is_institutional_admin($institution = null) {
541
        $a = $this->get('admininstitutions');
542
543
544
545
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
546
    }
547

548
549
550
551
552
553
554
555
    public function is_institutional_staff($institution = null) {
        $a = $this->get('staffinstitutions');
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
    }

556
557
558
559
    public function can_edit_institution($institution = null) {
        return $this->get('admin') || $this->is_institutional_admin($institution);
    }

560
561
562
563
    public function is_admin_for_user($user) {
        if ($this->get('admin')) {
            return true;
        }
564
565
566
567
568
569
570
571
572
        if (!$this->is_institutional_admin()) {
            return false;
        }
        if ($user instanceof User) {
            $userinstitutions = $user->get('institutions');
        } else {
            $userinstitutions = load_user_institutions($user->id);
        }
        foreach ($userinstitutions as $i) {
573
574
575
576
577
578
579
            if ($this->is_institutional_admin($i->institution)) {
                return true;
            }
        }
        return false;
    }

580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
    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;
    }

601
    public function set_admin_institutions($institutions) {
602
603
604
605
606
        if (empty($institutions)) {
            $this->set('admininstitutions', array());
        } else {
            $this->set('admininstitutions', array_combine($institutions, $institutions));
        }
607
608
    }

609
    public function add_institution_request($institution, $studentid = null) {
610
611
612
        if (empty($institution) || $institution == 'mahara') {
            return;
        }
613
614
        require_once('institution.php');
        $institution = new Institution($institution);
615
        $institution->addRequestFromUser($this, $studentid);
616
617
    }

618
619
620
    protected function reset_institutions() {
        $institutions             = load_user_institutions($this->id);
        $admininstitutions = array();
621
        $staffinstitutions = array();
622
        $this->theme = get_config('theme');
623
624
625
626
        foreach ($institutions as $i) {
            if ($i->admin) {
                $admininstitutions[$i->institution] = $i->institution;
            }
627
628
629
            if ($i->staff) {
                $staffinstitutions[$i->institution] = $i->institution;
            }
630
631
            if (!empty($i->theme) && $i->theme != get_config('theme')) {
                $this->theme = $i->theme;
632
            }
633
634
635
        }
        $this->institutions       = $institutions;
        $this->admininstitutions  = $admininstitutions;
636
        $this->staffinstitutions  = $staffinstitutions;
637
638
    }

639
640
641
642
643
644
645
646
647
648
649
    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);
    }

650
    public function can_view_artefact($a) {
651
        if ($this->get('admin')
652
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
653
654
655
656
657
658
659
            || $this->is_institutional_admin($a->get('institution'))) {
            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
660
                WHERE ar.artefact = ? AND g.member = ? AND ar.can_view = 1 AND g.group = ?", array($a->get('id'), $this->get('id'), $a->get('group')))
661
662
663
                || record_exists('artefact_access_usr', 'usr', $this->get('id'), 'artefact', $a->get('id'));
        }
        return false;
664
665
    }

Richard Mansfield's avatar
Richard Mansfield committed
666
667
    public function can_edit_artefact($a) {
        if ($this->get('admin')
668
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
Richard Mansfield's avatar
Richard Mansfield committed
669
670
671
672
673
            || $this->is_institutional_admin($a->get('institution'))) {
            return true;
        }
        $group = $a->get('group');
        if ($group) {
674
675
676
677
            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));
            /*
678
679
            require_once(get_config('docroot') . 'lib/group.php');
            $role = group_user_access($group, $this->get('id'));
Richard Mansfield's avatar
Richard Mansfield committed
680
681
682
            if ($role) {
                $aperms = $a->get('rolepermissions');
                return $aperms->{$role}->edit;
683
            } */
Richard Mansfield's avatar
Richard Mansfield committed
684
685
686
687
        }
        return false;
    }

688
689
690
691
692
    public function can_edit_view($v) {
        $owner = $v->get('owner');
        if ($owner == $this->get('id')) {
            return true;
        }
693
        $institution = $v->get('institution');
694
        if ($institution && $this->can_edit_institution($institution)) {
695
696
            return true;
        }
697
698
699
        $group = $v->get('group');
        if ($group) {
            $editroles = $v->get('editingroles');
700
            $this->reset_grouproles();
Richard Mansfield's avatar
Richard Mansfield committed
701
            return isset($this->grouproles[$group]) && in_array($this->grouproles[$group], $editroles);
702
703
704
705
        }
        return false;
    }

706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
    /**
     * Makes a literal copy of a list of views for this user.
     *
     * @param array $templateids A list of viewids to copy.
     */
    public function copy_views($templateids) {
        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();
        foreach (get_records_select_array('view', 'id IN (' . implode(', ', db_array_to_ph($templateids)) . ')', $templateids, '', 'id, title, description') as $result) {
            $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,
732
            ), $tid, $this->get('id'));
733
734
735
736
737
        }
        db_commit();
    }


738
739
740
}


Donal McMullan's avatar
Donal McMullan committed
741
742
class LiveUser extends User {

743
744
    protected $SESSION;

Donal McMullan's avatar
Donal McMullan committed
745
746
747
    public function __construct() {

        parent::__construct();
748
        $this->SESSION = Session::singleton();
Donal McMullan's avatar
Donal McMullan committed
749
750
751
752
753
754
755
756
757

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

758
    /**
759
     * Take a username and password and try to authenticate the
760
761
762
763
764
765
     * user
     *
     * @param  string $username
     * @param  string $password
     * @return bool
     */
766
    public function login($username, $password) {
767
768
        $sql = 'SELECT
                    *, 
769
770
771
772
                    ' . db_format_tsfield('expiry') . ',
                    ' . db_format_tsfield('lastlogin') . ',
                    ' . db_format_tsfield('lastlastlogin') . ',
                    ' . db_format_tsfield('lastaccess') . ',
773
774
775
776
777
778
                    ' . db_format_tsfield('suspendedctime') . '
                FROM
                    {usr}
                WHERE
                    LOWER(username) = ?';
        $user = get_record_sql($sql, array(strtolower($username)));
779

780
781
        if ($user == false) {
            throw new AuthUnknownUserException("\"$username\" is not known");
782
783
        }

784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
        // 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);
800
801
        if ($auth->authenticate_user_account($user, $password)) {
            $user->lastauthinstance = $auth->instanceid;
802
            $this->authenticate($user, $auth->instanceid);
803
            return true;
804
805
        }

806
807
808
809
810
811
812
        // Display a message to users who are only allowed to login via their
        // external application.
        if ($auth->authloginmsg != '') {
          global $SESSION;
          $SESSION->add_error_msg($auth->authloginmsg);
        }

813
814
815
        return false;
    }

816
817
818
819
820
821
822
823
824
825
826
    /**
     * 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());
        }
827

828
829
        // Unset session variables related to authentication
        $this->SESSION->set('authinstance', null);
830

831
832
833
834
835
836
837
838
839
        reset($this->defaults);
        foreach (array_keys($this->defaults) as $key) {
            $this->set($key, $this->defaults[$key]);
        }
        // We don't want to commit the USER object after logout:
        $this->changed = false;
    }

    /**
840
841
     * Updates information in a users' session once we know their session is 
     * continuing
842
843
     */
    public function renew() {
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
        $time = time();
        $this->set('logout_time', $time + get_config('session_timeout'));
        $oldlastaccess = $this->get('lastaccess');
        // If there is an access time update frequency, we use a cookie to 
        // prevent updating before this time has expired.
        // If it is set to zero, we always update the accesstime.
        $accesstimeupdatefrequency = get_config('accesstimeupdatefrequency');
        if ($accesstimeupdatefrequency == 0) {
            $this->set('lastaccess', $time);
            $this->commit();
        }
        else if ($oldlastaccess + $accesstimeupdatefrequency < $time) {
            $this->set('lastaccess', $time);
            $this->commit();
        }
859
860
    }

861
862
863
864
    /**
     * When a user creates a security context by whatever method, we do some 
     * standard stuff
     *
865
866
867
     * @param  object $user          Record from the usr table
     * @param  integer $authinstance The ID of the authinstance that the user 
     *                               signed in with
868
869
     * @return void
     */
870
    protected function authenticate($user, $authinstance) {
871
        $this->authenticated  = true;
872
873
874
875
876
877
878
879
880
881

        // If the user has reauthenticated and they were an MNET user, we 
        // don't set these variables, because we wish to remember that they 
        // originally SSO-ed in from their other authinstance. See the 
        // session timeout code in auth_setup() for more info.
        if ($this->SESSION->get('mnetuser') != $user->id) {
            $this->SESSION->set('mnetuser', null);
            $this->SESSION->set('authinstance', $authinstance);
        }

882
883
        $this->populate($user);
        session_regenerate_id(true);
884
        $this->lastlastlogin      = $this->lastlogin;
Donal McMullan's avatar
Donal McMullan committed
885
        $this->lastlogin          = time();
886
        $this->lastaccess         = time();
887
888
889
        $this->sessionid          = session_id();
        $this->logout_time        = time() + get_config('session_timeout');
        $this->sesskey            = get_random_key();
Donal McMullan's avatar
Donal McMullan committed
890
891
892

        // We need a user->id before we load_c*_preferences
        if (empty($user->id)) $this->commit();
893
894
        $this->activityprefs      = load_activity_preferences($user->id);
        $this->accountprefs       = load_account_preferences($user->id);
895
        $this->reset_institutions();
896
        $this->reset_grouproles();
897
898
899
        $this->commit();
    }

900
901
902
903
    /**
     * When a user creates a security context by whatever method, we do some 
     * standard stuff
     *
904
905
906
     * @param  int  $user       User ID
     * @param  int  $instanceid Auth Instance ID
     * @return bool             True if user with given ID exists
907
     */
908
    public function reanimate($id, $instanceid) {
909
        if ($user = get_record('usr','id',$id)) {
910
            $user->lastauthinstance = $instanceid;
911
            $this->authenticate($user, $instanceid);
912
913
914
915
916
            return true;
        }
        return false;
    }

Donal McMullan's avatar
Donal McMullan committed
917
918
919
920
921
922
923
924
925
    /**
     * 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) {
        if (!array_key_exists($key, $this->defaults)) {
926
            throw new InvalidArgumentException("Invalid key: $key");
Donal McMullan's avatar
Donal McMullan committed
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
        }
        if (null !== ($value = $this->SESSION->get("user/$key"))) {
            return $value;
        }
        return $this->defaults[$key];
    }

    /**
     * Sets the property keyed by $key
     */
    protected function set($key, $value) {

        if (!array_key_exists($key, $this->defaults)) {
            throw new InvalidArgumentException($key);
        }

        // For now, these fields are saved to the DB elsewhere
        if ($key != 'activityprefs' && $key !=  'accountprefs') {
            $this->changed = true;
        }
        $this->SESSION->set("user/$key", $value);
        return $this;
    }
Richard Mansfield's avatar
Richard Mansfield committed
950

951
952
953
954
    protected function reloadLiveUser($id=null) {
        if (is_null($id)) {
            $id = $this->get('id');
        }
Richard Mansfield's avatar
Richard Mansfield committed
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
        $this->find_by_id($id);
        $this->activityprefs = load_activity_preferences($id);
        $this->accountprefs = load_account_preferences($id);
    }

    public function change_identity_to($userid) {
        $user = new User;
        $user->find_by_id($userid);
        if (!$this->is_admin_for_user($user)) {
            throw new AccessDeniedException(get_string('loginasdenied', 'admin'));
        }
        $olduser = $this->get('parentuser');
        if (!is_null($olduser)) {
            throw new UserException(get_string('loginastwice', 'admin'));
        }

        $olduser = new StdClass;
        $olduser->id = $this->get('id');
        $olduser->name = $this->firstname . ' ' . $this->lastname;

        $this->reloadLiveUser($userid);

        $this->set('parentuser', $olduser);
    }

    public function restore_identity() {
        $id = $this->get('id');
        $olduser = $this->get('parentuser');
        if (empty($olduser) || empty($olduser->id)) {
            throw new UserException(get_string('loginasrestorenodata', 'admin'));
        }

        $this->reloadLiveUser($olduser->id);
        $this->set('parentuser', null);
989
        $this->set('loginanyway', false);
Richard Mansfield's avatar
Richard Mansfield committed
990
991
992
993

        return $id;
    }

994
995
996
997
998
999
1000
    public function leave_institution($institution) {
        parent::leave_institution($institution);
        $this->find_by_id($this->get('id'));
        $this->reset_institutions();
    }


Donal McMullan's avatar
Donal McMullan committed
1001
}
Donal McMullan's avatar
Donal McMullan committed
1002
?>