/[drupal]/contributions/modules/taxonomy_similar/taxonomy_similar.module
ViewVC logotype

Contents of /contributions/modules/taxonomy_similar/taxonomy_similar.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.7 - (show annotations) (download) (as text)
Sat Jun 2 14:23:11 2007 UTC (2 years, 5 months ago) by morbus
Branch: MAIN
CVS Tags: DRUPAL-5--1-0, HEAD
Branch point for: DRUPAL-5
Changes since 1.6: +65 -48 lines
File MIME type: text/x-php
Webchick's D5 patch, and my further fixes.
1 <?php
2 // $Id: taxonomy_similar.module,v 1.6 2005/05/18 18:42:09 morbus Exp $
3
4 /**
5 * @file
6 * Allows users to choose similar tags after content submission.
7 */
8
9 /**
10 * Implementation of hook_help().
11 */
12 function taxonomy_similar_help($section) {
13 switch ($section) {
14 case 'taxonomy_similar/node/'. arg(2):
15 return '<p>'. t("Edit your tags below based on their similiarity to others. By choosing similar or stronger tags with higher usage, you'll make it easier for your users to find relevant and related material based on your content. This step is entirely optional - skipping it will keep your tags exactly as you've entered them.") .'</p>';
16 }
17 }
18
19 /**
20 * Implementation of hook_menu().
21 */
22 function taxonomy_similar_menu($may_cache) {
23 $items = array();
24
25 if ($may_cache) {
26 $items[] = array('title' => t('Similar tags'),
27 'path' => 'taxonomy_similar/node',
28 'callback' => 'drupal_get_form',
29 'callback arguments' => array('taxonomy_similar_overview'),
30 'access' => user_access('choose similar tags'),
31 'type' => MENU_CALLBACK);
32 }
33
34 return $items;
35 }
36
37 /**
38 * Implementation of hook_perm().
39 */
40 function taxonomy_similar_perm() {
41 return array('choose similar tags');
42 }
43
44 /**
45 * Implementation of hook_nodeapi().
46 *
47 * This is the "trickiest" part of the code, not because it's difficult, but
48 * because it is relatively odd in its implementation. The need here is to
49 * interrupt the normal drupal_goto that happens after a node insert or
50 * update. drupal_goto will either a) go to the passed location during its
51 * invocation or b) listen to the $_REQUEST['destination'] instead.
52 *
53 * We use 'destination' here instead of our own hardcoded drupal_goto so
54 * other modules have a chance to perform their own _nodeapi code (with
55 * drupal_goto, we'd interrupt the processing of all modules after ours).
56 * The (small) downside of this approach is that we can be the ONLY module
57 * that uses this hack (as any module using 'destination' before ours would
58 * be overwritten, and any module after ours would disable us). This should
59 * work for awhile, at least, since no other module implements this trickery.
60 */
61 function taxonomy_similar_nodeapi($node, $op, $arg) {
62 switch ($op) {
63 case 'insert':
64 case 'update':
65 if (user_access('choose similar tags')) {
66 $vocabulary = taxonomy_get_vocabularies();
67 $terms = taxonomy_node_get_terms($node->nid);
68 if (is_array($terms)) {
69 foreach ($terms as $term) {
70 if (!$vocabulary[$term->vid]->tags) { continue; } // not free tags.
71 $_REQUEST['destination'] = check_url('taxonomy_similar/node/'. $node->nid);
72 return; // if we're this far, our destination has been set.
73 }
74 }
75 }
76 }
77 }
78
79 /**
80 * Display a form list of all similar terms for the passed node.
81 */
82 function taxonomy_similar_overview($nid = 0) {
83 if (!$nid || !is_numeric($nid)) { drupal_not_found(); return; }
84
85 // load up all our requisite data. loading of the entire node
86 // is really a waste here, but makes the title seem nicer.
87 $vocabulary = taxonomy_get_vocabularies();
88 $terms = taxonomy_node_get_terms($nid);
89 $node = node_load($nid);
90 drupal_set_title(t('%name similar tags', array('%name' => $node->title)));
91 $form['taxonomy']['#tree'] = TRUE;
92
93 // loop through every term associated with this node,
94 // find similar keywords, and display checkboxes.
95 if (is_array($terms)) { // perform if array.
96 foreach ($terms as $term) {
97 if (!$vocabulary[$term->vid]->tags) { continue; }
98 $similars = taxonomy_similar_tags($term);
99
100 $form['taxonomy'][$term->vid][$term->tid] = array(
101 '#default_value' => 1,
102 '#title' => t('%name (@matches) is just perfect, so keep it.', array('%name' => $term->name, '@matches' => format_plural(taxonomy_similar_term_count_nodes($term->tid), '1 match', '@count matches'))),
103 '#type' => 'checkbox',
104 );
105
106 if (is_array($similars)) {
107 foreach ($similars as $similar) {
108 $form['taxonomy'][$similar->vid][$similar->tid] = array(
109 '#attributes' => array('style' => 'margin-left: 1.5em;'),
110 '#default_value' => 0,
111 '#title' => t('Use similar tag: %name (@matches).', array('%name' => $similar->name, '@matches' => format_plural($similar->count, '1 match', '@count matches'))),
112 '#type' => 'checkbox',
113 );
114 }
115 }
116 else {
117 $form['taxonomy'][$term->vid][$term->tid] = array(
118 '#value' => '<div style="margin-left: 1.5em;">'. t('There were no similar tags found for %name', array('%name' => $term->name)) .'</div>',
119 );
120 }
121 }
122 }
123
124 $form['submit'] = array(
125 '#type' => 'submit',
126 '#value' => t('Submit'),
127 );
128
129 $form['#submit']['taxonomy_similar_overview_submit'] = array($nid);
130 return $form;
131 }
132
133 /**
134 * Submit callback; saves term associations and redirects back to node view.
135 */
136 function taxonomy_similar_overview_submit($form_id, &$form_values, $nid) {
137 taxonomy_similar_node_save($nid, $form_values['taxonomy']);
138 return "node/$nid";
139 }
140
141 /**
142 * A wrapper around taxonomy_node_save, which allows us to handle bum data
143 * (keys of 0) as well as potential future support for automatic saving to
144 * term_relations or merging one term with another.
145 */
146 function taxonomy_similar_node_save($nid, $form_terms) {
147 if (!$nid) { return; }
148 $final_terms = array();
149
150 // remove keys that has a value of 0. these are
151 // created by the zeroing out of checkbox items.
152 foreach ($form_terms as $vid => $tids) {
153 foreach ($tids as $tid => $value) {
154 if ($form_terms[$vid][$tid]) {
155 $final_terms[$vid][] = $tid;
156 }
157 }
158 }
159
160 // load in non-free tagging terms associated with
161 // this $nid. if we don't, they'll all get deleted
162 // once our new $terms reaches taxonomy_node_save.
163 $nonfree_terms = taxonomy_similar_node_get_nonfree_terms($nid);
164 $final_terms = $final_terms + $nonfree_terms;
165
166 taxonomy_node_save($nid, $final_terms);
167 }
168
169 /**
170 * Determines a list of terms similar to the one passed.
171 * $tags/$tag: all terms in the database for a vocabulary.
172 * $term: the passed term object to find similarities of.
173 */
174 function taxonomy_similar_tags($term, $percentage = 54) {
175 static $tags;
176
177 // cache the result of our "all keywords in vid".
178 if (!isset($tags[$term->vid])) {
179 $tags[$term->vid] = array();
180 $result = db_query('SELECT t.* FROM {term_data} t WHERE t.vid = %d ORDER BY name', $term->vid);
181 while ($tag = db_fetch_object($result)) {
182 $tags[$term->vid][] = $tag;
183 }
184 }
185
186 // look for similars.
187 $similars = array();
188 foreach ($tags[$term->vid] as $tag) {
189 if ($tag->tid == $term->tid) { continue; }
190
191 // create some percentage and soundalike testers.
192 similar_text(strtoupper($term->name), strtoupper($tag->name), $similar_percentage);
193 $tag_phone = metaphone($tag->name); $term_phone = metaphone($term->name);
194
195 // if our term has the same sound as this tag, or percentage
196 // for similar text is good, add to array for later returning.
197 if (preg_match ("/$term_phone/", $tag_phone) && $similar_percentage >= $percentage) {
198 $tag->percentage = $similar_percentage;
199 $tag->count = taxonomy_similar_term_count_nodes($tag->tid);
200 $similars[] = $tag;
201 }
202 }
203
204 // sort similars by their percentage.
205 usort($similars, "_sort_by_percentage");
206
207 return $similars;
208 }
209
210 /**
211 * A rewrite of taxonomy_term_count_nodes, because we don't need the call to
212 * taxonomy_term_children to get the count of a term's children. Also, the
213 * core function loads counts for every term in the database, which can be
214 * really prohibitive for very large tagging vocabularies.
215 */
216 function taxonomy_similar_term_count_nodes($tid) {
217 static $count;
218
219 if (!isset($count[$tid])) {
220 $result = db_fetch_object(db_query('SELECT COUNT(n.nid) AS count FROM {term_node} t INNER JOIN {node} n ON t.nid = n.nid WHERE n.status = 1 AND t.tid = %d GROUP BY t.tid', $tid));
221 $count[$tid] = $result->count ? $result->count : 0;
222 }
223
224 return $count[$tid];
225 }
226
227 /**
228 * This is a slight rewrite of taxonomy_node_get_terms, with the primary
229 * difference being we'll return terms that are NOT in any free tagging
230 * vocabularies. This function is called before we do any saving - if
231 * we didn't, all these nonfree terms would be accidentally deleted.
232 */
233 function taxonomy_similar_node_get_nonfree_terms($nid) {
234 static $terms;
235
236 if (!isset($terms[$nid])) {
237 $result = db_query('SELECT t.* FROM {term_data} t, {term_node} r, {vocabulary} v WHERE r.tid = t.tid AND r.nid = %d AND t.vid = v.vid AND v.tags = 0', $nid);
238 $terms[$nid] = array();
239 while ($term = db_fetch_object($result)) {
240 $terms[$nid][$term->vid][] = $term->tid;
241 }
242 }
243
244 return $terms[$nid];
245 }
246
247 /**
248 * Sorts terms by their percentage, via usort().
249 */
250 function _sort_by_percentage($a, $b) {
251 if ($a->percentage == $b->percentage) { return 0; }
252 return ($a->percentage > $b->percentage) ? -1 : 1;
253 }

  ViewVC Help
Powered by ViewVC 1.1.2