errors.php 23.2 KB
Newer Older
1
2
<?php
/**
3
 * This program is part of Mahara
4
 *
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 *  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 2 of the License, or
 *  (at your option) any later version.
 *
 *  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.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 *
 * @package    mahara
Penny Leach's avatar
Penny Leach committed
20
 * @subpackage core
21
22
23
 * @author     Nigel McNie <nigel@catalyst.net.nz>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @copyright  (C) 2006,2007 Catalyst IT Ltd http://catalyst.net.nz
24
25
26
 *
 */

27
28
defined('INTERNAL') || die();

29
30
31
/**#@+
 * @access private
 */
32
// These are bitmaps - the next one should be 4
33
/** Display the errors on the screen */
34
define('LOG_TARGET_SCREEN', 1);
35
/** Write the errors to the error log, as specified in your php configuration */
36
37
38
define('LOG_TARGET_ERRORLOG', 2);

// Logging levels
39
/** Environment type errors, such as register_globals being on */
40
define('LOG_LEVEL_ENVIRON', 1);
41
/** Debug messages */
42
define('LOG_LEVEL_DBG', 2);
43
/** Informational messages */
44
define('LOG_LEVEL_INFO', 4);
45
/** Warnings */
46
define('LOG_LEVEL_WARN', 8);
47
/**#@-*/
48

49
// Tell PHP about our error settings
50
error_reporting(E_ALL);
51
52
set_error_handler('error');
set_exception_handler('exception');
53
54
55
56


// Logging functions

57
58
59
60
61
62
63
64
65
66
/**
 * Logs a message at the debug level
 *
 * @param string $message   The message to display
 * @param bool   $escape    Whether to HTML escape the message
 * @param bool   $backtrace Whether to provide a backtrace if the system is
 *                          configured to give backtraces at this level.
 */
function log_debug ($message, $escape=true, $backtrace=true) {
    log_message($message, LOG_LEVEL_DBG, $escape, $backtrace);
67
68
}

69
70
71
72
73
74
75
76
77
78
/**
 * Logs a message at the info level
 *
 * @param string $message   The message to display
 * @param bool   $escape    Whether to HTML escape the message
 * @param bool   $backtrace Whether to provide a backtrace if the system is
 *                          configured to give backtraces at this level.
 */
function log_info ($message, $escape=true, $backtrace=true) {
    log_message($message, LOG_LEVEL_INFO, $escape, $backtrace);
79
80
}

81
82
83
84
85
86
87
88
89
90
/**
 * Logs a message at the warning level
 *
 * @param string $message   The message to display
 * @param bool   $escape    Whether to HTML escape the message
 * @param bool   $backtrace Whether to provide a backtrace if the system is
 *                          configured to give backtraces at this level.
 */
function log_warn ($message, $escape=true, $backtrace=true) {
    log_message($message, LOG_LEVEL_WARN, $escape, $backtrace);
91
92
}

93
94
95
96
97
98
99
100
101
102
/**
 * Logs a message at the environment level
 *
 * @param string $message   The message to display
 * @param bool   $escape    Whether to HTML escape the message
 * @param bool   $backtrace Whether to provide a backtrace if the system is
 *                          configured to give backtraces at this level.
 */
function log_environ ($message, $escape=true, $backtrace=true) {
    log_message($message, LOG_LEVEL_ENVIRON, $escape, $backtrace);
103
104
}

105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
/**
 * Logs a message at the given log level. This function should not be called by
 * any code outside of this module.
 *
 * @param string $message   The message to display
 * @param int    $loglevel  The level to log the message at
 * @param bool   $escape    Whether to HTML escape the message
 * @param bool   $backtrace Whether to provide a backtrace if the system is
 *                          configured to give backtraces at this level.
 * @param string $file      The file the error occured in
 * @param int    $line      The line number the error occured on
 * @param array  $trace     The backtrace for the error
 * @access private
 */
function log_message ($message, $loglevel, $escape, $backtrace, $file=null, $line=null, $trace=null) {
120
    global $SESSION;
121
    if (!$SESSION && function_exists('get_config')) {
122
123
124
        require_once(get_config('docroot') . 'auth/lib.php');
        $SESSION = Session::singleton();
    }
125
126
127
128
129
130

    static $requestprefix = '';
    if (!$requestprefix) {
        $requestprefix = substr(md5(microtime()), 0, 2) . ' ';
    }

131
132
133
134
135
136
137
138
139
140
141
142
    static $loglevelnames = array(
        LOG_LEVEL_ENVIRON => 'environ',
        LOG_LEVEL_DBG     => 'dbg',
        LOG_LEVEL_INFO    => 'info',
        LOG_LEVEL_WARN    => 'warn'
    );

    if (!function_exists('get_config') || null === ($targets = get_config('log_' . $loglevelnames[$loglevel] . '_targets'))) {
        $targets = LOG_TARGET_SCREEN | LOG_TARGET_ERRORLOG;
    }

    // Get nice backtrace information if required
143
    $trace = ($trace) ? $trace : debug_backtrace();
144
145
146
147
148
149
    // If the last caller was the 'error' function then it came from a PHP warning
    if (!is_null($file)) {
        $filename = $file;
        $linenum  = $line;
    }
    else {
150
151
        $filename  = $trace[1]['file'];
        $linenum   = $trace[1]['line'];
152
    }
153

154
    if (!function_exists('get_config') || get_config('log_backtrace_levels') & $loglevel) {
155
        list($textbacktrace, $htmlbacktrace) = log_build_backtrace($trace);
156
157
158
159
160
    }
    else {
        $textbacktrace = $htmlbacktrace = '';
    }

161
162
163
    if (is_bool($message)) {
        $loglines = array(($message ? 'bool(true)' : 'bool(false)'));
    }
164
165
166
    else if (is_null($message)) {
        $loglines = array('NULL');
    }
167
168
169
    else {
        $loglines = explode("\n", print_r($message, true));
    }
170
171

    // Make a prefix for each line, if we are logging a normal debug/info/warn message
172
    $prefix = $requestprefix;
173
    if ($loglevel != LOG_LEVEL_ENVIRON && function_exists('get_config')) {
174
175
176
177
        $docroot = get_config('docroot');
        $prefixfilename = (substr($filename, 0, strlen($docroot)) == $docroot)
            ? substr($filename, strlen($docroot))
            : $filename;
178
        $prefix .= '(' . $prefixfilename . ':' . $linenum . ') ';
179
    }
180
    $prefix = '[' . str_pad(substr(strtoupper($loglevelnames[$loglevel]), 0, 3), 3) . '] ' . $prefix;
181
182

    if ($targets & LOG_TARGET_SCREEN) {
183
184
185
186
187
        // Work out which method to call for displaying the message
        if ($loglevel == LOG_LEVEL_DBG || $loglevel == LOG_LEVEL_INFO) {
            $method = 'add_info_msg';
        }
        else {
188
            $method = 'add_error_msg';
189
190
        }

191
192
193
194
195
196
197
198
199
        $message = implode("\n", $loglines);
        if ($escape) {
            $message = htmlspecialchars($message, ENT_COMPAT, 'UTF-8');
            $message = str_replace('  ', '&nbsp; ', $message);
        }
        $message = nl2br($message);
        $message = '<div style="font-family: monospace;">' . $prefix . $message . "</div>\n";
        if (is_a($SESSION, 'Session')) {
            $SESSION->$method($message, false);
200
        }
201
202
203
204
205
206
        else if (!function_exists('get_config') || get_config('installed')) {
            // Don't output when we are not installed, since this will cause the
            // redirect to the install page to fail.
            echo $message;
        }

207
        if ($backtrace && $htmlbacktrace) {
208
209
210
            if (is_a($SESSION, 'Session')) {
                $SESSION->add_info_msg($htmlbacktrace, false);
            }
211
            else if (!function_exists('get_config') || get_config('installed')) {
212
213
                echo $htmlbacktrace;
            }
214
215
216
217
218
219
220
        }
    }

    if ($targets & LOG_TARGET_ERRORLOG) {
        foreach ($loglines as $line) {
            error_log($prefix . $line);
        }
221
        if ($backtrace && $textbacktrace) {
222
223
224
225
226
227
228
229
            $lines = explode("\n", $textbacktrace);
            foreach ($lines as $line) {
                error_log($line);
            }
        }
    }
}

230
231
232
233
234
235
236
237
238
239
/**
 * Given an array that contains a backtrace, builds two versions of it - one in
 * HTML form and one in text form - for logging.
 *
 * @param array $backtrace The backtrace to build
 * @return array           An array containing the backtraces, index 0
 *                         containing the text version and index 1 containing
 *                         the HTML version.
 * @access private
 */
240
241
242
function log_build_backtrace($backtrace) {
    $calls = array();

243
    // Remove the call to log_message
244
    //array_shift($backtrace);
245

246
247
248
249
250
251
252
253
    foreach ($backtrace as $bt) {
        $bt['file']  = (isset($bt['file'])) ? $bt['file'] : 'Unknown';
        $bt['line']  = (isset($bt['line'])) ? $bt['line'] : 0;
        $bt['class'] = (isset($bt['class'])) ? $bt['class'] : '';
        $bt['type']  = (isset($bt['type'])) ? $bt['type'] : '';
        $bt['args']  = (isset($bt['args'])) ? $bt['args'] : '';

        $args = '';
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
        if ($bt['args']) {
            foreach ($bt['args'] as $arg) {
                if (!empty($args)) {
                    $args .= ', ';
                }
                switch (gettype($arg)) {
                    case 'integer':
                    case 'double':
                        $args .= $arg;
                        break;
                    case 'string':
                        $arg = substr($arg, 0, 50) . ((strlen($arg) > 50) ? '...' : '');
                        $args .= '"' . $arg . '"';
                        break;
                    case 'array':
                        $args .= 'array(size ' . count($arg) . ')';
                        break;
                    case 'object':
                        $args .= 'object(' . get_class($arg) . ')';
                        break;
                    case 'resource':
                        $args .= 'resource(' . strstr($arg, '#') . ')';
                        break;
                    case 'boolean':
                        $args .= $arg ? 'true' : 'false';
                        break;
                    case 'NULL':
                        $args .= 'null';
                        break;
                    default:
                        $args .= 'unknown';
                }
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
            }
        }

        $calls[] = array(
            'file'  => $bt['file'],
            'line'  => $bt['line'],
            'class' => $bt['class'],
            'type'  => $bt['type'],
            'func'  => $bt['function'],
            'args'  => $args
        );
    }

    $textmessage = "Call stack (most recent first):\n";
    $htmlmessage = "<pre><strong>Call stack (most recent first):</strong>\n<ul>";

    foreach ($calls as $call) {
        $textmessage .= "  * {$call['class']}{$call['type']}{$call['func']}({$call['args']}) at {$call['file']}:{$call['line']}\n";
        $htmlmessage .= '<li><span style="color:#933;">' . htmlspecialchars($call['class'], ENT_COMPAT, 'UTF-8') . '</span><span style="color:#060;">'
305
            . htmlspecialchars($call['type'], ENT_COMPAT, 'UTF-8') . '</span><span style="color:#339;">' . htmlspecialchars($call['func'], ENT_COMPAT, 'UTF-8')
306
307
308
309
310
311
            . '</span><span style="color:#060;">(</span><span style="color:#f00;">' . htmlspecialchars($call['args'], ENT_COMPAT, 'UTF-8') . '</span><span style="color:#060;">)</span> at <strong>' . htmlspecialchars($call['file'], ENT_COMPAT, 'UTF-8') . ':' . $call['line'] . '</strong></li>';
    }
    $htmlmessage .= "</pre>\n";

    return array($textmessage, $htmlmessage);
}
312
313
314
315
316

/**
 * Ends the script with an informational message
 *
 * @param string $message The message to display
317
 * @todo this function should go away
318
319
320
321
322
323
324
325
326
327
 */
function die_info($message) {
    $smarty = smarty();
    $smarty->assign('message', $message);
    $smarty->assign('type', 'info');
    $smarty->display('message.tpl');
    exit;
}


328
329
// Error/Exception handling

