user.php 58.6 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 288
                            WHERE p.field=\'mobileuploadtoken\'
                              AND p.value ' . db_ilike() . ' \'%|\' || ? || \'|%\' AND u.username = ?
289 290
		';

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

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

        $this->populate($user);
298
        $this->accountprefs = load_account_preferences($user->id);
299 300 301
        return $this;
    }

302 303 304 305
    /**
     * Refreshes a users mobile 'token' and returns it
     *
     */
306 307 308 309 310 311 312 313 314 315 316
    public function refresh_mobileuploadtoken($old_token) {
        $new_token = md5(openssl_random_pseudo_bytes(8));
        $old_tokenstring = $this->get_account_preference('mobileuploadtoken');
        $tokenarray = explode('|', trim($old_tokenstring, '|'));
        foreach ($tokenarray as $k => $v) {
            if ( $v == $old_token ) {
                $tokenarray[$k] = $new_token;
            }
        }
        $new_tokenstring = empty($tokenarray) ? null : ('|' . join('|', $tokenarray) . '|');
        $this->set_account_preference('mobileuploadtoken', $new_tokenstring);
317
        $this->set('lastaccess', time());
318 319
        $this->commit();
        return $new_token;
320
    }
321

Richard Mansfield's avatar
Richard Mansfield committed
322 323 324 325 326
    /**
     * Set stuff that needs to be initialised once before a user record is created.
     */
    public function create() {
        $this->set('ctime', time());
327 328 329 330
        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
331 332 333
    }


Donal McMullan's avatar
Donal McMullan committed
334 335 336 337 338 339 340 341 342 343 344 345 346
    /**
     * 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});
            }
        }
347
    }
Donal McMullan's avatar
Donal McMullan committed
348

349 350 351 352 353 354 355 356 357 358 359 360 361 362 363
    /**
     * 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;
    }

364 365 366 367 368
    /**
     * 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
369
     * @throws InvalidArgumentException
370 371
     */
    public function get($key) {
Donal McMullan's avatar
Donal McMullan committed
372
        if (!array_key_exists($key, $this->defaults)) {
373
            throw new InvalidArgumentException($key);
374
        }
Donal McMullan's avatar
Donal McMullan committed
375 376
        if (array_key_exists($key, $this->attributes) && null !== $this->attributes[$key]) {
            return $this->attributes[$key];
377 378 379 380
        }
        return $this->defaults[$key];
    }

Donal McMullan's avatar
Donal McMullan committed
381 382 383 384 385 386 387 388 389 390 391
    /**
     * 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);
    }

392 393 394
    /**
     * Sets the property keyed by $key
     */
Donal McMullan's avatar
Donal McMullan committed
395 396 397
    protected function set($key, $value) {

        if (!array_key_exists($key, $this->defaults)) {
398
            throw new InvalidArgumentException($key);
399
        }
Donal McMullan's avatar
Donal McMullan committed
400 401 402 403

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

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

    /** 
     * This function returns a method for a particular
     * activity type, or null if it's not set.
     * 
450
     * @param int $key the activity type id
451 452 453
     */
    public function get_activity_preference($key) {
        $activityprefs = $this->get('activityprefs');
454
        return isset($activityprefs[$key]) ? $activityprefs[$key] : null;
455
    }
Donal McMullan's avatar
Donal McMullan committed
456

457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474
    /** @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
475

476 477
    /** @todo document this method */
    public function set_account_preference($field, $value) {
478 479 480
        if ($id = $this->get('id')) {
            set_account_preference($id, $field, $value);
        }
481 482 483
        $accountprefs = $this->get('accountprefs');
        $accountprefs[$field] = $value;
        $this->set('accountprefs', $accountprefs);
484 485
    }

486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504

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

505 506 507 508 509 510 511 512
    /**
     * 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() {
513
        return $this->get_view_by_type('profile');
514 515 516 517 518 519 520
    }

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

524
        db_begin();
525 526 527 528 529 530 531 532
        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),
533
            'description' => get_string('profiledescription'),
534 535
            'type'  => 'profile',
        ), $systemprofileviewid, $this->get('id'));
536 537 538 539 540 541 542

        // Add about me block
        $aboutme = new BlockInstance(0, array(
            'blocktype'  => 'profileinfo',
            'title'      => get_string('aboutme', 'blocktype.internal/profileinfo'),
            'view'       => $view->get('id'),
            'column'     => 1,
543
            'order'      => 1,
544 545 546 547 548 549
        ));
        $configdata = array('artefactids' => array());
        if ($intro = get_field('artefact', 'id', 'owner', $this->get('id'), 'artefacttype', 'introduction')) {
            $configdata['artefactids'][] = $intro;
        }
        else {
550
            $configdata['introtext'] = '';
551 552 553 554 555
        }
        if ($this->get('profileicon')) {
            $configdata['profileicon'] = $this->get('profileicon');
        }
        $aboutme->set('configdata', $configdata);
556
        $view->addblockinstance($aboutme);
557

558
        // Set view access
559 560
        $access = array(
            array(
561 562 563
                'type'      => 'loggedin',
                'startdate' => null,
                'stopdate'  => null,
564 565
            ),
        );
566 567 568 569 570 571 572 573 574 575 576 577
        if ($institutions = $this->get('institutions')) {
            foreach ($institutions as $i) {
                $access[] = array(
                    'type'      => 'institution',
                    'id'        => $i->institution,
                    'startdate' => null,
                    'stopdate'  => null,
                );
            }
        }
        $view->set_access($access);

578
        db_commit();
579 580 581 582

        return $view;
    }

583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
    /**
     * 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),
608
            'description' => get_string('dashboarddescription'),
609 610 611 612 613 614 615
            'type'  => 'dashboard',
        ), $systemdashboardviewid, $this->get('id'));

        db_commit();

        return $view;
    }
616

617 618 619 620
    protected function install_view($viewtype) {
        $function = 'install_' . $viewtype . '_view';
        return $this->$function();
    }
621

622 623 624 625 626 627
    // 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
628
            '"owner" = ? AND type IN (' . join(',', array_map('db_quote', $types)) . ')',
629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645
            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);
    }
646

647 648 649 650 651 652 653 654
    /**
     * 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
655

656
    public function to_stdclass() {
Donal McMullan's avatar
Donal McMullan committed
657 658 659
        $this->stdclass = new StdClass;
        reset($this->defaults);
        foreach (array_keys($this->defaults) as $k) {
Richard Mansfield's avatar
Richard Mansfield committed
660
            if ($k == 'expiry' || $k == 'lastlogin' || $k == 'lastlastlogin' || $k == 'lastaccess' || $k == 'suspendedctime' || $k == 'ctime') {
Donal McMullan's avatar
Donal McMullan committed
661 662 663 664
                $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));
            }
665 666 667
        }
        return $this->stdclass;
    }
668

Martyn Smith's avatar
Martyn Smith committed
669 670 671 672 673 674 675 676
    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
677 678
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
679 680 681 682 683 684 685 686 687 688
    }

    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
689 690
        $this->set("quotaused", $newquota);
        return $this;
Martyn Smith's avatar
Martyn Smith committed
691 692 693 694 695 696 697 698 699
    }

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

        return true;
    }
700

701 702 703 704 705 706 707 708
    public function quota_init() {
        if (!$this->get('quota')) {
            if ($defaultquota = get_config_plugin('artefact', 'file', 'defaultquota')) {
                $this->set('quota', $defaultquota);
            }
        }
    }

709 710 711 712 713 714
    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);
    }

715
    public function join_institution($institution) {
716
        if ($institution != 'mahara' && !$this->in_institution($institution)) {
717
            require_once('institution.php');
718 719
            $institution = new Institution($institution);
            $institution->addUserAsMember($this);
720
            $this->reset_institutions();
721 722 723
        }
    }

724 725 726 727
    public function leave_institution($institution) {
        if ($institution != 'mahara' && $this->in_institution($institution)) {
            require_once('institution.php');
            $institution = new Institution($institution);
728
            $institution->removeMember($this->to_stdclass());
729 730 731
        }
    }

732 733 734 735 736 737
    public function in_institution($institution, $role = null) {
        $institutions = $this->get('institutions');
        return isset($institutions[$institution]) 
            && (is_null($role) || $institutions[$institution]->{$role});
    }

738
    public function is_institutional_admin($institution = null) {
739
        $a = $this->get('admininstitutions');
740 741 742 743
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
744
    }
745

746 747 748 749 750 751 752 753
    public function is_institutional_staff($institution = null) {
        $a = $this->get('staffinstitutions');
        if (is_null($institution)) {
            return !empty($a);
        }
        return isset($a[$institution]);
    }

754 755 756 757
    public function can_edit_institution($institution = null, $staff = false) {
        if ($staff) {
            return $this->get('admin') || $this->get('staff') || $this->is_institutional_admin($institution) || $this->is_institutional_staff($institution);
        }
758 759 760
        return $this->get('admin') || $this->is_institutional_admin($institution);
    }

761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
    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;
    }

778 779 780 781 782 783 784 785
    /**
     * 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
     */
786 787 788 789
    public function is_admin_for_user($user) {
        if ($this->get('admin')) {
            return true;
        }
790 791 792
        if (!$this->is_institutional_admin()) {
            return false;
        }
793 794

        // Check privileges for institutional admins now
795
        if ($user instanceof User) {
796
            $userobj = $user;
797 798
        }
        else if (is_numeric($user)) {
799 800
            $userobj = new User;
            $userobj->find_by_id($user);
801 802 803
        }
        else if (is_object($user)) {
            // Should be a row from the usr table
804 805
            $userobj = new User;
            $userobj->find_by_id($user->id);
806
        }
807 808 809 810
        else {
            throw new SystemException("Invalid argument pass to is_admin_for_user method");
        }

811 812 813 814 815
        if ($userobj->get('admin')) {
            return false;
        }

        foreach ($userobj->get('institutions') as $i) {
816 817 818 819 820 821 822
            if ($this->is_institutional_admin($i->institution)) {
                return true;
            }
        }
        return false;
    }

823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843
    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;
    }

844
    public function add_institution_request($institution, $studentid = null) {
845 846 847
        if (empty($institution) || $institution == 'mahara') {
            return;
        }
848 849
        require_once('institution.php');
        $institution = new Institution($institution);
850
        $institution->addRequestFromUser($this, $studentid);
851 852
    }

853
    public function reset_institutions($nocachecss=false) {
854 855
        $institutions             = load_user_institutions($this->id);
        $admininstitutions = array();
856
        $staffinstitutions = array();
857 858
        $themename = get_config('theme');
        $headerlogo = null;
859
        $stylesheets = array();
860 861
        $themeinstitution = null;
        foreach ($institutions as $name => $i) {
862 863 864
            if ($i->admin) {
                $admininstitutions[$i->institution] = $i->institution;
            }
865 866 867
            if ($i->staff) {
                $staffinstitutions[$i->institution] = $i->institution;
            }
868 869
            if (is_null($themeinstitution)) {
                $themeinstitution = $name;
870
            }
871
        }
872 873 874
        if ($this->authinstance) {
            $authobj = AuthFactory::create($this->authinstance);
            if (isset($institutions[$authobj->institution])) {
875 876
                if ($institutions[$authobj->institution]->theme) {
                    $themeinstitution = $authobj->institution;
877 878 879
                }
            }
        }
880
        if (!is_null($themeinstitution)) {
881 882
            $themename  = $institutions[$themeinstitution]->theme;
            $headerlogo = $institutions[$themeinstitution]->logo;
883
            if ($institutions[$themeinstitution]->style) {
884 885 886 887 888
                $stylesheet = get_config('wwwroot') . 'style.php?id=' . $institutions[$themeinstitution]->style;
                if ($nocachecss) {
                    $stylesheet .= '&time=' . time();
                }
                $stylesheets[] = $stylesheet;
889
            }
890
        }
891 892 893 894
        $this->institutiontheme = (object) array(
            'basename'    => $themename,
            'headerlogo'  => $headerlogo,
            'stylesheets' => $stylesheets,
895
            'institutionname' => $themeinstitution,
896
        );
897 898
        $this->institutions       = $institutions;
        $this->admininstitutions  = $admininstitutions;
899
        $this->staffinstitutions  = $staffinstitutions;
900 901
    }

902
    public function get_themedata() {
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927
        $preftheme = $this->get_account_preference('theme');
        if (!empty($preftheme)) {
            // the format of preferred theme: <theme name>/<institution name>
            // This format is created by the function general_account_prefs_form_elements()
            $list = (explode('/', $preftheme));
            if (count($list) > 1) {
                $iid = $list[1];
                $institutions = load_user_institutions($this->id);
                if (isset($institutions[$iid])) {
                    $institution = $institutions[$iid];
                    $stylesheets = array();
                    if ($institution->style) {
                        $stylesheets[] = get_config('wwwroot') . 'style.php?id=' . $institution->style;
                    }
                    return (object) array(
                        'basename'    => $institution->theme,
                        'headerlogo'  => $institution->logo,
                        'stylesheets' => $stylesheets,
                        'institutionname' => $iid,
                    );
                }
            }
            else if (!empty($list[0]) && get_config('sitethemeprefs')) {
                return (object) array('basename' => $list[0]);
            }
928
        }
929 930 931
        // The current user has not picked a preferred theme yet
        // or his current preferred theme is not available for him
        // The system will pick one for him
932
        return $this->institutiontheme;
933 934
    }

935 936 937 938 939 940 941 942 943 944 945
    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);
    }

946
    public function can_view_artefact($a) {
947 948
        global $USER;

949 950 951 952 953 954
        $parent = $a->get_parent_instance();
        if ($parent) {
            if (!$this->can_view_artefact($parent)) {
                return false;
            }
        }
955
        if ($this->get('admin')
956
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
957
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
958 959
            return true;
        }
960 961 962 963 964 965 966 967 968
        // public site files
        else if ($a->get('institution') == 'mahara') {
            $thisparent = $a->get('parent');
            // if we are looking at the public folder or items in it
            if (($a->get('id') == ArtefactTypeFolder::admin_public_folder_id())
                ||  (!empty($thisparent) && $thisparent == ArtefactTypeFolder::admin_public_folder_id())) {
                return true;
            }
        }
969 970 971 972
        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
973
                WHERE ar.artefact = ? AND g.member = ? AND ar.can_view = 1 AND g.group = ?", array($a->get('id'), $this->get('id'), $a->get('group')))
974 975 976
                || record_exists('artefact_access_usr', 'usr', $this->get('id'), 'artefact', $a->get('id'));
        }
        return false;
977 978
    }

979 980 981 982 983 984 985 986 987 988 989 990 991 992
    public function can_edit_artefact($a, $viewparent=false) {
        $parent = $a->get_parent_instance();
        if ($parent) {
            if ($viewparent) {
                if (!$this->can_view_artefact($parent)) {
                    return false;
                }
            }
            else {
                if (!$this->can_edit_artefact($parent, true)) {
                    return false;
                }
            }
        }
Richard Mansfield's avatar
Richard Mansfield committed
993
        if ($this->get('admin')
994
            || ($this->get('id') and $this->get('id') == $a->get('owner'))
995
            || ($a->get('institution') and $this->is_institutional_admin($a->get('institution')))) {
Richard Mansfield's avatar
Richard Mansfield committed
996 997
            return true;
        }
998 999 1000

        if (!$group = $a->get('group')) {
            return false;
Richard Mansfield's avatar
Richard Mansfield committed
1001
        }
1002 1003 1004 1005 1006 1007 1008 1009

        require_once('group.php');
        if (!$role = group_user_access($group, $this->id)) {
            return false;
        }
        if ($role == 'admin') {
            return true;
        }
1010 1011 1012
        if (!group_within_edit_window($group)) {
            return false;
        }
1013 1014 1015 1016 1017
        if ($this->id == $a->get('author')) {
            return true;
        }

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

1020
    public function can_publish_artefact($a) {