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