1 | <?php |
---|
2 | |
---|
3 | /** |
---|
4 | * @file |
---|
5 | * Defines simple link field types. |
---|
6 | */ |
---|
7 | |
---|
8 | define('LINK_EXTERNAL', 'external'); |
---|
9 | define('LINK_INTERNAL', 'internal'); |
---|
10 | define('LINK_FRONT', 'front'); |
---|
11 | define('LINK_EMAIL', 'email'); |
---|
12 | define('LINK_NEWS', 'news'); |
---|
13 | define('LINK_DOMAINS', 'aero|arpa|asia|biz|com|cat|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|mobi|local'); |
---|
14 | // There are many other characters which are legal other than simply a-z - this includes them. |
---|
15 | // html_entity_decode() is buggy in php 4 - we'll put it back here for D7 when 5.x is assumed. |
---|
16 | /*define('LINK_ICHARS', (string) html_entity_decode(implode("", array( |
---|
17 | "æ", // Ê |
---|
18 | "Æ", // Ã |
---|
19 | "ø", // Þ |
---|
20 | "Ø", // Ã |
---|
21 | "å", // Ã¥ |
---|
22 | "Å", // Ã
|
---|
23 | "ä", // À |
---|
24 | "Ä", // Ã |
---|
25 | "ö", // ö |
---|
26 | "Ö", // Ã |
---|
27 | "ü", // ÃŒ |
---|
28 | "Ü", // Ã |
---|
29 | )), ENT_QUOTES, 'UTF-8'));*/ |
---|
30 | |
---|
31 | define('LINK_TARGET_DEFAULT', 'default'); |
---|
32 | define('LINK_TARGET_NEW_WINDOW', '_blank'); |
---|
33 | define('LINK_TARGET_TOP', '_top'); |
---|
34 | define('LINK_TARGET_USER', 'user'); |
---|
35 | |
---|
36 | /** |
---|
37 | * Maximum URLs length. |
---|
38 | */ |
---|
39 | define('LINK_URL_MAX_LENGTH', 2048); |
---|
40 | |
---|
41 | /** |
---|
42 | * Implementation of hook_field_info(). |
---|
43 | */ |
---|
44 | function link_field_info() { |
---|
45 | return array( |
---|
46 | 'link' => array( |
---|
47 | 'label' => t('Link'), |
---|
48 | 'description' => t('Store a title, href, and attributes in the database to assemble a link.'), |
---|
49 | ), |
---|
50 | ); |
---|
51 | } |
---|
52 | |
---|
53 | /** |
---|
54 | * Implementation of hook_field_settings(). |
---|
55 | */ |
---|
56 | function link_field_settings($op, $field) { |
---|
57 | switch ($op) { |
---|
58 | case 'form': |
---|
59 | $form = array( |
---|
60 | '#theme' => 'link_field_settings', |
---|
61 | ); |
---|
62 | |
---|
63 | $form['validate_url'] = array( |
---|
64 | '#type' => 'checkbox', |
---|
65 | '#title' => t('Validate URL'), |
---|
66 | '#default_value' => isset($field['validate_url']) && ($field['validate_url'] !== '') ? $field['validate_url'] : TRUE, |
---|
67 | '#description' => t('If checked, the URL field will be verified as a valid URL during validation.'), |
---|
68 | ); |
---|
69 | |
---|
70 | $form['url'] = array( |
---|
71 | '#type' => 'checkbox', |
---|
72 | '#title' => t('Optional URL'), |
---|
73 | '#default_value' => $field['url'], |
---|
74 | '#return_value' => 'optional', |
---|
75 | '#description' => t('If checked, the URL field is optional and submitting a title alone will be acceptable. If the URL is omitted, the title will be displayed as plain text.'), |
---|
76 | ); |
---|
77 | |
---|
78 | $title_options = array( |
---|
79 | 'optional' => t('Optional Title'), |
---|
80 | 'required' => t('Required Title'), |
---|
81 | 'value' => t('Static Title: '), |
---|
82 | 'none' => t('No Title'), |
---|
83 | ); |
---|
84 | |
---|
85 | $form['title'] = array( |
---|
86 | '#type' => 'radios', |
---|
87 | '#title' => t('Link Title'), |
---|
88 | '#default_value' => isset($field['title']) && ($field['title'] !== '') ? $field['title'] : 'optional', |
---|
89 | '#options' => $title_options, |
---|
90 | '#description' => t('If the link title is optional or required, a field will be displayed to the end user. If the link title is static, the link will always use the same title. If <a href="http://drupal.org/project/token">token module</a> is installed, the static title value may use any other node field as its value. Static and token-based titles may include most inline XHTML tags such as <em>strong</em>, <em>em</em>, <em>img</em>, <em>span</em>, etc.'), |
---|
91 | ); |
---|
92 | |
---|
93 | $form['title_value'] = array( |
---|
94 | '#type' => 'textfield', |
---|
95 | '#default_value' => $field['title_value'], |
---|
96 | '#size' => '46', |
---|
97 | ); |
---|
98 | |
---|
99 | $form['title_value_visibility'] = array( |
---|
100 | '#type' => 'checkbox', |
---|
101 | '#title' => t('Hide title if URL is empty'), |
---|
102 | '#default_value' => isset($field['title_value_visibility']) ? $field['title_value_visibility'] : 0, |
---|
103 | '#description' => t('Checking will hide the title when a URL is empty, leave un-checked to always display the title.'), |
---|
104 | ); |
---|
105 | |
---|
106 | // Add token module replacements if available |
---|
107 | if (module_exists('token')) { |
---|
108 | $form['tokens'] = array( |
---|
109 | '#type' => 'fieldset', |
---|
110 | '#collapsible' => TRUE, |
---|
111 | '#collapsed' => TRUE, |
---|
112 | '#title' => t('Placeholder tokens'), |
---|
113 | '#description' => t("The following placeholder tokens can be used in both paths and titles. When used in a path or title, they will be replaced with the appropriate values."), |
---|
114 | ); |
---|
115 | $form['tokens']['help'] = array( |
---|
116 | '#value' => theme('token_help', 'node'), |
---|
117 | ); |
---|
118 | |
---|
119 | $form['enable_tokens'] = array( |
---|
120 | '#type' => 'checkbox', |
---|
121 | '#title' => t('Allow user-entered tokens'), |
---|
122 | '#default_value' => isset($field['enable_tokens']) ? $field['enable_tokens'] : 1, |
---|
123 | '#description' => t('Checking will allow users to enter tokens in URLs and Titles on the node edit form. This does not affect the field settings on this page.'), |
---|
124 | ); |
---|
125 | } |
---|
126 | |
---|
127 | $form['display'] = array( |
---|
128 | '#tree' => TRUE, |
---|
129 | ); |
---|
130 | $form['display']['url_cutoff'] = array( |
---|
131 | '#type' => 'textfield', |
---|
132 | '#title' => t('URL Display Cutoff'), |
---|
133 | '#default_value' => isset($field['display']['url_cutoff']) ? $field['display']['url_cutoff'] : '80', |
---|
134 | '#description' => t('If the user does not include a title for this link, the URL will be used as the title. When should the link title be trimmed and finished with an elipsis (…)? Leave blank for no limit.'), |
---|
135 | '#maxlength' => 3, |
---|
136 | '#size' => 3, |
---|
137 | ); |
---|
138 | |
---|
139 | $target_options = array( |
---|
140 | LINK_TARGET_DEFAULT => t('Default (no target attribute)'), |
---|
141 | LINK_TARGET_TOP => t('Open link in window root'), |
---|
142 | LINK_TARGET_NEW_WINDOW => t('Open link in new window'), |
---|
143 | LINK_TARGET_USER => t('Allow the user to choose'), |
---|
144 | ); |
---|
145 | $form['attributes'] = array( |
---|
146 | '#tree' => TRUE, |
---|
147 | ); |
---|
148 | $form['attributes']['target'] = array( |
---|
149 | '#type' => 'radios', |
---|
150 | '#title' => t('Link Target'), |
---|
151 | '#default_value' => empty($field['attributes']['target']) ? LINK_TARGET_DEFAULT : $field['attributes']['target'], |
---|
152 | '#options' => $target_options, |
---|
153 | ); |
---|
154 | $form['attributes']['rel'] = array( |
---|
155 | '#type' => 'textfield', |
---|
156 | '#title' => t('Rel Attribute'), |
---|
157 | '#description' => t('When output, this link will have this rel attribute. The most common usage is <a href="http://en.wikipedia.org/wiki/Nofollow">rel="nofollow"</a> which prevents some search engines from spidering entered links.'), |
---|
158 | '#default_value' => empty($field['attributes']['rel']) ? '' : $field['attributes']['rel'], |
---|
159 | '#field_prefix' => 'rel = "', |
---|
160 | '#field_suffix' => '"', |
---|
161 | '#size' => 20, |
---|
162 | ); |
---|
163 | $form['attributes']['class'] = array( |
---|
164 | '#type' => 'textfield', |
---|
165 | '#title' => t('Additional CSS Class'), |
---|
166 | '#description' => t('When output, this link will have this class attribute. Multiple classes should be separated by spaces.'), |
---|
167 | '#default_value' => empty($field['attributes']['class']) ? '' : $field['attributes']['class'], |
---|
168 | ); |
---|
169 | $form['attributes']['title'] = array( |
---|
170 | '#title' => t("Link 'title' Attribute"), |
---|
171 | '#type' => 'textfield', |
---|
172 | '#field_prefix' => 'title = "', |
---|
173 | '#field_suffix' => '"', |
---|
174 | '#description' => t('When output, links will use this "title" attribute (when different from the link text). Read <a href="http://www.w3.org/TR/WCAG10-HTML-TECHS/#links">WCAG 1.0 Guidelines</a> for links comformances. Tokens values will be evaluated.'), |
---|
175 | '#default_value' => empty($field['attributes']['title']) ? '' : $field['attributes']['title'], |
---|
176 | ); |
---|
177 | return $form; |
---|
178 | |
---|
179 | case 'validate': |
---|
180 | if ($field['title'] == 'value' && empty($field['title_value'])) { |
---|
181 | form_set_error('title_value', t('A default title must be provided if the title is a static value')); |
---|
182 | } |
---|
183 | break; |
---|
184 | |
---|
185 | case 'save': |
---|
186 | return array('attributes', 'display', 'url', 'title', 'title_value', 'title_value_visibility', 'enable_tokens', 'validate_url'); |
---|
187 | |
---|
188 | case 'database columns': |
---|
189 | return array( |
---|
190 | 'url' => array('type' => 'varchar', 'length' => LINK_URL_MAX_LENGTH, 'not null' => FALSE, 'sortable' => TRUE), |
---|
191 | 'title' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE), |
---|
192 | 'attributes' => array('type' => 'text', 'size' => 'medium', 'not null' => FALSE), |
---|
193 | ); |
---|
194 | |
---|
195 | case 'views data': |
---|
196 | module_load_include('inc', 'link', 'views/link.views'); |
---|
197 | return link_views_content_field_data($field); |
---|
198 | } |
---|
199 | } |
---|
200 | |
---|
201 | /** |
---|
202 | * Implementation of hook_content_is_empty(). |
---|
203 | */ |
---|
204 | function link_content_is_empty($item, $field) { |
---|
205 | if (empty($item['title']) && empty($item['url'])) { |
---|
206 | return TRUE; |
---|
207 | } |
---|
208 | return FALSE; |
---|
209 | } |
---|
210 | |
---|
211 | /** |
---|
212 | * Implementation of hook_field(). |
---|
213 | */ |
---|
214 | function link_field($op, &$node, $field, &$items, $teaser, $page) { |
---|
215 | module_load_include('inc', 'link'); |
---|
216 | switch ($op) { |
---|
217 | case 'load': |
---|
218 | return _link_load($field, $items); |
---|
219 | |
---|
220 | case 'validate': |
---|
221 | $optional_field_found = FALSE; |
---|
222 | if ($field['validate_url'] !== 0 || is_null($field['validate_url']) || !isset($field['validate_url'])) { |
---|
223 | foreach ($items as $delta => $value) { |
---|
224 | _link_validate($items[$delta], $delta, $field, $node, $optional_field_found); |
---|
225 | } |
---|
226 | } |
---|
227 | |
---|
228 | if ($field['url'] === 'optional' && $field['title'] === 'optional' && $field['required'] && !$optional_field_found) { |
---|
229 | form_set_error($field['field_name'] .'][0][title', t('At least one title or URL must be entered.')); |
---|
230 | } |
---|
231 | break; |
---|
232 | |
---|
233 | case 'presave': |
---|
234 | case 'update': |
---|
235 | foreach ($items as $delta => $value) { |
---|
236 | _link_process($items[$delta], $delta, $field, $node); |
---|
237 | } |
---|
238 | break; |
---|
239 | |
---|
240 | case 'sanitize': |
---|
241 | foreach ($items as $delta => $value) { |
---|
242 | _link_sanitize($items[$delta], $delta, $field, $node); |
---|
243 | } |
---|
244 | break; |
---|
245 | } |
---|
246 | } |
---|
247 | |
---|
248 | /** |
---|
249 | * Implementation of hook_widget_info(). |
---|
250 | */ |
---|
251 | function link_widget_info() { |
---|
252 | return array( |
---|
253 | 'link' => array( |
---|
254 | 'label' => 'Link', |
---|
255 | 'field types' => array('link'), |
---|
256 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
257 | ), |
---|
258 | ); |
---|
259 | } |
---|
260 | |
---|
261 | /** |
---|
262 | * Implementation of hook_widget(). |
---|
263 | */ |
---|
264 | function link_widget(&$form, &$form_state, $field, $items, $delta = 0) { |
---|
265 | $element = array( |
---|
266 | '#type' => $field['widget']['type'], |
---|
267 | '#default_value' => isset($items[$delta]) ? $items[$delta] : '', |
---|
268 | '#title' => $field['widget']['label'], |
---|
269 | '#weight' => $field['widget']['weight'], |
---|
270 | '#description' => $field['widget']['description'], |
---|
271 | '#required' => $field['required'], |
---|
272 | '#field' => $field, |
---|
273 | ); |
---|
274 | return $element; |
---|
275 | } |
---|
276 | |
---|
277 | /** |
---|
278 | * Implementation of hook_theme(). |
---|
279 | */ |
---|
280 | function link_theme() { |
---|
281 | return array( |
---|
282 | 'link_field_settings' => array( |
---|
283 | 'arguments' => array('element' => NULL), |
---|
284 | 'file' => 'link.theme.inc', |
---|
285 | ), |
---|
286 | 'link_formatter_default' => array( |
---|
287 | 'arguments' => array('element' => NULL), |
---|
288 | 'file' => 'link.theme.inc', |
---|
289 | ), |
---|
290 | 'link_formatter_plain' => array( |
---|
291 | 'arguments' => array('element' => NULL), |
---|
292 | 'file' => 'link.theme.inc', |
---|
293 | ), |
---|
294 | 'link_formatter_absolute' => array( |
---|
295 | 'arguments' => array('element' => NULL), |
---|
296 | 'file' => 'link.theme.inc', |
---|
297 | ), |
---|
298 | 'link_formatter_title_plain' => array( |
---|
299 | 'arguments' => array('element' => NULL), |
---|
300 | 'file' => 'link.theme.inc', |
---|
301 | ), |
---|
302 | 'link_formatter_url' => array( |
---|
303 | 'arguments' => array('element' => NULL), |
---|
304 | 'file' => 'link.theme.inc', |
---|
305 | ), |
---|
306 | 'link_formatter_short' => array( |
---|
307 | 'arguments' => array('element' => NULL), |
---|
308 | 'file' => 'link.theme.inc', |
---|
309 | ), |
---|
310 | 'link_formatter_label' => array( |
---|
311 | 'arguments' => array('element' => NULL), |
---|
312 | 'file' => 'link.theme.inc', |
---|
313 | ), |
---|
314 | 'link_formatter_separate' => array( |
---|
315 | 'arguments' => array('element' => NULL), |
---|
316 | 'file' => 'link.theme.inc', |
---|
317 | ), |
---|
318 | 'link' => array( |
---|
319 | 'arguments' => array('element' => NULL), |
---|
320 | 'file' => 'link.theme.inc', |
---|
321 | ), |
---|
322 | ); |
---|
323 | } |
---|
324 | |
---|
325 | /** |
---|
326 | * Implementation of hook_elements(). |
---|
327 | */ |
---|
328 | function link_elements() { |
---|
329 | $elements = array(); |
---|
330 | $elements['link'] = array( |
---|
331 | '#input' => TRUE, |
---|
332 | '#process' => array('link_process'), |
---|
333 | ); |
---|
334 | return $elements; |
---|
335 | } |
---|
336 | |
---|
337 | /** |
---|
338 | * Process the link type element before displaying the field. |
---|
339 | * |
---|
340 | * Build the form element. When creating a form using FAPI #process, |
---|
341 | * note that $element['#value'] is already set. |
---|
342 | * |
---|
343 | * The $fields array is in $form['#field_info'][$element['#field_name']]. |
---|
344 | */ |
---|
345 | function link_process($element, $edit, $form_state, $form) { |
---|
346 | module_load_include('inc', 'link'); |
---|
347 | $field = $form['#field_info'][$element['#field_name']]; |
---|
348 | $delta = $element['#delta']; |
---|
349 | $element['url'] = array( |
---|
350 | '#type' => 'textfield', |
---|
351 | '#maxlength' => LINK_URL_MAX_LENGTH, |
---|
352 | '#title' => t('URL'), |
---|
353 | '#description' => $element['#description'], |
---|
354 | '#required' => ($delta == 0 && $field['url'] !== 'optional') ? $element['#required'] : FALSE, |
---|
355 | '#default_value' => isset($element['#value']['url']) ? $element['#value']['url'] : NULL, |
---|
356 | ); |
---|
357 | if ($field['title'] != 'none' && $field['title'] != 'value') { |
---|
358 | $element['title'] = array( |
---|
359 | '#type' => 'textfield', |
---|
360 | '#maxlength' => '255', |
---|
361 | '#title' => t('Title'), |
---|
362 | '#required' => ($delta == 0 && $field['title'] == 'required') ? $field['required'] : FALSE, |
---|
363 | '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL, |
---|
364 | ); |
---|
365 | } |
---|
366 | |
---|
367 | // Initialize field attributes as an array if it is not an array yet. |
---|
368 | if (!is_array($field['attributes'])) { |
---|
369 | $field['attributes'] = array(); |
---|
370 | } |
---|
371 | // Add default atrributes. |
---|
372 | $field['attributes'] += _link_default_attributes(); |
---|
373 | $attributes = isset($element['#value']['attributes']) ? $element['#value']['attributes'] : $field['attributes']; |
---|
374 | if (!empty($field['attributes']['target']) && $field['attributes']['target'] == LINK_TARGET_USER) { |
---|
375 | $element['attributes']['target'] = array( |
---|
376 | '#type' => 'checkbox', |
---|
377 | '#title' => t('Open URL in a New Window'), |
---|
378 | '#return_value' => LINK_TARGET_NEW_WINDOW, |
---|
379 | '#default_value' => $attributes['target'], |
---|
380 | ); |
---|
381 | } |
---|
382 | return $element; |
---|
383 | } |
---|
384 | |
---|
385 | /** |
---|
386 | * Implementation of hook_field_formatter_info(). |
---|
387 | */ |
---|
388 | function link_field_formatter_info() { |
---|
389 | return array( |
---|
390 | 'default' => array( |
---|
391 | 'label' => t('Title, as link (default)'), |
---|
392 | 'field types' => array('link'), |
---|
393 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
394 | ), |
---|
395 | 'title_plain' => array( |
---|
396 | 'label' => t('Title, as plain text'), |
---|
397 | 'field types' => array('link'), |
---|
398 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
399 | ), |
---|
400 | 'url' => array( |
---|
401 | 'label' => t('URL, as link'), |
---|
402 | 'field types' => array('link'), |
---|
403 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
404 | ), |
---|
405 | 'plain' => array( |
---|
406 | 'label' => t('URL, as plain text'), |
---|
407 | 'field types' => array('link'), |
---|
408 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
409 | ), |
---|
410 | 'absolute' => array( |
---|
411 | 'label' => t('URL, as absolute URL'), |
---|
412 | 'field types' => array('link'), |
---|
413 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
414 | ), |
---|
415 | 'short' => array( |
---|
416 | 'label' => t('Short, as link with title "Link"'), |
---|
417 | 'field types' => array('link'), |
---|
418 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
419 | ), |
---|
420 | 'label' => array( |
---|
421 | 'label' => t('Label, as link with label as title'), |
---|
422 | 'field types' => array('link'), |
---|
423 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
424 | ), |
---|
425 | 'separate' => array( |
---|
426 | 'label' => t('Separate title and URL'), |
---|
427 | 'field types' => array('link'), |
---|
428 | 'multiple values' => CONTENT_HANDLE_CORE, |
---|
429 | ), |
---|
430 | ); |
---|
431 | } |
---|
432 | |
---|
433 | function link_token_list($type = 'all') { |
---|
434 | if ($type == 'field' || $type == 'all') { |
---|
435 | $tokens = array(); |
---|
436 | |
---|
437 | $tokens['link']['url'] = t("Link URL"); |
---|
438 | $tokens['link']['title'] = t("Link title"); |
---|
439 | $tokens['link']['view'] = t("Formatted html link"); |
---|
440 | $tokens['link']['host'] = t("Link host"); |
---|
441 | |
---|
442 | return $tokens; |
---|
443 | } |
---|
444 | } |
---|
445 | |
---|
446 | function link_token_values($type, $object = NULL) { |
---|
447 | if ($type == 'field') { |
---|
448 | $item = $object[0]; |
---|
449 | |
---|
450 | $tokens['url'] = $item['url']; |
---|
451 | $tokens['title'] = $item['title']; |
---|
452 | $tokens['view'] = isset($item['view']) ? $item['view'] : ''; |
---|
453 | $host = @parse_url($item['url']); |
---|
454 | $tokens['host'] = isset($host['host']) ? $host['host'] : ''; |
---|
455 | |
---|
456 | return $tokens; |
---|
457 | } |
---|
458 | } |
---|
459 | |
---|
460 | /** |
---|
461 | * Implementation of hook_views_api(). |
---|
462 | */ |
---|
463 | function link_views_api() { |
---|
464 | return array( |
---|
465 | 'api' => 2, |
---|
466 | 'path' => drupal_get_path('module', 'link') .'/views', |
---|
467 | ); |
---|
468 | } |
---|
469 | /** |
---|
470 | * Implements hook_migrate_api(). |
---|
471 | */ |
---|
472 | function link_migrate_api() { |
---|
473 | return array('api' => 2); |
---|
474 | } |
---|
475 | |
---|
476 | /** |
---|
477 | * Implements hook_content_generate(). |
---|
478 | */ |
---|
479 | function link_content_generate($node, $field) { |
---|
480 | if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_MODULE) { |
---|
481 | return content_devel_multiple('_link_content_generate', $node, $field); |
---|
482 | } |
---|
483 | else { |
---|
484 | return _link_content_generate($node, $field); |
---|
485 | } |
---|
486 | } |
---|
487 | |
---|
488 | /** |
---|
489 | * Generates random link data for devel generate. |
---|
490 | */ |
---|
491 | function _link_content_generate($node, $field) { |
---|
492 | // Have to use title = true to get only one word, but then want to force it to lowercase. |
---|
493 | $url = url('http://www.example.com/' . drupal_strtolower(devel_create_greeking(1, TRUE))); |
---|
494 | $title = devel_create_greeking(mt_rand(1, 3), TRUE); |
---|
495 | |
---|
496 | return array( |
---|
497 | 'url' => $url, |
---|
498 | 'title' => $title, |
---|
499 | 'attributes' => array(), |
---|
500 | ); |
---|
501 | } |
---|