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

Contents of /contributions/modules/freelinking/freelinking.module

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


Revision 1.33 - (show annotations) (download) (as text)
Thu Oct 4 15:19:53 2007 UTC (2 years, 1 month ago) by eafarris
Branch: MAIN
CVS Tags: DRUPAL-6--1-0, HEAD
Changes since 1.32: +46 -42 lines
File MIME type: text/x-php
Compatibility with Drupal 6.x
1 <?php
2 /* freelinking.module -- implements CamelCase and [[free links]] filter for Drupal
3 ea. Farris <eafarris@gmail.com>
4 portions based on code from crw: http://ninjafish.org/project/wiki
5
6 Drupal freelinking project: http://www.drupal.org/project/freelinking
7
8 $Id: freelinking.module,v 1.31.2.3 2007/02/17 12:47:39 eafarris Exp $
9 */
10
11 function freelinking_menu() {
12 $items['freelinking'] = array(
13 'title' => 'Freelinks',
14 'description' => 'A list of freelinks used on this site',
15 'page callback' => 'freelinking_page',
16 'access arguments' => array('access freelinking list'),
17 );
18 $items['admin/settings/freelinking'] = array(
19 'title' => 'Freelinking settings',
20 'description' => 'Configure wiki-style freelinking settings for node content',
21 'page callback' => 'drupal_get_form',
22 'page arguments' => array('freelinking_settings'),
23 'access arguments' => array('administer freelinking'),
24 );
25 return $items;
26 } // endfunction freelinking_menu
27
28 function freelinking_perm() {
29 return array('administer freelinking', 'access freelinking list');
30 }
31
32 function freelinking_page($thetitle = NULL) {
33 if ($thetitle) { // find the matching title
34 $freelink = _freelinking_make_link($thetitle);
35 drupal_goto($freelink['path'], isset($freelink['args']) ? $freelink['args'] : '');
36 }
37 else { // no title was passed -- show a list of wikiwords and status
38 $header = array('phrase', 'target');
39 $query = "SELECT phrase, path FROM {freelinking} ORDER BY phrase";
40 $result = db_query($query);
41 $i = 0;
42 while ($freelink = db_fetch_object($result)) { // looping through phrase, target pairs
43 $rows[$i][] = urldecode($freelink->phrase);
44 if (preg_match('/^(http|mailto|https|ftp):/', $freelink->path)) { // an absolute link
45 $rows[$i][] = '<a class="freelink" href="' . $freelink->path . '">' . $flpair->path . '</a>';
46 }
47 else { // a freelink
48 $fltargetnid = _freelinking_exists($freelink->phrase);
49 $freelink = _freelinking_make_link($freelink->phrase);
50 if ($fltargetnid) {
51 $link = l(t('see this content'), drupal_get_path_alias('node/' . $fltargetnid));
52 }
53 else { // content not found, show link to create
54 $link = '<a href=' . url($freelink['path'], $freelink['args']) . '>' . t('create this content') . '</a>';
55 }
56 $rows[$i][] = $link;
57 }
58 $i++;
59 }
60 if (isset($rows)) {
61 return theme('table', $header, $rows);
62 }
63 else {
64 drupal_set_message(t('There are no freelinks on this site.'));
65 return '';
66 }
67 }
68 } // endfunction freelinking_page
69
70 function freelinking_block($op = 'list', $delta = 0) {
71 switch ($op) {
72 case 'list':
73 $blocks[0]['info'] = t('Freelink targets that need to be created');
74 return $blocks;
75 break;
76 case 'configure':
77 $form['freelinking_block_options'] = array(
78 '#type' => 'fieldset',
79 '#title' => t('Freelinking Block Options')
80 );
81 $form['freelinking_block_options']['freelinking_blocktitle'] = array(
82 '#title' => t('Title of freelinks block'),
83 '#type' => 'textfield',
84 '#default_value' => variable_get('freelinking_blocktitle', t('Create This Content')),
85 '#size' => 30,
86 '#maxlength' => 60,
87 '#description' => t('Title of the block that shows freelinked phrases without content.')
88 );
89 for ($i = 5; $i <=30; $i=$i+5) {
90 $options[$i] = $i;
91 }
92 $form['freelinking_block_options']['freelinking_blocknum'] = array(
93 '#title' => t('Number of non-existing link phrases to show'),
94 '#type' => 'select',
95 '#options' => $options,
96 '#default_value' => variable_get('freelinking_blocknum', '10'),
97 '#description' => t('Number of phrases to show in the block.')
98 );
99 return $form;
100 break;
101
102 case 'view':
103 switch ($delta) {
104 case 0:
105 $query = 'SELECT * FROM {freelinking} WHERE path LIKE "%node/add%" ORDER BY RAND()';
106 $result = db_query($query);
107 $i = 0;
108 $content = '';
109 while ($freelink = db_fetch_object($result)) {
110 if ($i == variable_get('freelinking_blocknum', 10)) { // we're done
111 break;
112 }
113 $items[] = l(urldecode($freelink->phrase), $freelink->path, array(), $freelink->args);
114 $i++;
115 } // endwhile looping through flpairs
116 $block['subject'] = variable_get('freelinking_blocktitle', 'Create This Content');
117 $block['content'] = theme('item_list', $items);
118 return $block;
119 default:
120 break;
121 } // endswitch $delta
122 default:
123 break;
124 } // endswitch $op
125 } // endfunction freelinking_block
126
127 function freelinking_settings() {
128 $notfoundoptions = array(
129 'create only' => t('Only try to create content'),
130 'no access search' => t('Search for content if user can\'t create'),
131 'always search' => t('Always search for content'),
132 );
133 $form["freelinking_nodetype"] = array(
134 '#title' => t('Default for new content'),
135 '#type' => 'select',
136 '#options' => node_get_types('names'),
137 '#default_value' => variable_get("freelinking_nodetype", 'story'),
138 '#description' => t('Type of content that the freelinking filter will create when clicking on a freelink without a target.')
139 );
140 $form['freelinking_notfound'] = array(
141 '#title' => t('What to do if content not found'),
142 '#type' => 'select',
143 '#options' => $notfoundoptions,
144 '#default_value' => variable_get('freelinking_notfound', 'no access search'),
145 '#description' => t('What to do when clicking on a freelink without a target. Choose to always attempt to create the content, search if the user doesn\'t have permission to create (the default), or to always search. NOTE: search functions require search.module to be activated.'),
146 );
147
148 $form["freelinking_restriction"] = array(
149 '#title' => t('Restrict free links to this content type'),
150 '#type' => 'select',
151 '#options' => array_merge(array('none' => t('No restrictions')), node_get_types('names')),
152 '#default_value' => variable_get("freelinking_restriction", 'none'),
153 '#description' => t('If desired, you can restrict the freelinking title search to just content of this type. Note that if it is not the same as the "Default for new content," above, new freelinked content cannot be found.')
154 );
155 $form["freelinking_camelcase"] = array(
156 '#title' => t('Allow CamelCase linking'),
157 '#type' => 'checkbox',
158 '#default_value' => variable_get("freelinking_camelcase", 0) == 0 ? TRUE : FALSE,
159 '#description' => t('If desired, you can enable CamelCase linking')
160 );
161 $form["freelinking_onceonly"] = array(
162 '#title' => t('Only link first occurance'),
163 '#type' => 'checkbox',
164 '#default_value' => variable_get("freelinking_onceonly", 0) == 1 ? TRUE : FALSE,
165 '#description' => t('If desired you can only turn the first occurance of a freelink into a link. This can improve the appearance of content that includes a lot of the same CamelCase words.')
166 );
167
168 return system_settings_form($form);
169 }
170
171
172 function freelinking_filter($op, $delta = 0, $format = -1, $text = '') {
173 switch ($op) {
174 case 'list':
175 return (array(0 => t('freelinking filter')));
176 break;
177
178 case 'name':
179 return t('freelinking filter');
180 break;
181
182 case 'description':
183 return t('Enables freelinking between nodes with CamelCase or delimiters like [[ and ]].');
184 break;
185
186 case 'process':
187 return _freelinking_do_filtering($text, FALSE);
188 break;
189
190 case 'prepare':
191 return $text;
192 break;
193
194 } // endswitch $op
195 } // endfunction freelinking_filter
196
197 function freelinking_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
198 switch ($op) {
199 case 'update':
200 _freelinking_do_filtering($node->body, TRUE);
201 break;
202 case 'insert':
203 _freelinking_do_filtering($node->body, TRUE);
204 break;
205 } // endswitch $op
206 } // endfunction freelinking_nodeapi
207
208 function freelinking_filter_tips($delta, $format, $long = FALSE) {
209 if ($long) {
210 $output = 'Content in [[double square brackets]] will be linked to existing content with that title, or a page to create that content. ';
211 $output .= 'Links can contain an optional bar, "|". Content on the left of the bar is the target; to the right, the link shown. ';
212 $output .= 'Links to pages outside this site are allowed. They must start with one of the following: "http", "https", "ftp", or "mailto", and can exist either by themselves, or on the left of the bar. ';
213 $output .= 'Examples: ';
214 $ouptut .= '<ul>';
215 $output .= '<li>[[simple link]] - will go to the content titled "simple link" or a page to create that content.</li>';
216 $output .= '<li>[[this is the target|this is the source]] - will present "this is the source" as a link to "this is the target", or a page to create that content.</li>';
217 $output .= '<li>[[http://www.example.com|this is the source]] - will present "this is the source" as a link to http://www.example.com.</li>';
218 $output .= '<li>[[http://www.example.com]] - will present "http://www.example.com" as a link to http://www.example.com.</li>';
219 $output .= '</ul>';
220 if (variable_get('freelinking_camelcase', TRUE)) {
221 $output .= 'Content consisting of two or more capitalized words run together (aka "CamelCase") will be linked to existing content with that title, or a page to create that content.';
222 }
223 }
224 else { // short tips displayed in-line
225 $output = 'Link to content with [[some text]], where "some text" is the title of existing content or the title of a new piece of content to create. You can also link text to a different title by using [[link to this title|show this text]]. ';
226 $output .= 'Link to outside URLs with [[http://www.example.com|some text]], or even [[http://www.example.com]]. ';
227 if (variable_get('freelinking_camelcase', TRUE)) {
228 $output .= 'Link to existing or new content with CamelCaseWords.';
229 }
230 }
231 return t($output);
232 }
233
234 function freelinking_form_alter(&$form, $form_state, $form_id) {
235 if (isset($form['type'])) {
236 $type = $form['type']['#value'];
237 if (variable_get('freelinking_nodetype', 'story') == $type && $_GET['edit']) { // on the right node type, with GET data
238 $form['title']['#default_value'] = urldecode($_GET['edit']['title']); // prepopulate the title field
239 } // endif node type and data
240 } // on a node creation form
241 } // endfunction freelinking_form_alter
242
243
244 /*
245 * PRIVATE FUNCTIONS BELOW
246 *
247 * Please do not use these functions outside of freelinking.module, as they are
248 * subject to change without notice.
249 *
250 */
251
252 function _freelinking_do_filtering($text, $store = FALSE) {
253 $allowcamelcase = variable_get("freelinking_camelcase", TRUE);
254 $freelinkingregexp = '/\[\[.+]]/U'; // this finds [[links like this]], un-greedily
255 preg_match_all($freelinkingregexp, $text, $flmatches);
256 if ($allowcamelcase) {
257 $camelcaseregexp = '/\b([[:upper:]][[:lower:]]+){2,}\b/'; // this gets us close, but is not perfect. Example: ThisIsACamelCaseWord won't match (two caps in a row)
258 preg_match_all($camelcaseregexp, $text, $ccmatches);
259 $wikiwords = array_merge($ccmatches[0], $flmatches[0]);
260 }
261 else {
262 $wikiwords = $flmatches[0];
263 }
264 foreach (array_unique($wikiwords) as $wikiword) {
265 if (substr($wikiword, 0, 2) == '[[') { // if it's a freelink, the expressions are different
266 $phrase = substr($wikiword, 2, -2);
267 $freelink = $phrase;
268 $barpos = strpos($phrase, '|');
269 $pattern = '/\[\[' . preg_quote($phrase,'/') . ']]/';
270 if ($barpos) {
271 $freelink = substr($freelink, 0, $barpos);
272 $phrase = substr($phrase, $barpos + 1);
273 }
274 if (preg_match('/^(http|mailto|https|ftp):/', $freelink)) {
275 $replacement = '<a class="freelinking external" href="' . $freelink . '">' . $phrase . '</a>';
276 }
277 else {
278 $replacement = l($phrase, 'freelinking/' . rawurlencode($freelink), array('class' => 'freelinking'));
279 }
280 }
281
282 else if ($allowcamelcase) { // it's a CamelCase, expressions are a bit simpler
283 $pattern = '/\b' . $wikiword . '\b(?![^<]*>)/';
284 $phrase = $wikiword; // consistency for the db
285 $freelink = $wikiword; // also for the db
286 $replacement = l($wikiword, 'freelinking/' . urlencode($wikiword));
287 }
288 $text = preg_replace($pattern, $replacement, $text, variable_get("freelinking_onceonly", 0) ? 1 : -1);
289
290 if ($store) {
291 _freelinking_store($freelink, $replacement);
292 }
293 } // foreach wikiword
294 return $text;
295 } // endfunction _freelinking_do_filtering
296
297 function _freelinking_store($phrase, $path, $args=NULL) { // store freelinking pair in the db
298 $hash = md5($phrase . $path . $args);
299 $query = "SELECT hash FROM {freelinking} WHERE phrase = '%s'";
300 $result = db_query($query, $phrase);
301 $num_rows = FALSE;
302 while ($dbobj = db_fetch_object($result)) {
303 $num_rows = TRUE;
304 $dbhash = $dbobj;
305 }
306 if ( !$num_rows) { // not in the db
307 $query = "INSERT INTO {freelinking} (hash, phrase, path, args) VALUES ('%s', '%s', '%s', '%s')";
308 $result = db_query($query, $hash, $phrase, $path, $args);
309 } // endif row not found in table
310 else { // in the db, but does it match?
311 if ($dbhash->hash != $hash) { // hashes don't match, replace db entry with new values
312 $query = "UPDATE {freelinking} SET hash = '%s', path = '%s', args = '%s' WHERE phrase = '%s'";
313 $result = db_query($query, $hash, $path, $args, $phrase);
314 } // endif hashes don't match
315 } // endifelse row found
316 } // endfunction _freelinking_store
317
318 function _freelinking_exists($thetitle) { // helper function for freelinking_page
319 // looks through the db for nodes matching $title. Returns the nid if such a node exists, otherwise, returns 0
320 $title = urldecode($thetitle);
321 $query = "SELECT nid FROM {node} WHERE title = '%s'";
322 $noderestrict = variable_get('freelinking_restriction', 'none');
323 if ($noderestrict != 'none') { // need to add the where clause
324 $query .= " AND type = '%s'";
325 $result = db_query($query, $title, $noderestrict);
326 }
327 else { // no restriction. query is fine but db_query doesn't need the extra argument
328 $result = db_query($query, $title);
329 }
330 // FIXME ***
331 while ($node = db_fetch_object($result)) { // only one, I hope... what if there's more than one?
332 $nid = $node->nid;
333 }
334 return (empty($nid) ? 0 : $nid);
335 }
336
337
338 function _freelinking_make_link($thetitle) { // helper function for freelinking_page
339 global $user;
340 // Returns a link to a node named $thetitle if found, or a link to new content otherwise.
341 $nid = _freelinking_exists($thetitle);
342 if ($nid) { // the node exists, set the path to go there
343 $freelink['path'] = 'node/' . $nid;
344 }
345 else { // node doesn't exist, set path to create it
346 switch (variable_get('freelinking_notfound', 'no access search')) {
347 case 'create only':
348 $freelink['path'] = 'node/add/' . variable_get('freelinking_nodetype', 'story');
349 $freelink['args'] = 'edit[title]=' . $thetitle;
350 break;
351 case 'no access search':
352 if (node_access('create', variable_get('freelinking_nodetype', 'story'))) {
353 $freelink['path'] = 'node/add/' . variable_get('freelinking_nodetype', 'story');
354 $freelink['args'] = 'edit[title]=' . $thetitle;
355 }
356 else {
357 $freelink['path'] = 'search/node/' . $thetitle;
358 }
359 break;
360 case 'always search':
361 $freelink['path'] = 'search/node/' . $thetitle;
362 break;
363 } // endswitch notfound options
364 }
365 _freelinking_store($thetitle, $freelink['path'], isset($freelink['args']) ? $freelink['args'] : '');
366 return $freelink;
367 } // endfunction _freelinking_make_link
368
369 // vim: tw=300 nowrap syn=php

  ViewVC Help
Powered by ViewVC 1.1.2