]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/ErrorManager.php
added destroyPostponedErrors: Get rid of all pending error messages in case of all
[SourceForge/phpwiki.git] / lib / ErrorManager.php
1 <?php rcs_id('$Id: ErrorManager.php,v 1.53 2008-03-17 19:04:05 rurban Exp $');
2
3 if (isset($GLOBALS['ErrorManager'])) return;
4
5 // php5: ignore E_STRICT (var warnings)
6 /*
7 if (defined('E_STRICT') 
8     and (E_ALL & E_STRICT)
9     and (error_reporting() & E_STRICT)) {
10     echo " errormgr: error_reporting=", error_reporting();
11     echo "\nplease fix that in your php.ini!";
12     error_reporting(E_ALL & ~E_STRICT);
13 }
14 */
15 define ('EM_FATAL_ERRORS', E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | ~2048);
16 define ('EM_WARNING_ERRORS',
17         E_WARNING | E_CORE_WARNING | E_COMPILE_WARNING | E_USER_WARNING);
18 define ('EM_NOTICE_ERRORS', E_NOTICE | E_USER_NOTICE);
19
20 /* It is recommended to leave assertions on. 
21    You can simply comment the two lines below to leave them on.
22    Only where absolute speed is necessary you might want to turn 
23    them off.
24 */
25 //also turn it on if phpwiki_version notes no release
26 if (defined('DEBUG') and DEBUG)
27     assert_options (ASSERT_ACTIVE, 1);
28 else
29     assert_options (ASSERT_ACTIVE, 0);
30 assert_options (ASSERT_CALLBACK, 'wiki_assert_handler');
31
32 function wiki_assert_handler ($file, $line, $code) {
33     ErrorManager_errorHandler( $code, sprintf("<br />%s:%s: %s: Assertion failed <br />", $file, $line, $code), $file, $line);
34 }
35
36 /**
37  * A class which allows custom handling of PHP errors.
38  *
39  * This is a singleton class. There should only be one instance
40  * of it --- you can access the one instance via $GLOBALS['ErrorManager'].
41  *
42  * FIXME: more docs.
43  */ 
44 class ErrorManager 
45 {
46     /**
47      * Constructor.
48      *
49      * As this is a singleton class, you should never call this.
50      * @access private
51      */
52     function ErrorManager() {
53         $this->_handlers = array();
54         $this->_fatal_handler = false;
55         $this->_postpone_mask = 0;
56         $this->_postponed_errors = array();
57
58         set_error_handler('ErrorManager_errorHandler');
59     }
60
61     /**
62      * Get mask indicating which errors are currently being postponed.
63      * @access public
64      * @return int The current postponed error mask.
65      */
66     function getPostponedErrorMask() {
67         return $this->_postpone_mask;
68     }
69
70     /**
71      * Set mask indicating which errors to postpone.
72      *
73      * The default value of the postpone mask is zero (no errors postponed.)
74      *
75      * When you set this mask, any queue errors which do not match the new
76      * mask are reported.
77      *
78      * @access public
79      * @param $newmask int The new value for the mask.
80      */
81     function setPostponedErrorMask($newmask) {
82         $this->_postpone_mask = $newmask;
83         if (function_exists('PrintXML'))
84             PrintXML($this->_flush_errors($newmask));
85         else
86             echo($this->_flush_errors($newmask));
87
88     }
89
90     /**
91      * Report any queued error messages.
92      * @access public
93      */
94     function flushPostponedErrors() {
95         if (function_exists('PrintXML'))
96             PrintXML($this->_flush_errors());
97         else
98             echo $this->_flush_errors();
99     }
100     
101     /**
102      * Get rid of all pending error messages in case of all non-html
103      * - pdf or image - output.
104      * @access public
105      */
106     function destroyPostponedErrors () {
107         $this->_postponed_errors = array();
108     }
109
110     /**
111      * Get postponed errors, formatted as HTML.
112      *
113      * This also flushes the postponed error queue.
114      *
115      * @return object HTML describing any queued errors (or false, if none). 
116      */
117     function getPostponedErrorsAsHTML() {
118         $flushed = $this->_flush_errors();
119         if (!$flushed)
120             return false;
121         if ($flushed->isEmpty())
122             return false;
123         // format it with the worst class (error, warning, notice)
124         $worst_err = $flushed->_content[0];
125         foreach ($flushed->_content as $err) {
126             if ($err and isa($err, 'PhpError') and $err->errno > $worst_err->errno) {
127                 $worst_err = $err;
128             }
129         }
130         if ($worst_err->isNotice())
131             return $flushed;
132         $class = $worst_err->getHtmlClass(); 
133         $html = HTML::div(array('style' => 'border: none', 'class' => $class),
134                           HTML::h4(array('class' => 'errors'), 
135                                    "PHP " . $worst_err->getDescription()));
136         $html->pushContent($flushed);
137         return $html;
138     }
139     
140     /**
141      * Push a custom error handler on the handler stack.
142      *
143      * Sometimes one is performing an operation where one expects
144      * certain errors or warnings. In this case, one might not want
145      * these errors reported in the normal manner. Installing a custom
146      * error handler via this method allows one to intercept such
147      * errors.
148      *
149      * An error handler installed via this method should be either a
150      * function or an object method taking one argument: a PhpError
151      * object.
152      *
153      * The error handler should return either:
154      * <dl>
155      * <dt> False <dd> If it has not handled the error. In this case,
156      *                 error processing will proceed as if the handler
157      *                 had never been called: the error will be passed
158      *                 to the next handler in the stack, or the
159      *                 default handler, if there are no more handlers
160      *                 in the stack.
161      *
162      * <dt> True <dd> If the handler has handled the error. If the
163      *                error was a non-fatal one, no further processing
164      *                will be done. If it was a fatal error, the
165      *                ErrorManager will still terminate the PHP
166      *                process (see setFatalHandler.)
167      *
168      * <dt> A PhpError object <dd> The error is not considered
169      *                             handled, and will be passed on to
170      *                             the next handler(s) in the stack
171      *                             (or the default handler). The
172      *                             returned PhpError need not be the
173      *                             same as the one passed to the
174      *                             handler. This allows the handler to
175      *                             "adjust" the error message.
176      * </dl>
177      * @access public
178      * @param $handler WikiCallback  Handler to call.
179      */
180     function pushErrorHandler($handler) {
181         array_unshift($this->_handlers, $handler);
182     }
183
184     /**
185      * Pop an error handler off the handler stack.
186      * @access public
187      */
188     function popErrorHandler() {
189         return array_shift($this->_handlers);
190     }
191
192     /**
193      * Set a termination handler.
194      *
195      * This handler will be called upon fatal errors. The handler
196      * gets passed one argument: a PhpError object describing the
197      * fatal error.
198      *
199      * @access public
200      * @param $handler WikiCallback  Callback to call on fatal errors.
201      */
202     function setFatalHandler($handler) {
203         $this->_fatal_handler = $handler;
204     }
205
206     /**
207      * Handle an error.
208      *
209      * The error is passed through any registered error handlers, and
210      * then either reported or postponed.
211      *
212      * @access public
213      * @param $error object A PhpError object.
214      */
215     function handleError($error) {
216         static $in_handler;
217
218         if (!empty($in_handler)) {
219             $msg = $error->_getDetail();
220             $msg->unshiftContent(HTML::h2(fmt("%s: error while handling error:",
221                                               "ErrorManager")));
222             $msg->printXML();
223             return;
224         }
225
226         // template which flushed the pending errors already handled,
227         // so display now all errors directly.
228         if (!empty($GLOBALS['request']->_finishing)) {
229             $this->_postpone_mask = 0;
230         }
231         
232         $in_handler = true;
233
234         foreach ($this->_handlers as $handler) {
235             if (!$handler) continue;
236             $result = $handler->call($error);
237             if (!$result) {
238                 continue;       // Handler did not handle error.
239             }
240             elseif (is_object($result)) {
241                 // handler filtered the result. Still should pass to
242                 // the rest of the chain.
243                 if ($error->isFatal()) {
244                     // Don't let handlers make fatal errors non-fatal.
245                     $result->errno = $error->errno;
246                 }
247                 $error = $result;
248             }
249             else {
250                 // Handler handled error.
251                 if (!$error->isFatal()) {
252                     $in_handler = false;
253                     return;
254                 }
255                 break;
256             }
257         }
258
259         // Error was either fatal, or was not handled by a handler.
260         // Handle it ourself.
261         if ($error->isFatal()) {
262             $this->_noCacheHeaders();
263             echo "<html><body><div style=\"font-weight:bold; color:red\">Fatal Error:</div>\n";
264             if (defined('DEBUG') and (DEBUG & _DEBUG_TRACE)) {
265                 echo "error_reporting=",error_reporting(),"\n<br>";
266                 if (function_exists("debug_backtrace")) // >= 4.3.0
267                     $error->printSimpleTrace(debug_backtrace());
268             }
269             $this->_die($error);
270         }
271         else if (($error->errno & error_reporting()) != 0) {
272             if  (($error->errno & $this->_postpone_mask) != 0) {
273                 if ((function_exists('isa') and isa($error, 'PhpErrorOnce'))
274                     or (!function_exists('isa') and 
275                     (
276                      // stdlib independent isa()
277                      (strtolower(get_class($error)) == 'phperroronce')
278                      or (is_subclass_of($error, 'PhpErrorOnce'))))) {
279                     $error->removeDoublettes($this->_postponed_errors);
280                     if ( $error->_count < 2 )
281                         $this->_postponed_errors[] = $error;
282                 } else {
283                     $this->_postponed_errors[] = $error;
284                 }
285             }
286             else {
287                 //echo "postponed errors: ";
288                 $this->_noCacheHeaders();
289                 if (defined('DEBUG') and (DEBUG & _DEBUG_TRACE)) {
290                     echo "error_reporting=",error_reporting(),"\n";
291                     if (function_exists("debug_backtrace")) // >= 4.3.0
292                         $error->printSimpleTrace(debug_backtrace());
293                 }
294                 $error->printXML();
295             }
296         }
297         $in_handler = false;
298     }
299
300     function warning($msg, $errno = E_USER_NOTICE) {
301         $this->handleError(new PhpWikiError($errno, $msg));
302     }
303     
304     /**
305      * @access private
306      */
307     function _die($error) {
308         global $WikiTheme;
309         //echo "\n\n<html><body>";
310         $error->printXML();
311         PrintXML($this->_flush_errors());
312         if ($this->_fatal_handler)
313             $this->_fatal_handler->call($error);
314         if (!$WikiTheme->DUMP_MODE)
315             exit -1;
316     }
317
318     /**
319      * @access private
320      */
321     function _flush_errors($keep_mask = 0) {
322         $errors = &$this->_postponed_errors;
323         if (empty($errors)) return '';
324         $flushed = HTML();
325         for ($i=0; $i<count($errors); $i++) {
326             $error =& $errors[$i];
327             if (!is_object($error)) {
328                 continue;
329             }
330             if (($error->errno & $keep_mask) != 0)
331                 continue;
332             unset($errors[$i]);
333             $flushed->pushContent($error);
334         }
335         return $flushed;
336     }
337
338     function _noCacheHeaders() {
339         global $request;
340         static $already = false;
341
342         if (isset($request) and isset($request->_validators)) {
343             $request->_validators->_tag = false;
344             $request->_validators->_mtime = false;
345         }
346         if ($already) return;
347         
348         // FIXME: Howto announce that to Request->cacheControl()?
349         if (!headers_sent()) {
350             header( "Cache-control: no-cache" );
351             header( "Pragma: nocache" );
352         }
353         $already = true;
354     }
355 }
356
357 /**
358  * Global error handler for class ErrorManager.
359  *
360  * This is necessary since PHP's set_error_handler() does not allow
361  * one to set an object method as a handler.
362  * 
363  * @access private
364  */
365 function ErrorManager_errorHandler($errno, $errstr, $errfile, $errline) 
366 {
367     if (!isset($GLOBALS['ErrorManager'])) {
368       $GLOBALS['ErrorManager'] = new ErrorManager;
369     }
370         
371     if (defined('DEBUG') and DEBUG)
372         $error = new PhpWikiError($errno, $errstr, $errfile, $errline);
373     else
374         $error = new PhpErrorOnce($errno, $errstr, $errfile, $errline);
375     $GLOBALS['ErrorManager']->handleError($error);
376 }
377
378
379 /**
380  * A class representing a PHP error report.
381  *
382  * @see The PHP documentation for set_error_handler at
383  *      http://php.net/manual/en/function.set-error-handler.php .
384  */
385 class PhpError {
386     /**
387      * The PHP errno
388      */
389     //var $errno;
390
391     /**
392      * The PHP error message.
393      */
394     //var $errstr;
395
396     /**
397      * The source file where the error occurred.
398      */
399     //var $errfile;
400
401     /**
402      * The line number (in $this->errfile) where the error occured.
403      */
404     //var $errline;
405
406     /**
407      * Construct a new PhpError.
408      * @param $errno   int
409      * @param $errstr  string
410      * @param $errfile string
411      * @param $errline int
412      */
413     function PhpError($errno, $errstr, $errfile, $errline) {
414         $this->errno   = $errno;
415         $this->errstr  = $errstr;
416         $this->errfile = $errfile;
417         $this->errline = $errline;
418     }
419
420     /**
421      * Determine whether this is a fatal error.
422      * @return boolean True if this is a fatal error.
423      */
424     function isFatal() {
425         return ($this->errno & (2048|EM_WARNING_ERRORS|EM_NOTICE_ERRORS)) == 0;
426     }
427
428     /**
429      * Determine whether this is a warning level error.
430      * @return boolean
431      */
432     function isWarning() {
433         return ($this->errno & EM_WARNING_ERRORS) != 0;
434     }
435
436     /**
437      * Determine whether this is a notice level error.
438      * @return boolean
439      */
440     function isNotice() {
441         return ($this->errno & EM_NOTICE_ERRORS) != 0;
442     }
443     function getHtmlClass() {
444         if ($this->isNotice()) {
445             return 'hint';
446         } elseif ($this->isWarning()) {
447             return 'warning';
448         } else {
449             return 'errors';
450         }
451     }
452     
453     function getDescription() {
454         if ($this->isNotice()) {
455             return 'Notice';
456         } elseif ($this->isWarning()) {
457             return 'Warning';
458         } else {
459             return 'Error';
460         }
461     }
462
463     /**
464      * Get a printable, HTML, message detailing this error.
465      * @return object The detailed error message.
466      */
467     function _getDetail() {
468         $dir = defined('PHPWIKI_DIR') ? PHPWIKI_DIR : substr(dirname(__FILE__),0,-4);
469         if (substr(PHP_OS,0,3) == 'WIN') {
470            $dir = str_replace('/','\\',$dir);
471            $this->errfile = str_replace('/','\\',$this->errfile);
472            $dir .= "\\";
473         } else 
474            $dir .= '/';
475         $errfile = preg_replace('|^' . preg_quote($dir) . '|', '', $this->errfile);
476         $lines = explode("\n", $this->errstr);
477         if (DEBUG & _DEBUG_VERBOSE) {
478           $msg = sprintf("%s:%d %s[%d]: %s",
479                          $errfile, $this->errline,
480                          $this->getDescription(), $this->errno,
481                          array_shift($lines));
482         }/* elseif (! $this->isFatal()) {
483           $msg = sprintf("%s:%d %s: \"%s\"",
484                          $errfile, $this->errline,
485                          $this->getDescription(),
486                          array_shift($lines));
487         }*/ else {
488           $msg = sprintf("%s:%d %s: \"%s\"",
489                          $errfile, $this->errline,
490                          $this->getDescription(),
491                          array_shift($lines));
492         }
493         
494         $html = HTML::div(array('class' => $this->getHtmlClass()), HTML::p($msg));
495         // The class is now used for the div container.
496         // $html = HTML::div(HTML::p($msg));
497         if ($lines) {
498             $list = HTML::ul();
499             foreach ($lines as $line)
500                 $list->pushContent(HTML::li($line));
501             $html->pushContent($list);
502         }
503         
504         return $html;
505     }
506
507     /**
508      * Print an HTMLified version of this error.
509      * @see asXML()
510      */
511     function printXML() {
512         PrintXML($this->_getDetail());
513     }
514
515     /**
516      * Return an HTMLified version of this error.
517      */
518     function asXML() {
519         return AsXML($this->_getDetail());
520     }
521
522     /**
523      * Return a plain-text version of this error.
524      */
525     function asString() {
526         return AsString($this->_getDetail());
527     }
528
529     function printSimpleTrace($bt) {
530         global $HTTP_SERVER_VARS;
531         $nl = isset($HTTP_SERVER_VARS['REQUEST_METHOD']) ? "<br />" : "\n";
532         echo $nl."Traceback:".$nl;
533         foreach ($bt as $i => $elem) {
534             if (!array_key_exists('file', $elem)) {
535                 continue;
536             }
537             print "  " . $elem['file'] . ':' . $elem['line'] . $nl;
538         }
539         flush();
540     }
541 }
542
543 /**
544  * A class representing a PhpWiki warning.
545  *
546  * This is essentially the same as a PhpError, except that the
547  * error message is quieter: no source line, etc...
548  */
549 class PhpWikiError extends PhpError {
550     /**
551      * Construct a new PhpError.
552      * @param $errno   int
553      * @param $errstr  string
554      */
555     function PhpWikiError($errno, $errstr) {
556         $this->PhpError($errno, $errstr, '?', '?');
557     }
558
559     function _getDetail() {
560         return HTML::div(array('class' => $this->getHtmlClass()), 
561                          HTML::p($this->getDescription() . ": $this->errstr"));
562     }
563 }
564
565 /**
566  * A class representing a Php warning, printed only the first time.
567  *
568  * Similar to PhpError, except only the first same error message is printed, 
569  * with number of occurences.
570  */
571 class PhpErrorOnce extends PhpError {
572
573     function PhpErrorOnce($errno, $errstr, $errfile, $errline) {
574         $this->_count = 1;
575         $this->PhpError($errno, $errstr, $errfile, $errline);
576     }
577
578     function _sameError($error) {
579         if (!$error) return false;
580         return ($this->errno == $error->errno and
581                 $this->errfile == $error->errfile and
582                 $this->errline == $error->errline);
583     }
584
585     // count similar handlers, increase _count and remove the rest
586     function removeDoublettes(&$errors) {
587         for ($i=0; $i < count($errors); $i++) {
588             if (!isset($errors[$i])) continue;
589             if ($this->_sameError($errors[$i])) {
590                 $errors[$i]->_count++;
591                 $this->_count++;
592                 if ($i) unset($errors[$i]);
593             }
594         }
595         return $this->_count;
596     }
597     
598     function _getDetail($count=0) {
599         if (!$count) $count = $this->_count;
600         $dir = defined('PHPWIKI_DIR') ? PHPWIKI_DIR : substr(dirname(__FILE__),0,-4);
601         if (substr(PHP_OS,0,3) == 'WIN') {
602            $dir = str_replace('/','\\',$dir);
603            $this->errfile = str_replace('/','\\',$this->errfile);
604            $dir .= "\\";
605         } else 
606            $dir .= '/';
607         $errfile = preg_replace('|^' . preg_quote($dir) . '|', '', $this->errfile);
608         if (is_string($this->errstr))
609                 $lines = explode("\n", $this->errstr);
610         elseif (is_object($this->errstr))
611                 $lines = array($this->errstr->asXML());
612         $errtype = (DEBUG & _DEBUG_VERBOSE) ? sprintf("%s[%d]", $this->getDescription(), $this->errno)
613                                             : sprintf("%s", $this->getDescription());
614         if ((DEBUG & _DEBUG_VERBOSE) or $this->isFatal()) {
615             $msg = sprintf("%s:%d %s: %s %s",
616                        $errfile, $this->errline,
617                        $errtype,
618                        array_shift($lines),
619                        $count > 1 ? sprintf(" (...repeated %d times)",$count) : ""
620                        );
621         } else {
622           $msg = sprintf("%s: \"%s\" %s",
623                          $errtype,
624                          array_shift($lines),
625                          $count > 1 ? sprintf(" (...repeated %d times)",$count) : "");
626         }
627         $html = HTML::div(array('class' => $this->getHtmlClass()), 
628                           HTML::p($msg));
629         if ($lines) {
630             $list = HTML::ul();
631             foreach ($lines as $line)
632                 $list->pushContent(HTML::li($line));
633             $html->pushContent($list);
634         }
635         
636         return $html;
637     }
638 }
639
640 require_once(dirname(__FILE__).'/HtmlElement.php');
641
642 if (!isset($GLOBALS['ErrorManager'])) {
643     $GLOBALS['ErrorManager'] = new ErrorManager;
644 }
645
646 // $Log: not supported by cvs2svn $
647 // Revision 1.52  2007/09/19 17:59:26  rurban
648 // use duplicates to save memory with DEBUG
649 //
650 // Revision 1.51  2007/09/15 12:31:37  rurban
651 // dont fatal on multi-page dumps
652 //
653 // Revision 1.50  2007/01/09 12:35:28  rurban
654 // release ready: turn off assert
655 //
656 // Revision 1.49  2006/12/22 00:17:49  rurban
657 // improve and unify error messages
658 //
659 // Revision 1.48  2006/03/19 14:29:40  rurban
660 // sf.net patch #1438439 by Matt Brown: Only set no-cache headers when error output is generated
661 //
662 // Revision 1.47  2005/10/31 17:20:40  rurban
663 // fix ConvertBefore
664 //
665 // Revision 1.46  2005/10/30 16:38:13  rurban
666 // minor fixes
667 //
668 // Revision 1.45  2005/10/29 14:28:08  uckelman
669 // existence of isa should be checked, not built-in is_a()
670 //
671 // Revision 1.44  2005/08/07 10:52:43  rurban
672 // stricter error handling: dba errors are fatal, display errors on Request->finish or session_close
673 //
674 // Revision 1.43  2005/04/11 19:41:23  rurban
675 // Improve postponed errors+warnins list layout.
676 //
677 // Revision 1.42  2005/02/26 18:29:07  rurban
678 // re-enable colored boxed errors
679 //
680 // Revision 1.41  2004/12/26 17:08:36  rurban
681 // php5 fixes: case-sensitivity, no & new
682 //
683 // Revision 1.40  2004/12/13 14:39:46  rurban
684 // aesthetics
685 //
686 // Revision 1.39  2004/11/05 18:04:20  rurban
687 // print errno only if _DEBUG_VERBOSE
688 //
689 // Revision 1.38  2004/10/19 17:34:55  rurban
690 // <4.3 fix
691 //
692 // Revision 1.37  2004/10/14 19:23:58  rurban
693 // remove debugging prints
694 //
695 // Revision 1.36  2004/10/12 15:35:43  rurban
696 // avoid Php Notice header
697 //
698 // Revision 1.35  2004/10/12 13:13:19  rurban
699 // php5 compatibility (5.0.1 ok)
700 //
701 // Revision 1.34  2004/09/24 18:52:19  rurban
702 // in deferred html error messages use the worst header and class
703 // (notice => warning => errors)
704 //
705 // Revision 1.33  2004/09/14 10:28:21  rurban
706 // use assert, maybe we should only turn it off for releases
707 //
708 // Revision 1.32  2004/07/08 13:50:32  rurban
709 // various unit test fixes: print error backtrace on _DEBUG_TRACE; allusers fix; new PHPWIKI_NOMAIN constant for omitting the mainloop
710 //
711 // Revision 1.31  2004/07/02 09:55:58  rurban
712 // more stability fixes: new DISABLE_GETIMAGESIZE if your php crashes when loading LinkIcons: failing getimagesize in old phps; blockparser stabilized
713 //
714 // Revision 1.30  2004/06/25 14:29:12  rurban
715 // WikiGroup refactoring:
716 //   global group attached to user, code for not_current user.
717 //   improved helpers for special groups (avoid double invocations)
718 // new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
719 // fixed a XHTML validation error on userprefs.tmpl
720 //
721 // Revision 1.29  2004/06/20 15:30:04  rurban
722 // get_class case-sensitivity issues
723 //
724 // Revision 1.28  2004/06/16 11:51:04  rurban
725 // fixed typo: undefined object #235
726 //
727 // Revision 1.27  2004/06/13 09:38:20  rurban
728 // isa() workaround, if stdlib.php is not loaded
729 //
730 // Revision 1.26  2004/06/02 18:01:45  rurban
731 // init global FileFinder to add proper include paths at startup
732 //   adds PHPWIKI_DIR if started from another dir, lib/pear also
733 // fix slashify for Windows
734 // fix USER_AUTH_POLICY=old, use only USER_AUTH_ORDER methods (besides HttpAuth)
735 //
736 // Revision 1.25  2004/06/02 10:18:36  rurban
737 // assert only if DEBUG is non-false
738 //
739 // Revision 1.24  2004/05/27 17:49:05  rurban
740 // renamed DB_Session to DbSession (in CVS also)
741 // added WikiDB->getParam and WikiDB->getAuthParam method to get rid of globals
742 // remove leading slash in error message
743 // added force_unlock parameter to File_Passwd (no return on stale locks)
744 // fixed adodb session AffectedRows
745 // added FileFinder helpers to unify local filenames and DATA_PATH names
746 // editpage.php: new edit toolbar javascript on ENABLE_EDIT_TOOLBAR
747 //
748 //
749
750 // (c-file-style: "gnu")
751 // Local Variables:
752 // mode: php
753 // tab-width: 8
754 // c-basic-offset: 4
755 // c-hanging-comment-ender-p: nil
756 // indent-tabs-mode: nil
757 // End:
758 ?>