]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/plugin/FuzzyPages.php
getName should not translate
[SourceForge/phpwiki.git] / lib / plugin / FuzzyPages.php
1 <?php
2
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 along
20  * with PhpWiki; if not, write to the Free Software Foundation, Inc.,
21  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 getDescription()
39     {
40         return sprintf(_("Search for page titles similar to %s."),
41             '[pagename]');
42     }
43
44     function getDefaultArguments()
45     {
46         return array('s' => false,
47             'debug' => false);
48     }
49
50     function spelling_similarity($subject)
51     {
52         $spelling_similarity_score = 0;
53         similar_text($subject, $this->_searchterm,
54             $spelling_similarity_score);
55         return $spelling_similarity_score;
56     }
57
58     function sound_similarity($subject)
59     {
60         $sound_similarity_score = 0;
61         similar_text(metaphone($subject), $this->_searchterm_metaphone,
62             $sound_similarity_score);
63         return $sound_similarity_score;
64     }
65
66     function averageSimilarities($subject)
67     {
68         return ($this->spelling_similarity($subject)
69             + $this->sound_similarity($subject)) / 2;
70     }
71
72     function collectSimilarPages(&$list, &$dbi)
73     {
74         if (!defined('MIN_SCORE_CUTOFF'))
75             define('MIN_SCORE_CUTOFF', 33);
76
77         $this->_searchterm_metaphone = metaphone($this->_searchterm);
78
79         $allPages = $dbi->getAllPages();
80
81         while ($pagehandle = $allPages->next()) {
82             $pagename = $pagehandle->getName();
83             $similarity_score = $this->averageSimilarities($pagename);
84             if ($similarity_score > MIN_SCORE_CUTOFF)
85                 $list[$pagename] = $similarity_score;
86         }
87     }
88
89     function sortCollectedPages(&$list)
90     {
91         arsort($list, SORT_NUMERIC);
92     }
93
94     function addTableCaption(&$table, &$dbi)
95     {
96         if ($dbi->isWikiPage($this->_searchterm))
97             $link = WikiLink($this->_searchterm, 'auto');
98         else
99             $link = $this->_searchterm;
100         $caption = fmt("These page titles match fuzzy with ā€œ%sā€", $link);
101         $table->pushContent(HTML::caption($caption));
102     }
103
104     function addTableHead(&$table)
105     {
106         $row = HTML::tr(HTML::th(_("Name")), HTML::th(_("Score")));
107
108         if (defined('DEBUG') && DEBUG && $this->debug) {
109             $this->pushDebugHeadingTDinto($row);
110         }
111
112         $table->pushContent(HTML::thead($row));
113     }
114
115     function addTableBody(&$list, &$table)
116     {
117         if (!defined('HIGHLIGHT_ROWS_CUTOFF_SCORE'))
118             define('HIGHLIGHT_ROWS_CUTOFF_SCORE', 60);
119
120         $tbody = HTML::tbody();
121         foreach ($list as $found_pagename => $score) {
122             $row = HTML::tr(array('class' =>
123                 $score > HIGHLIGHT_ROWS_CUTOFF_SCORE
124                     ? 'evenrow' : 'oddrow'),
125                 HTML::td(WikiLink($found_pagename)),
126                 HTML::td(array('class' => 'align-right'),
127                     round($score)));
128
129             if (defined('DEBUG') && DEBUG && $this->debug) {
130                 $this->pushDebugTDinto($row, $found_pagename);
131             }
132
133             $tbody->pushContent($row);
134         }
135         $table->pushContent($tbody);
136     }
137
138     function formatTable(&$list, &$dbi)
139     {
140
141         if (empty($list)) {
142             return HTML::p(fmt("No fuzzy matches with ā€œ%sā€", $this->_searchterm));
143         }
144         $table = HTML::table(array('class' => 'pagelist'));
145         $this->addTableCaption($table, $dbi);
146         $this->addTableHead($table);
147         $this->addTableBody($list, $table);
148         return $table;
149     }
150
151     function run($dbi, $argstr, &$request, $basepage)
152     {
153         $args = $this->getArgs($argstr, $request);
154         extract($args);
155         if (empty($s)) {
156             return HTML();
157         }
158
159         if (defined('DEBUG') && DEBUG) {
160             $this->debug = $debug;
161         }
162
163         $this->_searchterm = $s;
164         $this->_list = array();
165
166         $this->collectSimilarPages($this->_list, $dbi);
167         $this->sortCollectedPages($this->_list);
168         return $this->formatTable($this->_list, $dbi);
169     }
170
171     private function pushDebugHeadingTDinto(&$row)
172     {
173         $row->pushContent(HTML::td(_("Spelling Score")),
174             HTML::td(_("Sound Score")),
175             HTML::td('Metaphones'));
176     }
177
178     private function pushDebugTDinto(&$row, $pagename)
179     {
180         // This actually calculates everything a second time for each pagename
181         // so the individual scores can be displayed separately for debugging.
182         $debug_spelling = round($this->spelling_similarity($pagename), 1);
183         $debug_sound = round($this->sound_similarity($pagename), 1);
184         $debug_metaphone = sprintf("(%s, %s)", metaphone($pagename),
185             $this->_searchterm_metaphone);
186
187         $row->pushcontent(HTML::td(array('class' => 'align-center'), $debug_spelling),
188             HTML::td(array('class' => 'align-center'), $debug_sound),
189             HTML::td($debug_metaphone));
190     }
191 }
192
193 // Local Variables:
194 // mode: php
195 // tab-width: 8
196 // c-basic-offset: 4
197 // c-hanging-comment-ender-p: nil
198 // indent-tabs-mode: nil
199 // End: