4 * Copyright 1999, 2000, 2001, 2002 $ThePhpWikiProgrammingTeam
5 * Copyright 2009 Marc-Etienne Vargenau, Alcatel-Lucent
7 * This file is part of PhpWiki.
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.
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.
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.
25 * FuzzyPages is plugin which searches for similar page titles.
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
32 * http://www.php.net/manual/en/function.similar-text.php
33 * http://www.php.net/manual/en/function.metaphone.php
35 class WikiPlugin_FuzzyPages
40 return _("FuzzyPages");
43 function getDescription()
45 return sprintf(_("Search for page titles similar to %s."),
49 function getDefaultArguments()
51 return array('s' => false,
55 function spelling_similarity($subject)
57 $spelling_similarity_score = 0;
58 similar_text($subject, $this->_searchterm,
59 $spelling_similarity_score);
60 return $spelling_similarity_score;
63 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;
71 function averageSimilarities($subject)
73 return ($this->spelling_similarity($subject)
74 + $this->sound_similarity($subject)) / 2;
77 function collectSimilarPages(&$list, &$dbi)
79 if (!defined('MIN_SCORE_CUTOFF'))
80 define('MIN_SCORE_CUTOFF', 33);
82 $this->_searchterm_metaphone = metaphone($this->_searchterm);
84 $allPages = $dbi->getAllPages();
86 while ($pagehandle = $allPages->next()) {
87 $pagename = $pagehandle->getName();
88 $similarity_score = $this->averageSimilarities($pagename);
89 if ($similarity_score > MIN_SCORE_CUTOFF)
90 $list[$pagename] = $similarity_score;
94 function sortCollectedPages(&$list)
96 arsort($list, SORT_NUMERIC);
99 function addTableCaption(&$table, &$dbi)
101 if ($dbi->isWikiPage($this->_searchterm))
102 $link = WikiLink($this->_searchterm, 'auto');
104 $link = $this->_searchterm;
105 $caption = fmt("These page titles match fuzzy with ā%sā", $link);
106 $table->pushContent(HTML::caption($caption));
109 function addTableHead(&$table)
111 $row = HTML::tr(HTML::th(_("Name")), HTML::th(_("Score")));
113 if (defined('DEBUG') && DEBUG && $this->debug) {
114 $this->_pushDebugHeadingTDinto($row);
117 $table->pushContent(HTML::thead($row));
120 function addTableBody(&$list, &$table)
122 if (!defined('HIGHLIGHT_ROWS_CUTOFF_SCORE'))
123 define('HIGHLIGHT_ROWS_CUTOFF_SCORE', 60);
125 $tbody = HTML::tbody();
126 foreach ($list as $found_pagename => $score) {
127 $row = HTML::tr(array('class' =>
128 $score > HIGHLIGHT_ROWS_CUTOFF_SCORE
129 ? 'evenrow' : 'oddrow'),
130 HTML::td(WikiLink($found_pagename)),
131 HTML::td(array('class' => 'align-right'),
134 if (defined('DEBUG') && DEBUG && $this->debug) {
135 $this->_pushDebugTDinto($row, $found_pagename);
138 $tbody->pushContent($row);
140 $table->pushContent($tbody);
143 function formatTable(&$list, &$dbi)
147 return HTML::p(fmt("No fuzzy matches with ā%sā", $this->_searchterm));
149 $table = HTML::table(array('class' => 'pagelist'));
150 $this->addTableCaption($table, $dbi);
151 $this->addTableHead($table);
152 $this->addTableBody($list, $table);
156 function run($dbi, $argstr, &$request, $basepage)
158 $args = $this->getArgs($argstr, $request);
164 if (defined('DEBUG') && DEBUG) {
165 $this->debug = $debug;
168 $this->_searchterm = $s;
169 $this->_list = array();
171 $this->collectSimilarPages($this->_list, $dbi);
172 $this->sortCollectedPages($this->_list);
173 return $this->formatTable($this->_list, $dbi);
176 private function _pushDebugHeadingTDinto(&$row)
178 $row->pushContent(HTML::td(_("Spelling Score")),
179 HTML::td(_("Sound Score")),
180 HTML::td('Metaphones'));
183 private function _pushDebugTDinto(&$row, $pagename)
185 // This actually calculates everything a second time for each pagename
186 // so the individual scores can be displayed separately for debugging.
187 $debug_spelling = round($this->spelling_similarity($pagename), 1);
188 $debug_sound = round($this->sound_similarity($pagename), 1);
189 $debug_metaphone = sprintf("(%s, %s)", metaphone($pagename),
190 $this->_searchterm_metaphone);
192 $row->pushcontent(HTML::td(array('class' => 'align-center'), $debug_spelling),
193 HTML::td(array('class' => 'align-center'), $debug_sound),
194 HTML::td($debug_metaphone));
202 // c-hanging-comment-ender-p: nil
203 // indent-tabs-mode: nil