]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiDB/backend.php
include [all] Include and file path should be devided with single space. File path...
[SourceForge/phpwiki.git] / lib / WikiDB / backend.php
1 <?php
2
3 /*
4  * Copyright 2004-2010 Reini Urban
5  *
6  * This file is part of PhpWiki.
7  *
8  * PhpWiki is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * PhpWiki is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 /*
24   Pagedata
25
26    maintained by WikiPage
27     //:latestversion
28     //:deleted (*)     (Set if latest content is empty.)
29     //:pagename (*)
30
31     hits
32     is_locked
33
34   Versiondata
35
36     %content (?should this be here?)
37     _supplanted : Time version ceased to be the current version
38
39     mtime (*)   : Time of version edit.
40     orig_mtime
41     is_minor_edit (*)
42     author      : nominal author
43     author_id   : authenticated author
44     summary
45
46     //version
47     //created (*)
48     //%superceded
49
50     //:serial
51
52      (types are scalars: strings, ints, bools)
53 */
54
55 /**
56  * A WikiDB_backend handles the storage and retrieval of data for a WikiDB.
57  *
58  * It does not have to be this way, of course, but the standard WikiDB uses
59  * a WikiDB_backend.  (Other WikiDB's could be written which use some other
60  * method to access their underlying data store.)
61  *
62  * The interface outlined here seems to work well with both RDBM based
63  * and flat DBM/hash based methods of data storage.
64  *
65  * Though it contains some default implementation of certain methods,
66  * this is an abstract base class.  It is expected that most efficient
67  * backends will override nearly all the methods in this class.
68  *
69  * @access protected
70  * @see WikiDB
71  */
72 class WikiDB_backend
73 {
74     /**
75      * Get page meta-data from database.
76      *
77      * @param $pagename string Page name.
78      * @return hash
79      * Returns a hash containing the page meta-data.
80      * Returns an empty array if there is no meta-data for the requested page.
81      * Keys which might be present in the hash are:
82      * <dl>
83      *  <dt> locked  <dd> If the page is locked.
84      *  <dt> hits    <dd> The page hit count.
85      *  <dt> created <dd> Unix time of page creation. (FIXME: Deprecated: I
86      *                    don't think we need this...)
87      * </dl>
88      */
89     function get_pagedata($pagename) {
90         trigger_error("virtual", E_USER_ERROR);
91     }
92
93     /**
94      * Update the page meta-data.
95      *
96      * Set page meta-data.
97      *
98      * Only meta-data whose keys are preset in $newdata is affected.
99      *
100      * For example:
101      * <pre>
102      *   $backend->update_pagedata($pagename, array('locked' => 1));
103      * </pre>
104      * will set the value of 'locked' to 1 for the specified page, but it
105      * will not affect the value of 'hits' (or whatever other meta-data
106      * may have been stored for the page.)
107      *
108      * To delete a particular piece of meta-data, set its value to false.
109      * <pre>
110      *   $backend->update_pagedata($pagename, array('locked' => false));
111      * </pre>
112      *
113      * @param $pagename string Page name.
114      * @param $newdata hash New meta-data.
115      */
116     function update_pagedata($pagename, $newdata) {
117         trigger_error("virtual", E_USER_ERROR);
118     }
119
120     /**
121      * Get the current version number for a page.
122      *
123      * @param $pagename string Page name.
124      * @return int The latest version number for the page.  Returns zero if
125      *  no versions of a page exist.
126      */
127     function get_latest_version($pagename) {
128         trigger_error("virtual", E_USER_ERROR);
129     }
130
131     /**
132      * Get preceding version number.
133      *
134      * @param $pagename string Page name.
135      * @param $version int Find version before this one.
136      * @return int The version number of the version in the database which
137      *  immediately preceeds $version.
138      */
139     function get_previous_version($pagename, $version) {
140         trigger_error("virtual", E_USER_ERROR);
141     }
142
143     /**
144      * Get revision meta-data and content.
145      *
146      * @param $pagename string Page name.
147      * @param $version integer Which version to get.
148      * @param $want_content boolean
149      *  Indicates the caller really wants the page content.  If this
150      *  flag is not set, the backend is free to skip fetching of the
151      *  page content (as that may be expensive).  If the backend omits
152      *  the content, the backend might still want to set the value of
153      *  '%content' to the empty string if it knows there's no content.
154      *
155      * @return hash The version data, or false if specified version does not
156      *    exist.
157      *
158      * Some keys which might be present in the $versiondata hash are:
159      * <dl>
160      * <dt> %content
161      *  <dd> This is a pseudo-meta-data element (since it's actually
162      *       the page data, get it?) containing the page content.
163      *       If the content was not fetched, this key may not be present.
164      * </dl>
165      * For description of other version meta-data see WikiDB_PageRevision::get().
166      * @see WikiDB_PageRevision::get
167      */
168     function get_versiondata($pagename, $version, $want_content = false) {
169         trigger_error("virtual", E_USER_ERROR);
170     }
171
172     /**
173      * Delete page from the database with backup possibility.
174      * This should remove all links (from the named page) from
175      * the link database.
176      *
177      * @param $pagename string Page name.
178      * i.e save_page('') and DELETE nonempty id
179      * Can be undone and is seen in RecentChanges.
180      */
181     function delete_page($pagename) {
182         $mtime = time();
183         $user =& $GLOBALS['request']->_user;
184         $vdata = array('author' => $user->getId(),
185                        'author_id' => $user->getAuthenticatedId(),
186                        'mtime' => $mtime);
187
188         $this->lock(); // critical section:
189         $version = $this->get_latest_version($pagename);
190         $this->set_versiondata($pagename, $version+1, $vdata);
191         $this->set_links($pagename, false); // links are purged.
192         // SQL needs to invalidate the non_empty id
193         if (! WIKIDB_NOCACHE_MARKUP) {
194             // need the hits, perms and LOCKED, otherwise you can reset the perm
195             // by action=remove and re-create it with default perms
196             $pagedata = $this->get_pagedata($pagename);
197             unset($pagedata['_cached_html']);
198             $this->update_pagedata($pagename, $pagedata);
199         }
200         $this->unlock();
201     }
202
203     /**
204      * Delete page (and all its revisions) from the database.
205      *
206      */
207     function purge_page($pagename) {
208         trigger_error("virtual", E_USER_ERROR);
209     }
210
211     /**
212      * Delete an old revision of a page.
213      *
214      * Note that one is never allowed to delete the most recent version,
215      * but that this requirement is enforced by WikiDB not by the backend.
216      *
217      * In fact, to be safe, backends should probably allow the deletion of
218      * the most recent version.
219      *
220      * @param $pagename string Page name.
221      * @param $version integer Version to delete.
222      */
223     function delete_versiondata($pagename, $version) {
224         trigger_error("virtual", E_USER_ERROR);
225     }
226
227     /**
228      * Create a new page revision.
229      *
230      * If the given ($pagename,$version) is already in the database,
231      * this method completely overwrites any stored data for that version.
232      *
233      * @param $pagename string Page name.
234      * @param $version int New revisions content.
235      * @param $data hash New revision metadata.
236      *
237      * @see get_versiondata
238      */
239     function set_versiondata($pagename, $version, $data) {
240         trigger_error("virtual", E_USER_ERROR);
241     }
242
243     /**
244      * Update page version meta-data.
245      *
246      * If the given ($pagename,$version) is already in the database,
247      * this method only changes those meta-data values whose keys are
248      * explicity listed in $newdata.
249      *
250      * @param $pagename string Page name.
251      * @param $version int New revisions content.
252      * @param $newdata hash New revision metadata.
253      * @see set_versiondata, get_versiondata
254      */
255     function update_versiondata($pagename, $version, $newdata) {
256         $data = $this->get_versiondata($pagename, $version, true);
257         if (!$data) {
258             assert($data);
259             return;
260         }
261         foreach ($newdata as $key => $val) {
262             if (empty($val))
263                 unset($data[$key]);
264             else
265                 $data[$key] = $val;
266         }
267         $this->set_versiondata($pagename, $version, $data);
268     }
269
270     /**
271      * Set links for page.
272      *
273      * @param $pagename string Page name.
274      *
275      * @param $links array List of page(names) which page links to.
276      */
277     function set_links($pagename, $links) {
278         trigger_error("virtual", E_USER_ERROR);
279     }
280
281     /**
282      * Find pages which link to or are linked from a page.
283      *
284      * @param $pagename string Page name.
285      * @param $reversed boolean True to get backlinks.
286      *
287      * FIXME: array or iterator?
288      * @return object A WikiDB_backend_iterator.
289      */
290     function get_links($pagename, $reversed, $include_empty=false,
291                        $sortby='', $limit='', $exclude='') {
292         //FIXME: implement simple (but slow) link finder.
293         die("FIXME get_links");
294     }
295
296     /**
297      * Get all revisions of a page.
298      *
299      * @param $pagename string The page name.
300      * @return object A WikiDB_backend_iterator.
301      */
302     function get_all_revisions($pagename) {
303         include_once 'lib/WikiDB/backend/dumb/AllRevisionsIter.php';
304         return new WikiDB_backend_dumb_AllRevisionsIter($this, $pagename);
305     }
306
307     /**
308      * Get all pages in the database.
309      *
310      * Pages should be returned in alphabetical order if that is
311      * feasable.
312      *
313      * @access protected
314      *
315      * @param $include_defaulted boolean
316      * If set, even pages with no content will be returned
317      * --- but still only if they have at least one revision (not
318      * counting the default revision 0) entered in the database.
319      *
320      * Normally pages whose current revision has empty content
321      * are not returned as these pages are considered to be
322      * non-existing.
323      *
324      * @return object A WikiDB_backend_iterator.
325      */
326     function get_all_pages($include_defaulted, $orderby=false, $limit='', $exclude='') {
327         trigger_error("virtual", E_USER_ERROR);
328     }
329
330     /**
331      * Title or full text search.
332      *
333      * Pages should be returned in alphabetical order if that is
334      * feasable.
335      *
336      * @access protected
337      *
338      * @param $search object A TextSearchQuery object describing the parsed query string,
339      *                       with efficient methods for SQL and PCRE match.
340      *
341      * @param $fullsearch boolean If true, a full text search is performed,
342      *  otherwise a title search is performed.
343      *
344      * @return object A WikiDB_backend_iterator.
345      *
346      * @see WikiDB::titleSearch
347      */
348     function text_search($search, $fulltext=false, $sortby='',
349              $limit='', $exclude='')
350     {
351         // This method implements a simple linear search
352         // through all the pages in the database.
353         //
354         // It is expected that most backends will overload
355         // this method with something more efficient.
356         include_once 'lib/WikiDB/backend/dumb/TextSearchIter.php';
357         // ignore $limit
358         $pages = $this->get_all_pages(false, $sortby, false, $exclude);
359         return new WikiDB_backend_dumb_TextSearchIter($this, $pages, $search, $fulltext,
360                                                       array('limit'   => $limit,
361                                                             'exclude' => $exclude));
362     }
363
364     /**
365      *
366      * @access protected
367      * @param $pages     object A TextSearchQuery object.
368      * @param $linkvalue object A TextSearchQuery object for the linkvalues
369      *                          (linkto, relation or backlinks or attribute values).
370      * @param $linktype  string One of the 4 linktypes.
371      * @param $relation  object A TextSearchQuery object or false.
372      * @param $options   array Currently ignored. hash of sortby, limit, exclude.
373      * @return object A WikiDB_backend_iterator.
374      * @see WikiDB::linkSearch
375      */
376     function link_search( $pages, $linkvalue, $linktype, $relation=false, $options=array() ) {
377         include_once 'lib/WikiDB/backend/dumb/LinkSearchIter.php';
378         $pageiter = $this->text_search($pages);
379         return new WikiDB_backend_dumb_LinkSearchIter($this, $pageiter, $linkvalue, $linktype, $relation, $options);
380     }
381
382     /**
383      * Find pages with highest hit counts.
384      *
385      * Find the pages with the highest hit counts.  The pages should
386      * be returned in reverse order by hit count.
387      *
388      * @access protected
389      * @param  integer $limit No more than this many pages
390      * @return object  A WikiDB_backend_iterator.
391      */
392     function most_popular($limit, $sortby='-hits') {
393         // This is method fetches all pages, then
394         // sorts them by hit count.
395         // (Not very efficient.)
396         //
397         // It is expected that most backends will overload
398         // method with something more efficient.
399         include_once 'lib/WikiDB/backend/dumb/MostPopularIter.php';
400         $pages = $this->get_all_pages(false, $sortby, false);
401         return new WikiDB_backend_dumb_MostPopularIter($this, $pages, $limit);
402     }
403
404     /**
405      * Find recent changes.
406      *
407      * @access protected
408      * @param $params hash See WikiDB::mostRecent for a description
409      *  of parameters which can be included in this hash.
410      * @return object A WikiDB_backend_iterator.
411      * @see WikiDB::mostRecent
412      */
413     function most_recent($params) {
414         // This method is very inefficient and searches through
415         // all pages for the most recent changes.
416         //
417         // It is expected that most backends will overload
418         // method with something more efficient.
419         include_once 'lib/WikiDB/backend/dumb/MostRecentIter.php';
420         $pages = $this->get_all_pages(true, '-mtime');
421         return new WikiDB_backend_dumb_MostRecentIter($this, $pages, $params);
422     }
423
424     function wanted_pages($exclude_from='', $exclude='', $sortby='', $limit='') {
425         include_once 'lib/WikiDB/backend/dumb/WantedPagesIter.php';
426         $allpages = $this->get_all_pages(true,false,false,$exclude_from);
427         return new WikiDB_backend_dumb_WantedPagesIter($this, $allpages, $exclude, $sortby, $limit);
428     }
429
430     /**
431      * Lock backend database.
432      *
433      * Calls may be nested.
434      *
435      * @param $write_lock boolean Unless this is set to false, a write lock
436      *     is acquired, otherwise a read lock.  If the backend doesn't support
437      *     read locking, then it should make a write lock no matter which type
438      *     of lock was requested.
439      *
440      *     All backends <em>should</em> support write locking.
441      */
442     function lock($write_lock = true) {
443     }
444
445     /**
446      * Unlock backend database.
447      *
448      * @param $force boolean Normally, the database is not unlocked until
449      *  unlock() is called as many times as lock() has been.  If $force is
450      *  set to true, the the database is unconditionally unlocked.
451      */
452     function unlock($force = false) {
453     }
454
455
456     /**
457      * Close database.
458      */
459     function close () {
460     }
461
462     /**
463      * Synchronize with filesystem.
464      *
465      * This should flush all unwritten data to the filesystem.
466      */
467     function sync() {
468     }
469
470     /**
471      * Optimize the database.
472      */
473     function optimize() {
474     }
475
476     /**
477      * Check database integrity.
478      *
479      * This should check the validity of the internal structure of the database.
480      * Errors should be reported via:
481      * <pre>
482      *   trigger_error("Message goes here.", E_USER_WARNING);
483      * </pre>
484      *
485      * @return boolean True iff database is in a consistent state.
486      */
487     function check($args=false) {
488     }
489
490     /**
491      * Put the database into a consistent state
492      * by reparsing and restoring all pages.
493      *
494      * This should put the database into a consistent state.
495      * (I.e. rebuild indexes, etc...)
496      *
497      * @return boolean True iff successful.
498      */
499     function rebuild($args=false) {
500     global $request;
501     $dbh = $request->getDbh();
502         $iter = $dbh->getAllPages(false);
503         while ($page = $iter->next()) {
504         $current = $page->getCurrentRevision(true);
505         $pagename = $page->getName();
506         $meta = $current->_data;
507         $version = $current->getVersion();
508         $content =& $meta['%content'];
509         $formatted = new TransformedText($page, $content, $current->getMetaData());
510         $type = $formatted->getType();
511         $meta['pagetype'] = $type->getName();
512         $links = $formatted->getWikiPageLinks(); // linkto => relation
513         $this->lock(array('version','page','recent','link','nonempty'));
514         $this->set_versiondata($pagename, $version, $meta);
515         $this->set_links($pagename, $links);
516         $this->unlock(array('version','page','recent','link','nonempty'));
517         }
518     }
519
520     function _parse_searchwords($search) {
521         $search = strtolower(trim($search));
522         if (!$search)
523             return array(array(),array());
524
525         $words = preg_split('/\s+/', $search);
526         $exclude = array();
527         foreach ($words as $key => $word) {
528             if ($word[0] == '-' && $word != '-') {
529                 $word = substr($word, 1);
530                 $exclude[] = preg_quote($word);
531                 unset($words[$key]);
532             }
533         }
534         return array($words, $exclude);
535     }
536
537     /**
538      * Split the given limit parameter into offset,limit. (offset is optional. default: 0)
539      * Duplicate the PageList function here to avoid loading the whole PageList.php
540      * Usage:
541      *   list($offset,$count) = $this->limit($args['limit']);
542      */
543     function limit($limit) {
544         if (strstr($limit, ',')) {
545             list($from, $limit) = explode(',', $limit);
546             if ((!empty($from) && !is_numeric($from)) or (!empty($limit) && !is_numeric($limit))) {
547                 return $this->error(_("Illegal 'limit' argument: must be numeric"));
548             }
549             return array($from, $limit);
550         }
551         else {
552             if (!empty($limit) && !is_numeric($limit)) {
553                 return $this->error(_("Illegal 'limit' argument: must be numeric"));
554             }
555             return array(0, $limit);
556         }
557     }
558
559     /**
560      * Handle sortby requests for the DB iterator and table header links.
561      * Prefix the column with + or - like "+pagename","-mtime", ...
562      * supported actions: 'flip_order' "mtime" => "+mtime" => "-mtime" ...
563      *                    'db'         "-pagename" => "pagename DESC"
564      * In PageList all columns are sortable. (patch by DanFr)
565      * Here with the backend only some, the rest is delayed to PageList.
566      * (some kind of DumbIter)
567      * Duplicate the PageList function here to avoid loading the whole
568      * PageList.php, and it forces the backend specific sortable_columns()
569      */
570     function sortby ($column, $action, $sortable_columns=false) {
571         if (empty($column)) return '';
572         //support multiple comma-delimited sortby args: "+hits,+pagename"
573         if (strstr($column, ',')) {
574             $result = array();
575             foreach (explode(',', $column) as $col) {
576                 if (empty($this))
577                     $result[] = WikiDB_backend::sortby($col, $action);
578                 else
579                     $result[] = $this->sortby($col, $action);
580             }
581             return join(",",$result);
582         }
583         if (substr($column,0,1) == '+') {
584             $order = '+'; $column = substr($column,1);
585         } elseif (substr($column,0,1) == '-') {
586             $order = '-'; $column = substr($column,1);
587         }
588         // default order: +pagename, -mtime, -hits
589         if (empty($order))
590             if (in_array($column,array('mtime','hits')))
591                 $order = '-';
592             else
593                 $order = '+';
594         if ($action == 'flip_order') {
595             return ($order == '+' ? '-' : '+') . $column;
596         } elseif ($action == 'init') {
597             $this->_sortby[$column] = $order;
598             return $order . $column;
599         } elseif ($action == 'check') {
600             return (!empty($this->_sortby[$column]) or
601                     ($GLOBALS['request']->getArg('sortby') and
602                      strstr($GLOBALS['request']->getArg('sortby'),$column)));
603         } elseif ($action == 'db') {
604             // native sort possible?
605             if (!empty($this) and !$sortable_columns)
606                 $sortable_columns = $this->sortable_columns();
607             if (in_array($column, $sortable_columns))
608                 // asc or desc: +pagename, -pagename
609                 return $column . ($order == '+' ? ' ASC' : ' DESC');
610             else
611                 return '';
612         }
613         return '';
614     }
615
616     function sortable_columns() {
617         return array('pagename'/*,'mtime','author_id','author'*/);
618     }
619
620     // adds surrounding quotes
621     function quote ($s) { return "'".$s."'"; }
622     // no surrounding quotes because we know it's a string
623     function qstr ($s)  { return $s; }
624
625     function isSQL () {
626         return in_array(DATABASE_TYPE, array('SQL','ADODB','PDO'));
627     }
628
629     function backendType() {
630         return DATABASE_TYPE;
631     }
632
633     function write_accesslog(&$entry) {
634         global $request;
635         if (!$this->isSQL()) return;
636         $dbh = &$this->_dbh;
637         $log_tbl = $entry->_accesslog->logtable;
638         // duration problem: sprintf "%f" might use comma e.g. "100,201" in european locales
639         $dbh->query("INSERT INTO $log_tbl"
640                     . " (time_stamp,remote_host,remote_user,request_method,request_line,request_args,"
641                     .   "request_uri,request_time,status,bytes_sent,referer,agent,request_duration)"
642                     . " VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)",
643                     array(
644                           // Problem: date formats are backend specific. Either use unixtime as %d (long),
645                           // or the native timestamp format.
646                           $entry->time,
647                           $entry->host,
648                           $entry->user,
649                           $entry->request_method,
650                           $entry->request,
651                           $entry->request_args,
652                           $entry->request_uri,
653                           $entry->_ncsa_time($entry->time),
654                           $entry->status,
655                           (int)$entry->size,
656                           $entry->referer,
657                           $entry->user_agent,
658                           $entry->duration));
659     }
660 };
661
662 /**
663  * Iterator returned by backend methods which (possibly) return
664  * multiple records.
665  *
666  * FIXME: This might be two seperate classes: page_iter and version_iter.
667  * For the versions we have WikiDB_backend_dumb_AllRevisionsIter.
668  */
669 class WikiDB_backend_iterator
670 {
671     /**
672      * Get the next record in the iterator set.
673      *
674      * This returns a hash. The hash may contain the following keys:
675      * <dl>
676      * <dt> pagename <dt> (string) the page name or linked page name on link iterators
677      * <dt> version  <dt> (int) the version number
678      * <dt> pagedata <dt> (hash) page meta-data (as returned from backend::get_pagedata().)
679      * <dt> versiondata <dt> (hash) page meta-data (as returned from backend::get_versiondata().)
680      * <dt> linkrelation <dt> (string) the page naming the relation (e.g. isa:=page <=> isa)
681      *
682      * If this is a page iterator, it must contain the 'pagename' entry --- the others
683      * are optional.
684      *
685      * If this is a version iterator, the 'pagename', 'version', <strong>and</strong> 'versiondata'
686      * entries are mandatory.  ('pagedata' is optional.)
687      *
688      * If this is a link iterator, the 'pagename' is mandatory, 'linkrelation' is optional.
689      */
690     function next() {
691         trigger_error("virtual", E_USER_ERROR);
692     }
693
694     function count() {
695         if (!empty($this->_pages))
696         return count($this->_pages);
697     else
698         return 0;
699     }
700
701     function asArray() {
702         if (!empty($this->_pages)) {
703             reset($this->_pages);
704             return $this->_pages;
705         } else {
706             $result = array();
707             while ($page = $this->next())
708                 $result[] = $page;
709             return $result;
710         }
711     }
712
713     /**
714      * limit - if empty the pagelist iterator will do nothing.
715      * Some backends limit the result set itself (dba, file, flatfile),
716      * Some SQL based leave it to WikiDB/PageList - deferred filtering in the iterator.
717      */
718     function limit() {
719         return empty($this->_options['limit']) ? 0 : $this->_options['limit'];
720     }
721
722     /**
723      * Release resources held by this iterator.
724      */
725     function free() {
726     }
727 };
728
729 /**
730  * search baseclass, pcre-specific
731  */
732 class WikiDB_backend_search
733 {
734     function WikiDB_backend_search($search, &$dbh) {
735         $this->_dbh = $dbh;
736         $this->_case_exact = $search->_case_exact;
737         $this->_stoplist   =& $search->_stoplist;
738         $this->stoplisted = array();
739     }
740     function _quote($word) {
741         return preg_quote($word, "/");
742     }
743     //TODO: use word anchors
744     function EXACT($word) { return "^".$this->_quote($word)."$"; }
745     function STARTS_WITH($word) { return "^".$this->_quote($word); }
746     function ENDS_WITH($word) { return $this->_quote($word)."$"; }
747     function WORD($word) { return $this->_quote($word); }
748     function REGEX($word) { return $word; }
749     //TESTME
750     function _pagename_match_clause($node) {
751         $method = $node->op;
752         $word = $this->$method($node->word);
753         return "preg_match(\"/\".$word.\"/\"".($this->_case_exact ? "i":"").")";
754     }
755     /* Eliminate stoplist words.
756      *  Keep a list of Stoplisted words to inform the poor user.
757      */
758     function isStoplisted ($node) {
759         // check only on WORD or EXACT fulltext search
760         if ($node->op != 'WORD' and $node->op != 'EXACT')
761             return false;
762         if (preg_match("/^".$this->_stoplist."$/i", $node->word)) {
763             array_push($this->stoplisted, $node->word);
764             return true;
765         }
766         return false;
767     }
768     function getStoplisted($word) {
769         return $this->stoplisted;
770     }
771 }
772
773 /**
774  * search baseclass, sql-specific
775  */
776 class WikiDB_backend_search_sql extends WikiDB_backend_search
777 {
778     function _pagename_match_clause($node) {
779         // word already quoted by TextSearchQuery_node_word::_sql_quote()
780         $word = $node->sql();
781         if ($word == '%') // ALL shortcut
782             return "1=1";
783         else
784             return ($this->_case_exact
785                     ? "pagename LIKE '$word'"
786                     : "LOWER(pagename) LIKE '$word'");
787     }
788     function _fulltext_match_clause($node) {
789         // force word-style %word% for fulltext search
790         $word = '%' . $node->_sql_quote($node->word) . '%';
791         // eliminate stoplist words
792         if ($this->isStoplisted($node))
793             return "1=1";  // and (pagename or 1) => and 1
794         else
795             return $this->_pagename_match_clause($node)
796                 // probably convert this MATCH AGAINST or SUBSTR/POSITION without wildcards
797                 . ($this->_case_exact ? " OR content LIKE '$word'"
798                                       : " OR LOWER(content) LIKE '$word'");
799     }
800 }
801
802 // Local Variables:
803 // mode: php
804 // tab-width: 8
805 // c-basic-offset: 4
806 // c-hanging-comment-ender-p: nil
807 // indent-tabs-mode: nil
808 // End: