]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/backend/file.php
just aesthetics
[SourceForge/phpwiki.git] / lib / WikiDB / backend / file.php
1 <?php // -*-php-*-
2 rcs_id('$Id: file.php,v 1.17 2004-07-09 13:05:34 rurban Exp $');
3
4 /**
5  Copyright 1999, 2000, 2001, 2002, 2003 $ThePhpWikiProgrammingTeam
6
7  This file is part of PhpWiki.
8
9  PhpWiki is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13
14  PhpWiki is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18
19  You should have received a copy of the GNU General Public License
20  along with PhpWiki; if not, write to the Free Software
21  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24
25
26 /**
27  * Backend for handling file storage. 
28  *
29  * Author: Jochen Kalmbach, Jochen@kalmbachnet.de
30  */
31
32 /*
33  * TODO: 
34  * - Implement "optimize" / "sync" / "check" / "rebuild"
35  * - Optimize "get_previous_version"
36  * - Optimize "get_links" (reversed = true)
37  * - Optimize "get_all_revisions"
38  * - Optimize "most_popular" (separate file for "hitcount", 
39  *   which contains all pages)
40  * - Optimize "most_recent"
41  * - What should be done in "lock"/"unlock"/"close" ?
42  * - "WikiDB_backend_file_iter": Do I need to return 'version' and 'versiondata' ?
43  *
44  */
45
46 require_once('lib/WikiDB/backend.php');
47 require_once('lib/ErrorManager.php');
48
49 class WikiDB_backend_file
50 extends WikiDB_backend
51 {
52     var $data_dir;
53     var $_dir_names;
54
55     var $_page_data;          // temporarily stores the pagedata (via _loadPageData)
56     var $_page_version_data;  // temporarily stores the versiondata (via _loadVersionData)
57     var $_latest_versions;    // temporarily stores the latest version-numbers (for every pagename)
58
59     function WikiDB_backend_file( $dbparam )
60     {
61         $this->data_dir = $dbparam['directory'];
62         if (file_exists($this->data_dir) and is_file($this->data_dir))
63                 unlink($this->data_dir);
64         if (is_dir($this->data_dir) == false) {
65             mkdir($this->data_dir, 0755);
66         }
67
68         $this->_dir_names
69             = array('ver_data'     => $this->data_dir.'/'.'ver_data',
70                     'page_data'    => $this->data_dir.'/'.'page_data',
71                     'latest_ver'   => $this->data_dir.'/'.'latest_ver',
72                     'links'        => $this->data_dir.'/'.'links' );
73
74         foreach ($this->_dir_names as $key => $val) {
75                 if (file_exists($val) and is_file($val))
76                     unlink($val);
77             if (is_dir($val) == false)
78                 mkdir($val, 0755);
79         }
80
81         $this->_page_data = NULL;
82         $this->_page_version_data = NULL;
83         $this->_latest_versions = NULL;
84
85
86     }
87
88     // *********************************************************************
89     // common file load / save functions:
90     function _pagename2filename($type, $pagename, $version) {
91          if ($version == 0)
92              return $this->_dir_names[$type].'/'.urlencode($pagename);
93          else
94              return $this->_dir_names[$type].'/'.urlencode($pagename).'--'.$version;
95     }
96
97     function _loadPage($type, $pagename, $version, $set_pagename = true) {
98       $filename = $this->_pagename2filename($type, $pagename, $version);
99       if (!file_exists($filename)) return NULL;
100       if (!filesize($filename)) return array();
101       if ($fd = @fopen($filename, "rb")) {
102          $locked = flock($fd, 1); # Read lock
103          if (!$locked) { 
104             ExitWiki("Timeout while obtaining lock. Please try again"); 
105          }
106          if ($data = fread($fd, filesize($filename))) {
107             $pd = unserialize($data);
108             if ($set_pagename == true)
109                 $pd['pagename'] = $pagename;
110             if ($version != 0)
111                 $pd['version'] = $version;
112             if (!is_array($pd))
113                 ExitWiki(sprintf(gettext("'%s': corrupt file"),
114                                  htmlspecialchars($filename)));
115             else
116               return $pd;
117          }      
118          fclose($fd);
119       }
120       return NULL;
121     }
122
123     function _savePage($type, $pagename, $version, $data) {
124         $filename = $this->_pagename2filename($type, $pagename, $version);
125         if($fd = fopen($filename, 'a+b')) { 
126            $locked = flock($fd,2); #Exclusive blocking lock 
127            if (!$locked) { 
128               ExitWiki("Timeout while obtaining lock. Please try again"); 
129            }
130
131            rewind($fd);
132            ftruncate($fd, 0);
133            $pagedata = serialize($data);
134            fwrite($fd, $pagedata); 
135            fclose($fd);
136         } else {
137            ExitWiki("Error while writing page '$pagename'");
138         }
139     }
140
141     function _removePage($type, $pagename, $version) {
142         $filename = $this->_pagename2filename($type, $pagename, $version);
143         if (!file_exists($filename)) return NULL;
144         $f = @unlink($filename);
145         if ($f == false)
146             trigger_error("delete file failed: ".$filename." ver: ".$version, E_USER_WARNING);
147     }
148
149     // *********************************************************************
150
151     // *********************************************************************
152     // Load/Save Version-Data
153     function _loadVersionData($pagename, $version) {
154         if ($this->_page_version_data != NULL) {
155             if ( ($this->_page_version_data['pagename'] == $pagename) && 
156                 ($this->_page_version_data['version'] == $version) ) {
157                 return $this->_page_version_data;
158              }
159         }
160         $vd = $this->_loadPage('ver_data', $pagename, $version);
161         if ($vd != NULL) {
162             $this->_page_version_data = $vd;
163             if ( ($this->_page_version_data['pagename'] == $pagename) && 
164                 ($this->_page_version_data['version'] == $version) ) {
165                 return $this->_page_version_data;
166              }
167         }
168         return NULL;
169     }
170
171     function _saveVersionData($pagename, $version, $data) {
172         $this->_savePage('ver_data', $pagename, $version, $data);
173
174         // check if this is a newer version:
175         if ($this->_getLatestVersion($pagename) < $version) {
176             // write new latest-version-info
177             $this->_setLatestVersion($pagename, $version);
178         }
179     }
180
181
182     // *********************************************************************
183     // Load/Save Page-Data
184     function _loadPageData($pagename) {
185         if ($this->_page_data != NULL) {
186             if ($this->_page_data['pagename'] == $pagename) {
187                 return $this->_page_data;
188              }
189         }
190         $pd = $this->_loadPage('page_data', $pagename, 0);
191         if ($pd != NULL)
192             $this->_page_data = $pd;
193         if ($this->_page_data != NULL) {
194             if ($this->_page_data['pagename'] == $pagename) {
195                 return $this->_page_data;
196              }
197         }
198         return array();  // no values found
199     }
200
201     function _savePageData($pagename, $data) {
202         $this->_savePage('page_data', $pagename, 0, $data);
203     }
204
205     // *********************************************************************
206     // Load/Save Latest-Version
207     function _saveLatestVersions() {
208         $data = $this->_latest_versions;
209         if ($data == NULL)
210             $data = array();
211         $this->_savePage('latest_ver', 'latest_versions', 0, $data);
212     }
213
214     function _setLatestVersion($pagename, $version) {
215         // make sure the page version list is loaded:
216         $this->_getLatestVersion($pagename);
217         if ($version > 0) {
218             $this->_getLatestVersion($pagename);
219             $this->_latest_versions[$pagename] = $version;
220         }
221         else {
222             // Remove this page from the Latest-Version-List:
223             unset($this->_latest_versions[$pagename]);
224         }
225         $this->_saveLatestVersions();
226     }
227
228     function _loadLatestVersions() {
229         if ($this->_latest_versions != NULL)
230             return;
231
232         $pd = $this->_loadPage('latest_ver', 'latest_versions', 0, false);
233         if ($pd != NULL)
234             $this->_latest_versions = $pd;
235         else
236             $this->_latest_versions = array(); // empty array
237     }
238
239     function _getLatestVersion($pagename) {
240        $this->_loadLatestVersions();
241        if (array_key_exists($pagename, $this->_latest_versions) == false)
242            return 0; // do version exists
243        return $this->_latest_versions[$pagename];
244     }
245
246
247     // *********************************************************************
248     // Load/Save Page-Links
249     function _loadPageLinks($pagename) {
250         $pd = $this->_loadPage('links', $pagename, 0, false);
251         if ($pd != NULL)
252             return $pd;;
253         return array();  // no values found
254     }
255
256     function _savePageLinks($pagename, $links) {
257         $this->_savePage('links', $pagename, 0, $links);
258     }
259
260
261
262     /**
263      * Get page meta-data from database.
264      *
265      * @param $pagename string Page name.
266      * @return hash
267      * Returns a hash containing the page meta-data.
268      * Returns an empty array if there is no meta-data for the requested page.
269      * Keys which might be present in the hash are:
270      * <dl>
271      *  <dt> locked  <dd> If the page is locked.
272      *  <dt> hits    <dd> The page hit count.
273      *  <dt> created <dd> Unix time of page creation. (FIXME: Deprecated: I
274      *                    don't think we need this...) 
275      * </dl>
276      */
277     function get_pagedata($pagename) {
278         return $this->_loadPageData($pagename);
279     }
280
281     /**
282      * Update the page meta-data.
283      *
284      * Set page meta-data.
285      *
286      * Only meta-data whose keys are preset in $newdata is affected.
287      *
288      * For example:
289      * <pre>
290      *   $backend->update_pagedata($pagename, array('locked' => 1)); 
291      * </pre>
292      * will set the value of 'locked' to 1 for the specified page, but it
293      * will not affect the value of 'hits' (or whatever other meta-data
294      * may have been stored for the page.)
295      *
296      * To delete a particular piece of meta-data, set it's value to false.
297      * <pre>
298      *   $backend->update_pagedata($pagename, array('locked' => false)); 
299      * </pre>
300      *
301      * @param $pagename string Page name.
302      * @param $newdata hash New meta-data.
303      */
304     /**
305      * This will create a new page if page being requested does not
306      * exist.
307      */
308     function update_pagedata($pagename, $newdata) {
309         $data = $this->get_pagedata($pagename);
310         if (count($data) == 0) {
311             $this->_savePageData($pagename, $newdata);  // create a new pagedata-file
312             return;
313         }
314         
315         foreach ($newdata as $key => $val) {
316             if (empty($val))
317                 unset($data[$key]);
318             else
319                 $data[$key] = $val;
320         }
321         $this->_savePageData($pagename, $data);  // write new pagedata-file
322     }
323     
324
325     /**
326      * Get the current version number for a page.
327      *
328      * @param $pagename string Page name.
329      * @return int The latest version number for the page.  Returns zero if
330      *  no versions of a page exist.
331      */
332     function get_latest_version($pagename) {
333         return $this->_getLatestVersion($pagename);
334     }
335     
336     /**
337      * Get preceding version number.
338      *
339      * @param $pagename string Page name.
340      * @param $version int Find version before this one.
341      * @return int The version number of the version in the database which
342      *  immediately preceeds $version.
343      *
344      * FIXED: Check if this version really exists!
345      */
346     function get_previous_version($pagename, $version) {
347         $prev = ($version > 0 ? $version - 1 : 0);
348         while ($prev and !file_exists($this->_pagename2filename('ver_data', $pagename, $prev))) {
349             $prev--;
350         }
351         return $prev;
352     }
353     
354     /**
355      * Get revision meta-data and content.
356      *
357      * @param $pagename string Page name.
358      * @param $version integer Which version to get.
359      * @param $want_content boolean
360      *  Indicates the caller really wants the page content.  If this
361      *  flag is not set, the backend is free to skip fetching of the
362      *  page content (as that may be expensive).  If the backend omits
363      *  the content, the backend might still want to set the value of
364      *  '%content' to the empty string if it knows there's no content.
365      *
366      * @return hash The version data, or false if specified version does not
367      *    exist.
368      *
369      * Some keys which might be present in the $versiondata hash are:
370      * <dl>
371      * <dt> %content
372      *  <dd> This is a pseudo-meta-data element (since it's actually
373      *       the page data, get it?) containing the page content.
374      *       If the content was not fetched, this key may not be present.
375      * </dl>
376      * For description of other version meta-data see WikiDB_PageRevision::get().
377      * @see WikiDB_PageRevision::get
378      */
379     function get_versiondata($pagename, $version, $want_content = false) {
380         $vd = $this->_loadVersionData($pagename, $version);
381         if ($vd == NULL)
382             return false;
383         return $vd;
384     }
385
386     /**
387      * Rename all files for this page
388      *
389      * @access protected   Via WikiDB
390      */
391     function rename_page($pagename, $to) {
392         $version = _getLatestVersion($pagename);
393         foreach ($this->_dir_names as $type => $path) {
394             if (is_dir($path)) {
395                 $filename = $this->_pagename2filename($type, $pagename, $version);
396                 $new = $this->_pagename2filename($type, $to, $version);
397                 @rename($filename,$new);
398             }
399         }
400         $this->update_pagedata($pagename, array('pagename' => $to)); 
401         return true;
402     }
403
404     /**
405      * Delete page from the database.
406      *
407      * Delete page (and all it's revisions) from the database.
408      *
409      * @param $pagename string Page name.
410      */
411     function delete_page($pagename) {
412         $ver = $this->get_latest_version($pagename);
413         while($ver > 0) {
414             $this->_removePage('ver_data', $pagename, $ver);
415             $ver = $this->get_previous_version($pagename, $ver);
416         }
417         $this->_removePage('page_data', $pagename, 0);
418         $this->_removePage('links', $pagename, 0);
419         // remove page from latest_version...
420         $this->_setLatestVersion($pagename, 0);
421     }
422             
423     /**
424      * Delete an old revision of a page.
425      *
426      * Note that one is never allowed to delete the most recent version,
427      * but that this requirement is enforced by WikiDB not by the backend.
428      *
429      * In fact, to be safe, backends should probably allow the deletion of
430      * the most recent version.
431      *
432      * @param $pagename string Page name.
433      * @param $version integer Version to delete.
434      */
435     function delete_versiondata($pagename, $version) {
436         if ($this->get_latest_version($pagename) == $version) {
437             // try to delete the latest version!
438             // so check if an older version exist:
439             if ($this->get_versiondata($pagename, $this->get_previous_version($pagename, $version), false) == false) {
440               // there is no older version....
441               // so the completely page will be removed:
442               $this->delete_page($pagename);
443               return;
444             }
445         }
446         $this->_removePage('ver_data', $pagename, $version);
447     }                           
448
449     /**
450      * Create a new page revision.
451      *
452      * If the given ($pagename,$version) is already in the database,
453      * this method completely overwrites any stored data for that version.
454      *
455      * @param $pagename string Page name.
456      * @param $version int New revisions content.
457      * @param $data hash New revision metadata.
458      *
459      * @see get_versiondata
460      */
461     function set_versiondata($pagename, $version, $data) {
462         $this->_saveVersionData($pagename, $version, $data);
463     }
464
465     /**
466      * Update page version meta-data.
467      *
468      * If the given ($pagename,$version) is already in the database,
469      * this method only changes those meta-data values whose keys are
470      * explicity listed in $newdata.
471      *
472      * @param $pagename string Page name.
473      * @param $version int New revisions content.
474      * @param $newdata hash New revision metadata.
475      * @see set_versiondata, get_versiondata
476      */
477     function update_versiondata($pagename, $version, $newdata) {
478         $data = $this->get_versiondata($pagename, $version, true);
479         if (!$data) {
480             assert($data);
481             return;
482         }
483         foreach ($newdata as $key => $val) {
484             if (empty($val))
485                 unset($data[$key]);
486             else
487                 $data[$key] = $val;
488         }
489         $this->set_versiondata($pagename, $version, $data);
490     }
491     
492     /**
493      * Set links for page.
494      *
495      * @param $pagename string Page name.
496      *
497      * @param $links array List of page(names) which page links to.
498      */
499     function set_links($pagename, $links) {
500         $this->_savePageLinks($pagename, $links);
501     }
502         
503     /**
504      * Find pages which link to or are linked from a page.
505      *
506      * @param $pagename string Page name.
507      * @param $reversed boolean True to get backlinks.
508      *
509      * FIXME: array or iterator?
510      * @return object A WikiDB_backend_iterator.
511      */
512     function get_links($pagename, $reversed) {
513         if ($reversed == false)
514             return new WikiDB_backend_file_iter($this, $this->_loadPageLinks($pagename));
515
516         $this->_loadLatestVersions();
517         $pagenames = $this->_latest_versions;  // now we have an array with the key is the pagename of all pages
518
519         $out = array();  // create empty out array
520
521         foreach ($pagenames as $key => $val) {
522             $links = $this->_loadPageLinks($key);
523             foreach ($links as $key2 => $val2) {
524                 if ($val2 == $pagename)
525                     array_push($out, $key);
526             }
527         }
528         return new WikiDB_backend_file_iter($this, $out);
529     }
530
531     /**
532      * Get all revisions of a page.
533      *
534      * @param $pagename string The page name.
535      * @return object A WikiDB_backend_iterator.
536      */
537     /*
538     function get_all_revisions($pagename) {
539         include_once('lib/WikiDB/backend/dumb/AllRevisionsIter.php');
540         return new WikiDB_backend_dumb_AllRevisionsIter($this, $pagename);
541     }
542     */
543     
544     /**
545      * Get all pages in the database.
546      *
547      * Pages should be returned in alphabetical order if that is
548      * feasable.
549      *
550      * @access protected
551      *
552      * @param $include_defaulted boolean
553      * If set, even pages with no content will be returned
554      * --- but still only if they have at least one revision (not
555      * counting the default revision 0) entered in the database.
556      *
557      * Normally pages whose current revision has empty content
558      * are not returned as these pages are considered to be
559      * non-existing.
560      *
561      * @return object A WikiDB_backend_iterator.
562      */
563     function get_all_pages($include_deleted=false, $sortby=false, $limit=false) {
564         require_once("lib/PageList.php");
565         $this->_loadLatestVersions();
566         $a = array_keys($this->_latest_versions);
567         if (empty($a))
568             return new WikiDB_backend_file_iter($this, $a);
569         $sortby = $this->sortby($sortby, 'db');
570         switch ($sortby) {
571         case '': break;
572         case 'pagename ASC':  sort($a); break;
573         case 'pagename DESC': rsort($a); break;
574         }
575         return new WikiDB_backend_file_iter($this, $a);
576     }
577
578     function sortable_columns() {
579         return array('pagename');
580     }
581
582     function numPages($filter=false, $exclude='') {
583         $this->_loadLatestVersions();
584         return count($this->_latest_versions);
585     }
586
587     /**
588      * Lock backend database.
589      *
590      * Calls may be nested.
591      *
592      * @param $write_lock boolean Unless this is set to false, a write lock
593      *     is acquired, otherwise a read lock.  If the backend doesn't support
594      *     read locking, then it should make a write lock no matter which type
595      *     of lock was requested.
596      *
597      *     All backends <em>should</em> support write locking.
598      */
599     function lock($write_lock = true) {
600         //trigger_error("lock: Not Implemented", E_USER_WARNING);
601     }
602
603     /**
604      * Unlock backend database.
605      *
606      * @param $force boolean Normally, the database is not unlocked until
607      *  unlock() is called as many times as lock() has been.  If $force is
608      *  set to true, the the database is unconditionally unlocked.
609      */
610     function unlock($force = false) {
611         //trigger_error("unlock: Not Implemented", E_USER_WARNING);
612     }
613
614
615     /**
616      * Close database.
617      */
618     function close () {
619         //trigger_error("close: Not Implemented", E_USER_WARNING);
620     }
621
622     /**
623      * Synchronize with filesystem.
624      *
625      * This should flush all unwritten data to the filesystem.
626      */
627     function sync() {
628         //trigger_error("sync: Not Implemented", E_USER_WARNING);
629     }
630
631     /**
632      * Optimize the database.
633      */
634     function optimize() {
635         return 0;//trigger_error("optimize: Not Implemented", E_USER_WARNING);
636     }
637
638     /**
639      * Check database integrity.
640      *
641      * This should check the validity of the internal structure of the database.
642      * Errors should be reported via:
643      * <pre>
644      *   trigger_error("Message goes here.", E_USER_WARNING);
645      * </pre>
646      *
647      * @return boolean True iff database is in a consistent state.
648      */
649     function check() {
650         //trigger_error("check: Not Implemented", E_USER_WARNING);
651     }
652
653     /**
654      * Put the database into a consistent state.
655      *
656      * This should put the database into a consistent state.
657      * (I.e. rebuild indexes, etc...)
658      *
659      * @return boolean True iff successful.
660      */
661     function rebuild() {
662         //trigger_error("rebuild: Not Implemented", E_USER_WARNING);
663     }
664
665     function _parse_searchwords($search) {
666         $search = strtolower(trim($search));
667         if (!$search)
668             return array(array(),array());
669         
670         $words = preg_split('/\s+/', $search);
671         $exclude = array();
672         foreach ($words as $key => $word) {
673             if ($word[0] == '-' && $word != '-') {
674                 $word = substr($word, 1);
675                 $exclude[] = preg_quote($word);
676                 unset($words[$key]);
677             }
678         }
679         return array($words, $exclude);
680     }
681        
682 };
683
684 class WikiDB_backend_file_iter extends WikiDB_backend_iterator
685 {
686     function WikiDB_backend_file_iter(&$backend, &$query_result) {
687         $this->_backend = &$backend;
688         $this->_result = $query_result;
689
690         if (count($this->_result) > 0)
691             reset($this->_result);
692     }
693     
694     function next() {
695         if (!$this->_result)
696             return false;
697         if (count($this->_result) <= 0)
698             return false;
699
700         $e = each($this->_result);
701         if ($e == false) {
702             return false;
703         }
704         
705         $pn = $e[1];
706         $pagedata = $this->_backend->get_pagedata($pn);
707         // don't pass _cached_html via iterators
708         if (isset($pagedata['_cached_html']))
709             unset($pagedata['_cached_html']);
710         unset($pagedata['pagename']);
711         $rec = array('pagename' => $pn,
712                      'pagedata' => $pagedata);
713         //$rec['version'] = $backend->get_latest_version($pn);
714         //$rec['versiondata'] = $backend->get_versiondata($pn, $rec['version'], true);
715         return $rec;
716     }
717
718     function count() {
719         return count($this->_result);
720     }
721     
722     function free () {
723     }
724 }
725
726 // $Log: not supported by cvs2svn $
727 // Revision 1.16  2004/07/09 12:47:45  rurban
728 // dont cache _ cached_html and pagename in flatfile page iterators
729 //
730 // Revision 1.15  2004/07/09 10:06:50  rurban
731 // Use backend specific sortby and sortable_columns method, to be able to
732 // select between native (Db backend) and custom (PageList) sorting.
733 // Fixed PageList::AddPageList (missed the first)
734 // Added the author/creator.. name to AllPagesBy...
735 //   display no pages if none matched.
736 // Improved dba and file sortby().
737 // Use &$request reference
738 //
739 // Revision 1.14  2004/07/08 17:31:43  rurban
740 // improve numPages for file (fixing AllPagesTest)
741 //
742 // Revision 1.13  2004/07/08 15:23:59  rurban
743 // less verbose for tests
744 //
745 // Revision 1.12  2004/07/08 13:50:32  rurban
746 // various unit test fixes: print error backtrace on _DEBUG_TRACE; allusers fix; new PHPWIKI_NOMAIN constant for omitting the mainloop
747 //
748 // Revision 1.11  2004/07/08 11:12:49  rurban
749 // quiet the testruns
750 //
751 // Revision 1.10  2004/06/03 22:08:17  rurban
752 // fix bug #963268 (check existing previous version)
753 //
754 // Revision 1.9  2004/04/27 16:03:05  rurban
755 // missing pageiter::count methods
756 //
757 // Revision 1.8  2004/03/01 13:48:45  rurban
758 // rename fix
759 // p[] consistency fix
760 //
761 // Revision 1.7  2004/02/12 14:11:36  rurban
762 // more rename_page backend methods: only tested for PearDB! please help
763 //
764 // Revision 1.6  2004/01/26 09:17:51  rurban
765 // * changed stored pref representation as before.
766 //   the array of objects is 1) bigger and 2)
767 //   less portable. If we would import packed pref
768 //   objects and the object definition was changed, PHP would fail.
769 //   This doesn't happen with an simple array of non-default values.
770 // * use $prefs->retrieve and $prefs->store methods, where retrieve
771 //   understands the interim format of array of objects also.
772 // * simplified $prefs->get() and fixed $prefs->set()
773 // * added $user->_userid and class '_WikiUser' portability functions
774 // * fixed $user object ->_level upgrading, mostly using sessions.
775 //   this fixes yesterdays problems with loosing authorization level.
776 // * fixed WikiUserNew::checkPass to return the _level
777 // * fixed WikiUserNew::isSignedIn
778 // * added explodePageList to class PageList, support sortby arg
779 // * fixed UserPreferences for WikiUserNew
780 // * fixed WikiPlugin for empty defaults array
781 // * UnfoldSubpages: added pagename arg, renamed pages arg,
782 //   removed sort arg, support sortby arg
783 //
784 // Revision 1.5  2004/01/25 08:17:29  rurban
785 // ORDER BY support for all other backends,
786 // all non-SQL simply ignoring it, using plain old dumb_iter instead
787 //
788 // Revision 1.4  2003/02/24 01:53:28  dairiki
789 // Bug fix.  Don't need to urldecode pagenames in WikiDB_backend_file_iter.
790 //
791 // Revision 1.3  2003/01/04 03:41:51  wainstead
792 // Added copyleft flowerboxes
793 //
794 // Revision 1.2  2003/01/04 03:30:34  wainstead
795 // added log tag, converted file to unix format
796 //
797
798 // For emacs users
799 // Local Variables:
800 // mode: php
801 // tab-width: 8
802 // c-basic-offset: 4
803 // c-hanging-comment-ender-p: nil
804 // indent-tabs-mode: nil
805 // End:
806 ?>