]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/FuzzyPages.php
Display error message if no argument provided
[SourceForge/phpwiki.git] / lib / plugin / FuzzyPages.php
1 <?php // -*-php-*-
2 rcs_id('$Id$');
3 /*
4  Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam
5  Copyright 2009 Marc-Etienne Vargenau, Alcatel-Lucent
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  * FuzzyPages is plugin which searches for similar page titles.
26  *
27  * Pages are considered similar by averaging the similarity scores of
28  * the spelling comparison and the metaphone comparison for each page
29  * title in the database (php's metaphone() is an improved soundex
30  * function).
31  *
32  * http://www.php.net/manual/en/function.similar-text.php
33  * http://www.php.net/manual/en/function.metaphone.php
34  */
35 class WikiPlugin_FuzzyPages
36 extends WikiPlugin
37 {
38     function getName() {
39         return _("FuzzyPages");
40     }
41
42     function getDescription() {
43         return sprintf(_("Search for page titles similar to %s."),
44                        '[pagename]');
45     }
46
47     function getVersion() {
48         return preg_replace("/[Revision: $]/", '',
49                             "\$Revision$");
50     }
51
52     function getDefaultArguments() {
53         return array('s'     => false,
54                      'debug' => false);
55     }
56
57     function spelling_similarity($subject) {
58         $spelling_similarity_score = 0;
59         similar_text($subject, $this->_searchterm,
60                      $spelling_similarity_score);
61         return $spelling_similarity_score;
62     }
63
64     function sound_similarity($subject) {
65         $sound_similarity_score = 0;
66         similar_text(metaphone($subject), $this->_searchterm_metaphone,
67                      $sound_similarity_score);
68         return $sound_similarity_score;
69     }
70
71     function averageSimilarities($subject) {
72         return ($this->spelling_similarity($subject)
73                 + $this->sound_similarity($subject)) / 2;
74     }
75
76     function collectSimilarPages(&$list, &$dbi) {
77         if (! defined('MIN_SCORE_CUTOFF'))
78             define('MIN_SCORE_CUTOFF', 33);
79
80         $this->_searchterm_metaphone = metaphone($this->_searchterm);
81
82         $allPages = $dbi->getAllPages();
83
84         while ($pagehandle = $allPages->next()) {
85             $pagename = $pagehandle->getName();
86             $similarity_score = $this->averageSimilarities($pagename);
87             if ($similarity_score > MIN_SCORE_CUTOFF)
88                 $list[$pagename] = $similarity_score;
89         }
90     }
91
92     function sortCollectedPages(&$list) {
93         arsort($list, SORT_NUMERIC);
94     }
95
96     function addTableCaption(&$table, &$dbi) {
97         if ($dbi->isWikiPage($this->_searchterm))
98             $link = WikiLink($this->_searchterm, 'auto');
99         else
100             $link = $this->_searchterm;
101         $caption = fmt("These page titles match fuzzy with '%s'", $link);
102         $table->pushContent(HTML::caption(array('align'=>'top'), $caption));
103     }
104
105     function addTableHead(&$table) {
106         $row = HTML::tr(HTML::th(_("Name")),
107                         HTML::th(array('align' => 'right'), _("Score")));
108         if ($this->debug)
109             $this->_pushDebugHeadingTDinto($row);
110
111         $table->pushContent(HTML::thead($row));
112     }
113
114     function addTableBody(&$list, &$table) {
115         if (! defined('HIGHLIGHT_ROWS_CUTOFF_SCORE'))
116             define('HIGHLIGHT_ROWS_CUTOFF_SCORE', 60);
117
118         $tbody = HTML::tbody();
119         foreach ($list as $found_pagename => $score) {
120             $row = HTML::tr(array('class' =>
121                                   $score > HIGHLIGHT_ROWS_CUTOFF_SCORE
122                                   ? 'evenrow' : 'oddrow'),
123                             HTML::td(WikiLink($found_pagename)),
124                             HTML::td(array('align' => 'right'),
125                                      round($score)));
126
127             if ($this->debug)
128                 $this->_pushDebugTDinto($row, $found_pagename);
129
130             $tbody->pushContent($row);
131         }
132         $table->pushContent($tbody);
133     }
134
135     function formatTable(&$list, &$dbi) {
136
137         $table = HTML::table(array('cellpadding' => 2,
138                                    'cellspacing' => 1,
139                                    'border'      => 0,
140                                    'class' => 'pagelist'));
141         $this->addTableCaption($table, $dbi);
142         $this->addTableHead($table);
143         $this->addTableBody($list, $table);
144         return $table;
145     }
146
147
148     function run($dbi, $argstr, &$request, $basepage) {
149         $args = $this->getArgs($argstr, $request);
150         extract($args);
151         if (empty($s)) {
152             return HTML::div(array('class' => "error"), "Please provide 's' argument to the plugin.");
153         }
154         $this->debug = $debug;
155
156         $this->_searchterm = $s;
157         $this->_list = array();
158
159         $this->collectSimilarPages($this->_list, $dbi);
160         $this->sortCollectedPages($this->_list);
161         return $this->formatTable($this->_list, $dbi);
162     }
163
164
165
166     function _pushDebugHeadingTDinto(&$row) {
167         $row->pushContent(HTML::td(_("Spelling Score")),
168                           HTML::td(_("Sound Score")),
169                           HTML::td('Metaphones'));
170     }
171
172     function _pushDebugTDinto(&$row, $pagename) {
173         // This actually calculates everything a second time for each pagename
174         // so the individual scores can be displayed separately for debugging.
175         $debug_spelling = round($this->spelling_similarity($pagename), 1);
176         $debug_sound = round($this->sound_similarity($pagename), 1);
177         $debug_metaphone = sprintf("(%s, %s)", metaphone($pagename),
178                                    $this->_searchterm_metaphone);
179
180         $row->pushcontent(HTML::td(array('align' => 'center'), $debug_spelling),
181                           HTML::td(array('align' => 'center'), $debug_sound),
182                           HTML::td($debug_metaphone));
183     }
184 };
185
186 // Local Variables:
187 // mode: php
188 // tab-width: 8
189 // c-basic-offset: 4
190 // c-hanging-comment-ender-p: nil
191 // indent-tabs-mode: nil
192 // End:
193 ?>