330
331
332
333
334
335
336
337
338
/**
 * Called when any error occurs, due to a PHP error or through a call to
 * {@link trigger_error}.
 *
 * @param int    $code    The code of the error message
 * @param string $message The message reported
 * @param string $file    The file the error was detected in
 * @param string $line    The line number the error was detected on
 * @param array  $vars    The contents of $GLOBALS at the time the error was detected
339
 * @access private
340
 */
341
342
343
function error ($code, $message, $file, $line, $vars) {
    static $error_lookup = array(
        E_NOTICE => 'Notice',
344
345
346
347
348
349
350
        E_WARNING => 'Warning',
        // Not sure if these ever get handled here
        E_ERROR => 'Error',
        // These three are not used by this application but may be used by third parties
        E_USER_NOTICE => 'User Notice',
        E_USER_WARNING => 'User Warning',
        E_USER_ERROR => 'User Error'
351
352
353
354
355
356
357
358
359
360
    );

    if (!error_reporting()) {
        return;
    }

    if (!isset($error_lookup[$code])) {
        return;
    }

361
    // Ignore errors from smarty templates, which happen all too often
362
    if (function_exists('get_config')) {
363
        $compiledir = realpath(get_config('dataroot') . 'smarty/compile');
364
365
366
367

        if (E_NOTICE == $code && substr($file, 0, strlen($compiledir)) == $compiledir) {
            return;
        }
368
369
    }

370
371
372
373
    // Fix up the message, which is in HTML form
    $message = strip_tags($message);
    $message = htmlspecialchars_decode($message);

374
    log_message($message, LOG_LEVEL_WARN, true, true, $file, $line);
375
376
}

377
/**
378
379
380
381
382
 * Catches all otherwise uncaught exceptions. Will be deliberately used in some
 * situations. After this is called the script will end, so make sure to catch
 * any exceptions that you can deal with.
 *
 * @param Exception $e The exception that was thrown.
383
 * @access private
384
 */
385
function exception (Exception $e) {
386
387
    global $USER;
    if ($USER) {
Martyn Smith's avatar
Martyn Smith committed
388
        if (!($e instanceof MaharaException) || get_class($e) == 'MaharaException') {
389
            log_warn("An exception was thrown of class " . get_class($e) . ". \nTHIS IS BAD "
390
391
                     . "and should be changed to something extending MaharaException,\n" 
                     . "unless the exception is from a third party library.\n"
392
393
394
395
396
                     . "Original trace follows", true, false);
            log_message($e->getMessage(), LOG_LEVEL_WARN, true, true, $e->getFile(), $e->getLine(), $e->getTrace());
            $e = new SystemException($e->getMessage());
            $e->set_log_off();
        }
397
    }
398
399

    // Display the message and die
400
    $e->handle_exception();
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
}


interface MaharaThrowable {
    
    public function render_exception();
    
}

// Standard exceptions  - top level exception class. 
// all exceptions should extend one of these three.

/**
 * Very top of the tree for exceptions in Mahara.
 * Nothing should extend this directly. 
 * Contains a few helper functions for all exceptions.
 */
class MaharaException extends Exception {

    protected $log = true;

    public function get_string() {
        $args = func_get_args();
424
        if (function_exists('get_string')) {
425
426
427
428
429
            $args[0] = strtolower(get_class($this)) . $args[0];
            $args[1] = 'error';
            $str = call_user_func_array('get_string', $args);
            if (strpos($str, '[[') !== 0) {
                return $str;
430
431
            }
        }
432
433
434
435
436

        $tag = func_get_arg(0);
        $strings = $this->strings();
        if (array_key_exists($tag, $strings)) {
            return $strings[$tag];
437
        }
438
439
440
441
442
443
444
        
        return 'An error occured';
    }

    public function get_sitename() {
        if (!function_exists('get_config') || !$sitename = @get_config('sitename')) {
            $sitename = 'Mahara';
445
        }
446
447
448
449
450
451
        return $sitename;
    }

    public function strings() {
        return array('title' => $this->get_sitename() . ': Site unavailable');
    }
452

453
454
455
456
457
458
459
460
461
462
463
464
    public function set_log() {
        $this->log = true;
    }

    public function set_log_off() {
        $this->log = false;
    }

    public function render_exception() {
        return $this->getMessage();
    }

465
    public final function handle_exception() {
466

467
        if (!empty($this->log)) {
468
            log_message($this->getMessage(), LOG_LEVEL_WARN, true, true, $this->getFile(), $this->getLine(), $this->getTrace());
469
470
471
472
473
474
475
476
        }

        if (defined('JSON')) { // behave differently
            @header('Content-type: text/plain');
            @header('Pragma: no-cache');
            echo json_encode(array('error' => true, 'message' => $this->render_exception()));
            exit;
        }
477
478
479
480
481
        
        if (defined('CRON')) {
            echo $this->render_exception();
            exit;
        }
482

Donal McMullan's avatar
Donal McMullan committed
483
484
485
486
487
        if (defined('XMLRPC')) { // it's preferable to throw an XmlrpcServerException
            echo xmlrpc_error($this->render_exception(), $this->getCode());
            exit;
        }

488
489
        $outputtitle = $this->get_string('title');
        $outputmessage = $this->render_exception();
490

491
        if (function_exists('smarty') && !$this instanceof ConfigSanityException) {
492
493
494
495
496
            $smarty = smarty();
            $smarty->assign('title', $outputtitle);
            $smarty->assign('message', $outputmessage);
            $smarty->display('error.tpl');
        }
497
        else {
498
499
            $outputtitle   = htmlspecialchars($outputtitle, ENT_COMPAT, 'UTF-8');
            $outputmessage = nl2br(htmlspecialchars($outputmessage, ENT_COMPAT, 'UTF-8'));
500
            echo <<<EOF
501
502
<html>
<head>
503
    <title>$outputtitle</title>
504
    <style type="text/css">
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
        html {
            margin: 0;
            padding: 0;
            font-family: Arial, sans-serif;
        }
        body {
            width: 600px;
            margin: 100px auto;
            font-size: 12px;
        }
        h1 {
            color: #547c22;
            font-size: 20px;
            font-weight: normal;	
            margin: 0 0 5px 0;
            padding: 0;
            text-transform: capitalize;
            border-bottom: 1px solid #819f18;
            text-align: center;
        }
        #message {
            width: 90%;
            margin: 0 auto;
            text-align: justify;
        }
530
531
532
533
534
535
        #reason {
            margin: 0 3em;
        }
    </style>
</head>
<body>
536
537
EOF;
    echo <<<EOF
538
<h1>$outputtitle</h1>
539
<div id="message">$outputmessage</div>
540
541
542
</body>
</html>
EOF;
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
        }
        // end of printing stuff to the screen...
        die();
    }
}






/**
 * SystemException - this is basically a bug in the system.
 */
class SystemException extends MaharaException implements MaharaThrowable {

    public function render_exception () {
        return $this->get_string('message');
    }

    public function strings() {
        return array_merge(parent::strings(), 
                           array('message' => 'A nonrecoverable error occured. '
                                 . 'This probably means you have encountered a bug in the system'));
    }
    
569
570
}

571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
/**
 * ConfigException - something is misconfigured that's causing a problem.
 * Generally these will be the fault of admins
 */
class ConfigException extends MaharaException  implements MaharaThrowable {
     
    public function render_exception () {
        return $this->getMessage();
    }

    public function strings() {
        return array_merge(parent::strings(), 
                           array('message' => $this->get_sitename() 
                           . ' is misconfigured and this is causing problems. '
                           . 'You probably need to contact an administrator to get this fixed.  '
Penny Leach's avatar
Penny Leach committed
586
                           . ' Details, if any, follow:'));
587
588
589
590
591
592
    }
}

/**
 * UserException - the user has done something they shouldn't (or tried to)
 */
Martyn Smith's avatar
Martyn Smith committed
593
class UserException extends MaharaException implements MaharaThrowable {
594
595

    protected $log = false;
596

597
    public function render_exception() {
598
        return $this->get_string('message') . "\n\n" . $this->getMessage();
599
600
601
602
603
604
    }

    public function strings() {
        return array_merge(parent::strings(),  
                           array('message' => 'Something in the way you\'re interacting with ' 
                                 . $this->get_sitename()
605
                                 . " is causing an error.\nDetails if any, follow:"));
606
607
608
    }
}

609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
/**
 * Exception - Not found. Throw this if a user is trying to view something
 * that doesn't exist
 */
class NotFoundException extends UserException {
    public function strings() {
        return array_merge(parent::strings(), 
                           array('message' => 'The page you are looking for could not be found',
                                 'title' => 'Not Found'));
    }

    public function render_exception() {
        header('HTTP/1.0 404 Not Found', true);
        return parent::render_exception();
    }
}

626
627
628

/** 
 * The configuration that Mahara is trying to be run in is insane
629
 */
630
631
632
633
634
class ConfigSanityException extends ConfigException {
    public function strings() {
        return array_merge(parent::strings(), array('message' => ''));
    }
}
635
636

/**
637
 * An SQL related error occured
638
 */
639
class SQLException extends SystemException {
640
    public function __construct($message=null, $code=0) {
641
642
        global $DB_IGNORE_SQL_EXCEPTIONS;

643
        if ($GLOBALS['_TRANSACTION_LEVEL'] > 0) {
644
645
            db_rollback();
        }
646
        parent::__construct($message, $code);
647
648
649
650

        if (empty($DB_IGNORE_SQL_EXCEPTIONS)) {
            log_warn($this->getMessage());
        }
651
652
    }
}
653

654
655
656
/**
 * An exception generated by invalid GET or POST parameters
 */
657
class ParameterException extends UserException {
658
    public function strings() {
659
660
661
662
        return array_merge(parent::strings(), array(
            'title'   => 'Mahara: Invalid Parameter',
            'message' => 'A required parameter is missing or malformed')
        );
663
664
    }
}
665

666
667
668
669
670
671
672
673
674
675
676
/**
 * A function or method has been passed the wrong kind of argument
 * Unfortunately, broken type-hints cause fatal errors - not exceptions
 */
class ParamOutOfRangeException extends SystemException {}

/**
 * Remote Server exception - something has gone wrong at the remote machine
 */
class RemoteServerException extends SystemException {}

Donal McMullan's avatar
Donal McMullan committed
677
678
679
/**
 * Xmlrpc Server exception - must output well formed XMLRPC error to the client
 */
Donal McMullan's avatar
Donal McMullan committed
680
681
682
class XmlrpcServerException extends SystemException {}

/**
683
 * Xmlrpc Client exception - Something has gone wrong in the networking
Donal McMullan's avatar
Donal McMullan committed
684
685
686
687
688
689
690
 */
class XmlrpcClientException extends SystemException {}

/**
 * Error with SSL and encryption
 */
class CryptException extends SystemException {}
Donal McMullan's avatar
Donal McMullan committed
691

692
693
694
/**
 * An exception generated when e-mail can't be sent
 */
695
class EmailException extends SystemException {}
696

697
698
699
/** 
 * Exception - artefact not found 
 */
700
class ArtefactNotFoundException extends NotFoundException {}
701

702
703
704
705
706
/**
 * Exception - block instance not found
 */
class BlockInstanceNotFoundException extends NotFoundException {}

707
708
709
/**
 * Exception - view not found
 */
710
class ViewNotFoundException extends NotFoundException {}
711

Penny Leach's avatar
Penny Leach committed
712
713
714
/**
 * Exception - user not found
 */
715
class UserNotFoundException extends NotFoundException {}
716

717
/**
718
 * Exception - group not found
719
 */
720
class GroupNotFoundException extends NotFoundException {}
721

Martyn Smith's avatar
Martyn Smith committed
722
723
724
725
726
/**
 * Exception - fired when something happens that would make the user exceed their quota
 */
class QuotaExceededException extends UserException {}

Penny Leach's avatar
Penny Leach committed
727
728
729
/**
 * Exception - Access denied. Throw this if a user is trying to view something they can't
 */
730
731
732
733
734
735
736
737
738
739
740
741
742
743
class AccessDeniedException extends UserException {
    public function strings() {
        return array_merge(parent::strings(), 
                           array('message' => 'You do not have access to view this page',
                                 'title' => 'Access denied'));
    }

    public function render_exception() {
        header("HTTP/1.0 403 Forbidden", true);
        return parent::render_exception();
    }
}


744
?>