| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
1 <?php 2 // $Id: link.module,v 1.24.4.34 2010/06/14 18:14:11 jcfiala Exp $ 3 4 /** 5 * @file 6 * Defines simple link field types. 7 */ 8 9 define('LINK_EXTERNAL', 'external'); 10 define('LINK_INTERNAL', 'internal'); 11 define('LINK_FRONT', 'front'); 12 define('LINK_EMAIL', 'email'); 13 define('LINK_NEWS', 'news'); 14 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'); 15 // There are many other characters which are legal other than simply a-z - this includes them. 16 // html_entity_decode() is buggy in php 4 - we'll put it back here for D7 when 5.x is assumed. 17 /*define('LINK_ICHARS', (string) html_entity_decode(implode("", array( 18 "æ", // æ 19 "Æ", // Æ 20 "ø", // ø 21 "Ø", // Ø 22 "å", // å 23 "Å", // Å 24 "ä", // ä 25 "Ä", // Ä 26 "ö", // ö 27 "Ö", // Ö 28 "ü", // ü 29 "Ü", // Ü 30 )), ENT_QUOTES, 'UTF-8'));*/ 31 32 define('LINK_TARGET_DEFAULT', 'default'); 33 define('LINK_TARGET_NEW_WINDOW', '_blank'); 34 define('LINK_TARGET_TOP', '_top'); 35 define('LINK_TARGET_USER', 'user'); 36 37 /** 38 * Maximum URLs length. 39 */ 40 define('LINK_URL_MAX_LENGTH', 2048); 41 42 /** 43 * Implementation of hook_field_info(). 44 */ 45 function link_field_info() { 46 return array( 47 'link' => array( 48 'label' => t('Link'), 49 'description' => t('Store a title, href, and attributes in the database to assemble a link.'), 50 ), 51 ); 52 } 53 54 /** 55 * Implementation of hook_field_settings(). 56 */ 57 function link_field_settings($op, $field) { 58 switch ($op) { 59 case 'form': 60 $form = array( 61 '#theme' => 'link_field_settings', 62 ); 63 64 $form['validate_url'] = array( 65 '#type' => 'checkbox', 66 '#title' => t('Validate URL'), 67 '#default_value' => isset($field['validate_url']) && ($field['validate_url'] !== '') ? $field['validate_url'] : TRUE, 68 '#description' => t('If checked, the URL field will be verified as a valid URL during validation.'), 69 ); 70 71 $form['url'] = array( 72 '#type' => 'checkbox', 73 '#title' => t('Optional URL'), 74 '#default_value' => $field['url'], 75 '#return_value' => 'optional', 76 '#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.'), 77 ); 78 79 $title_options = array( 80 'optional' => t('Optional Title'), 81 'required' => t('Required Title'), 82 'value' => t('Static Title: '), 83 'none' => t('No Title'), 84 ); 85 86 $form['title'] = array( 87 '#type' => 'radios', 88 '#title' => t('Link Title'), 89 '#default_value' => isset($field['title']) && ($field['title'] !== '') ? $field['title'] : 'optional', 90 '#options' => $title_options, 91 '#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.'), 92 ); 93 94 $form['title_value'] = array( 95 '#type' => 'textfield', 96 '#default_value' => $field['title_value'], 97 '#size' => '46', 98 ); 99 100 // Add token module replacements if available 101 if (module_exists('token')) { 102 $form['tokens'] = array( 103 '#type' => 'fieldset', 104 '#collapsible' => TRUE, 105 '#collapsed' => TRUE, 106 '#title' => t('Placeholder tokens'), 107 '#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."), 108 ); 109 $form['tokens']['help'] = array( 110 '#value' => theme('token_help', 'node'), 111 ); 112 113 $form['enable_tokens'] = array( 114 '#type' => 'checkbox', 115 '#title' => t('Allow user-entered tokens'), 116 '#default_value' => isset($field['enable_tokens']) ? $field['enable_tokens'] : 1, 117 '#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.'), 118 ); 119 } 120 121 $form['display'] = array( 122 '#tree' => TRUE, 123 ); 124 $form['display']['url_cutoff'] = array( 125 '#type' => 'textfield', 126 '#title' => t('URL Display Cutoff'), 127 '#default_value' => isset($field['display']['url_cutoff']) ? $field['display']['url_cutoff'] : '80', 128 '#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.'), 129 '#maxlength' => 3, 130 '#size' => 3, 131 ); 132 133 $target_options = array( 134 LINK_TARGET_DEFAULT => t('Default (no target attribute)'), 135 LINK_TARGET_TOP => t('Open link in window root'), 136 LINK_TARGET_NEW_WINDOW => t('Open link in new window'), 137 LINK_TARGET_USER => t('Allow the user to choose'), 138 ); 139 $form['attributes'] = array( 140 '#tree' => TRUE, 141 ); 142 $form['attributes']['target'] = array( 143 '#type' => 'radios', 144 '#title' => t('Link Target'), 145 '#default_value' => empty($field['attributes']['target']) ? LINK_TARGET_DEFAULT : $field['attributes']['target'], 146 '#options' => $target_options, 147 ); 148 $form['attributes']['rel'] = array( 149 '#type' => 'textfield', 150 '#title' => t('Rel Attribute'), 151 '#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.'), 152 '#default_value' => empty($field['attributes']['rel']) ? '' : $field['attributes']['rel'], 153 '#field_prefix' => 'rel = "', 154 '#field_suffix' => '"', 155 '#size' => 20, 156 ); 157 $form['attributes']['class'] = array( 158 '#type' => 'textfield', 159 '#title' => t('Additional CSS Class'), 160 '#description' => t('When output, this link will have this class attribute. Multiple classes should be separated by spaces.'), 161 '#default_value' => empty($field['attributes']['class']) ? '' : $field['attributes']['class'], 162 ); 163 $form['attributes']['title'] = array( 164 '#title' => t("Link 'title' Attribute"), 165 '#type' => 'textfield', 166 '#field_prefix' => 'title = "', 167 '#field_suffix' => '"', 168 '#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.'), 169 '#default_value' => empty($field['attributes']['title']) ? '' : $field['attributes']['title'], 170 ); 171 return $form; 172 173 case 'validate': 174 if ($field['title'] == 'value' && empty($field['title_value'])) { 175 form_set_error('title_value', t('A default title must be provided if the title is a static value')); 176 } 177 break; 178 179 case 'save': 180 return array('attributes', 'display', 'url', 'title', 'title_value', 'enable_tokens', 'validate_url'); 181 182 case 'database columns': 183 return array( 184 'url' => array('type' => 'varchar', 'length' => LINK_URL_MAX_LENGTH, 'not null' => FALSE, 'sortable' => TRUE), 185 'title' => array('type' => 'varchar', 'length' => 255, 'not null' => FALSE, 'sortable' => TRUE), 186 'attributes' => array('type' => 'text', 'size' => 'medium', 'not null' => FALSE), 187 ); 188 189 case 'views data': 190 module_load_include('inc', 'link', 'views/link.views'); 191 return link_views_content_field_data($field); 192 } 193 } 194 195 /** 196 * Theme the settings form for the link field. 197 */ 198 function theme_link_field_settings($form) { 199 $title_value = drupal_render($form['title_value']); 200 $title_checkbox = drupal_render($form['title']['value']); 201 202 // Set Static Title radio option to include the title_value textfield. 203 $form['title']['value'] = array('#value' => '<div class="container-inline">'. $title_checkbox . $title_value .'</div>'); 204 205 // Reprint the title radio options with the included textfield. 206 return drupal_render($form); 207 } 208 209 /** 210 * Implementation of hook_content_is_empty(). 211 */ 212 function link_content_is_empty($item, $field) { 213 if (empty($item['title']) && empty($item['url'])) { 214 return TRUE; 215 } 216 return FALSE; 217 } 218 219 /** 220 * Implementation of hook_field(). 221 */ 222 function link_field($op, &$node, $field, &$items, $teaser, $page) { 223 switch ($op) { 224 case 'load': 225 return _link_load($field, $items); 226 227 case 'validate': 228 $optional_field_found = FALSE; 229 if ($field['validate_url'] !== 0 || is_null($field['validate_url']) || !isset($field['validate_url'])) { 230 foreach ($items as $delta => $value) { 231 _link_validate($items[$delta], $delta, $field, $node, $optional_field_found); 232 } 233 } 234 235 if ($field['url'] == 'optional' && $field['title'] == 'optional' && $field['required'] && !$optional_field_found) { 236 form_set_error($field['field_name'] .'][0][title', t('At least one title or URL must be entered.')); 237 } 238 break; 239 240 case 'presave': 241 foreach ($items as $delta => $value) { 242 _link_process($items[$delta], $delta, $field, $node); 243 } 244 break; 245 246 case 'sanitize': 247 foreach ($items as $delta => $value) { 248 _link_sanitize($items[$delta], $delta, $field, $node); 249 } 250 break; 251 } 252 } 253 254 /** 255 * Implementation of hook_widget_info(). 256 */ 257 function link_widget_info() { 258 return array( 259 'link' => array( 260 'label' => 'Link', 261 'field types' => array('link'), 262 'multiple values' => CONTENT_HANDLE_CORE, 263 ), 264 ); 265 } 266 267 /** 268 * Implementation of hook_widget(). 269 */ 270 function link_widget(&$form, &$form_state, $field, $items, $delta = 0) { 271 $element = array( 272 '#type' => $field['widget']['type'], 273 '#default_value' => isset($items[$delta]) ? $items[$delta] : '', 274 '#title' => $field['widget']['label'], 275 '#weight' => $field['widget']['weight'], 276 '#description' => $field['widget']['description'], 277 '#required' => $field['required'], 278 '#field' => $field, 279 ); 280 return $element; 281 } 282 283 function _link_load($field, &$items) { 284 foreach ($items as $delta => $item) { 285 // Unserialize the attributes array. 286 $items[$delta]['attributes'] = unserialize($item['attributes']); 287 } 288 return array($field['field_name'] => $items); 289 } 290 291 function _link_process(&$item, $delta = 0, $field, $node) { 292 // Trim whitespace from URL. 293 $item['url'] = trim($item['url']); 294 295 // if no attributes are set then make sure $item['attributes'] is an empty array - this lets $field['attributes'] override it. 296 if (empty($item['attributes'])) { 297 $item['attributes'] = array(); 298 } 299 300 // Serialize the attributes array. 301 $item['attributes'] = serialize($item['attributes']); 302 303 // Don't save an invalid default value (e.g. 'http://'). 304 if ((isset($field['widget']['default_value'][$delta]['url']) && $item['url'] == $field['widget']['default_value'][$delta]['url']) && is_object($node)) { 305 if (!link_validate_url($item['url'])) { 306 unset($item['url']); 307 } 308 } 309 } 310 311 function _link_validate(&$item, $delta, $field, $node, &$optional_field_found) { 312 if ($item['url'] && !(isset($field['widget']['default_value'][$delta]['url']) && $item['url'] == $field['widget']['default_value'][$delta]['url'] && !$field['required'])) { 313 // Validate the link. 314 if (link_validate_url(trim($item['url'])) == FALSE) { 315 form_set_error($field['field_name'] .']['. $delta .'][url', t('Not a valid URL.')); 316 } 317 // Require a title for the link if necessary. 318 if ($field['title'] == 'required' && strlen(trim($item['title'])) == 0) { 319 form_set_error($field['field_name'] .']['. $delta .'][title', t('Titles are required for all links.')); 320 } 321 } 322 // Require a link if we have a title. 323 if ($field['url'] !== 'optional' && strlen($item['title']) > 0 && strlen(trim($item['url'])) == 0) { 324 form_set_error($field['field_name'] .']['. $delta .'][url', t('You cannot enter a title without a link url.')); 325 } 326 // In a totally bizzaro case, where URLs and titles are optional but the field is required, ensure there is at least one link. 327 if ($field['url'] == 'optional' && $field['title'] == 'optional' && (strlen(trim($item['url'])) != 0 || strlen(trim($item['title'])) != 0)) { 328 $optional_field_found = TRUE; 329 } 330 } 331 332 /** 333 * Cleanup user-entered values for a link field according to field settings. 334 * 335 * @param $item 336 * A single link item, usually containing url, title, and attributes. 337 * @param $delta 338 * The delta value if this field is one of multiple fields. 339 * @param $field 340 * The CCK field definition. 341 * @param $node 342 * The node containing this link. 343 */ 344 function _link_sanitize(&$item, $delta, &$field, &$node) { 345 // Don't try to process empty links. 346 if (empty($item['url']) && empty($item['title'])) { 347 return; 348 } 349 350 // Replace URL tokens. 351 if (module_exists('token') && $field['enable_tokens']) { 352 // Load the node if necessary for nodes in views. 353 $token_node = isset($node->nid) ? node_load($node->nid) : $node; 354 $item['url'] = token_replace($item['url'], 'node', $token_node); 355 } 356 357 $type = link_validate_url($item['url']); 358 // If we can't determine the type of url, and we've been told not to validate it, 359 // then we assume it's a LINK_EXTERNAL type for later processing. #357604 360 if ($type == FALSE && $field['validate_url'] === 0) { 361 $type = LINK_EXTERNAL; 362 } 363 $url = link_cleanup_url($item['url']); 364 365 // Separate out the anchor if any. 366 if (strpos($url, '#') !== FALSE) { 367 $item['fragment'] = substr($url, strpos($url, '#') + 1); 368 $url = substr($url, 0, strpos($url, '#')); 369 } 370 // Separate out the query string if any. 371 if (strpos($url, '?') !== FALSE) { 372 $item['query'] = substr($url, strpos($url, '?') + 1); 373 $url = substr($url, 0, strpos($url, '?')); 374 } 375 // Save the new URL without the anchor or query. 376 if ($field['validate_url'] === 0) { 377 $item['url'] = check_plain($url); 378 } 379 else { 380 $item['url'] = $url; 381 } 382 383 // Create a shortened URL for display. 384 $display_url = $type == LINK_EMAIL ? str_replace('mailto:', '', $url) : url($url, array('query' => isset($item['query']) ? $item['query'] : NULL, 'fragment' => isset($item['fragment']) ? $item['fragment'] : NULL, 'absolute' => TRUE)); 385 if ($field['display']['url_cutoff'] && strlen($display_url) > $field['display']['url_cutoff']) { 386 $display_url = substr($display_url, 0, $field['display']['url_cutoff']) ."..."; 387 } 388 $item['display_url'] = $display_url; 389 390 // Use the title defined at the field level. 391 if ($field['title'] == 'value' && strlen(trim($field['title_value']))) { 392 $title = $field['title_value']; 393 } 394 // Use the title defined by the user at the widget level. 395 else { 396 $title = $item['title']; 397 } 398 // Replace tokens. - originally we only did it for value titles. 399 if ($field['enable_tokens'] && module_exists('token')) { 400 // Load the node if necessary for nodes in views. 401 $token_node = isset($node->nid) ? node_load($node->nid) : $node; 402 $title = filter_xss(token_replace($title, 'node', $token_node), array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); 403 $item['html'] = TRUE; 404 } 405 elseif ($field['title'] == 'value') { 406 $title = filter_xss($title, array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); 407 $item['html'] = TRUE; 408 } 409 $item['display_title'] = empty($title) ? $item['display_url'] : $title; 410 411 if (!isset($item['attributes'])) { 412 $item['attributes'] = array(); 413 } 414 415 // Unserialize attributtes array if it has not been unserialized yet. 416 if (!is_array($item['attributes'])) { 417 $item['attributes'] = (array)unserialize($item['attributes']); 418 } 419 420 // Add default attributes. Make sure that $field['attributes'] is an array. #626932 421 if (!is_array($field['attributes'])) { 422 $field['attributes'] = array(); 423 } 424 $field['attributes'] += _link_default_attributes(); 425 426 // Merge item attributes with attributes defined at the field level. 427 $item['attributes'] += $field['attributes']; 428 429 // If user is not allowed to choose target attribute, use default defined at 430 // field level. 431 if ($field['attributes']['target'] != LINK_TARGET_USER) { 432 $item['attributes']['target'] = $field['attributes']['target']; 433 } 434 435 // Remove the target attribute if the default (no target) is selected. 436 if (empty($item['attributes']) || $item['attributes']['target'] == LINK_TARGET_DEFAULT) { 437 unset($item['attributes']['target']); 438 } 439 440 // Remove the rel=nofollow for internal links. 441 if ($type != LINK_EXTERNAL && $type != LINK_NEWS && strpos($item['attributes']['rel'], 'nofollow') !== FALSE) { 442 $item['attributes']['rel'] = str_replace('nofollow', '', $item['attributes']['rel']); 443 } 444 // Some old field data may have $element['#item']['attributes']['rel'] as an array, which will cause errors: 445 if (is_array($element['#item']['attributes']['rel'])) { 446 unset($element['#item']['attributes']['rel']); 447 } 448 449 // Handle "title" link attribute 450 if ($field['enable_tokens'] && !empty($item['attributes']['title']) && module_exists('token')) { 451 // Load the node (necessary for nodes in views). 452 $token_node = isset($node->nid) ? node_load($node->nid) : $node; 453 $item['attributes']['title'] = token_replace($item['attributes']['title'], 'node', $token_node); 454 } 455 456 // Remove title attribute if it's equal to link text. 457 if ($item['attributes']['title'] == $item['display_title']) { 458 unset($item['attributes']['title']); 459 } 460 461 // Remove empty attributes. 462 $item['attributes'] = array_filter($item['attributes']); 463 464 // Add the widget label. 465 $item['label'] = $field['widget']['label']; 466 } 467 468 /** 469 * Implementation of hook_theme(). 470 */ 471 function link_theme() { 472 return array( 473 'link_field_settings' => array( 474 'arguments' => array('element' => NULL), 475 ), 476 'link_formatter_default' => array( 477 'arguments' => array('element' => NULL), 478 ), 479 'link_formatter_plain' => array( 480 'arguments' => array('element' => NULL), 481 ), 482 'link_formatter_title_plain' => array( 483 'arguments' => array('element' => NULL), 484 ), 485 'link_formatter_url' => array( 486 'arguments' => array('element' => NULL), 487 ), 488 'link_formatter_short' => array( 489 'arguments' => array('element' => NULL), 490 ), 491 'link_formatter_label' => array( 492 'arguments' => array('element' => NULL), 493 ), 494 'link_formatter_separate' => array( 495 'arguments' => array('element' => NULL), 496 ), 497 'link' => array( 498 'arguments' => array('element' => NULL), 499 ), 500 ); 501 } 502 503 /** 504 * FAPI theme for an individual text elements. 505 */ 506 function theme_link($element) { 507 drupal_add_css(drupal_get_path('module', 'link') .'/link.css'); 508 509 // Prefix single value link fields with the name of the field. 510 if (empty($element['#field']['multiple'])) { 511 if (isset($element['url']) && isset($element['title'])) { 512 $element['url']['#title'] = $element['#title'] .' '. $element['url']['#title']; 513 $element['title']['#title'] = $element['#title'] .' '. $element['title']['#title']; 514 } 515 elseif ($element['url']) { 516 $element['url']['#title'] = $element['#title']; 517 } 518 } 519 520 $output = ''; 521 $output .= '<div class="link-field-subrow clear-block">'; 522 if (isset($element['title'])) { 523 $output .= '<div class="link-field-title link-field-column">'. theme('textfield', $element['title']) .'</div>'; 524 } 525 $output .= '<div class="link-field-url'. (isset($element['title']) ? ' link-field-column' : '') .'">'. theme('textfield', $element['url']) .'</div>'; 526 $output .= '</div>'; 527 if (!empty($element['attributes']['target'])) { 528 $output .= '<div class="link-attributes">'. theme('checkbox', $element['attributes']['target']) .'</div>'; 529 } 530 return $output; 531 } 532 533 /** 534 * Implementation of hook_elements(). 535 */ 536 function link_elements() { 537 $elements = array(); 538 $elements['link'] = array( 539 '#input' => TRUE, 540 '#process' => array('link_process'), 541 ); 542 return $elements; 543 } 544 545 function _link_default_attributes() { 546 return array( 547 'title' => '', 548 'target' => LINK_TARGET_DEFAULT, 549 'class' => '', 550 'rel' => '', 551 ); 552 } 553 554 /** 555 * Process the link type element before displaying the field. 556 * 557 * Build the form element. When creating a form using FAPI #process, 558 * note that $element['#value'] is already set. 559 * 560 * The $fields array is in $form['#field_info'][$element['#field_name']]. 561 */ 562 function link_process($element, $edit, $form_state, $form) { 563 $field = $form['#field_info'][$element['#field_name']]; 564 $delta = $element['#delta']; 565 $element['url'] = array( 566 '#type' => 'textfield', 567 '#maxlength' => LINK_URL_MAX_LENGTH, 568 '#title' => t('URL'), 569 '#description' => $element['#description'], 570 '#required' => ($delta == 0 && $field['url'] !== 'optional') ? $element['#required'] : FALSE, 571 '#default_value' => isset($element['#value']['url']) ? $element['#value']['url'] : NULL, 572 ); 573 if ($field['title'] != 'none' && $field['title'] != 'value') { 574 $element['title'] = array( 575 '#type' => 'textfield', 576 '#maxlength' => '255', 577 '#title' => t('Title'), 578 '#required' => ($delta == 0 && $field['title'] == 'required') ? $field['required'] : FALSE, 579 '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL, 580 ); 581 } 582 583 // Initialize field attributes as an array if it is not an array yet. 584 if (!is_array($field['attributes'])) { 585 $field['attributes'] = array(); 586 } 587 // Add default atrributes. 588 $field['attributes'] += _link_default_attributes(); 589 $attributes = isset($element['#value']['attributes']) ? $element['#value']['attributes'] : $field['attributes']; 590 if (!empty($field['attributes']['target']) && $field['attributes']['target'] == LINK_TARGET_USER) { 591 $element['attributes']['target'] = array( 592 '#type' => 'checkbox', 593 '#title' => t('Open URL in a New Window'), 594 '#return_value' => LINK_TARGET_NEW_WINDOW, 595 '#default_value' => $attributes['target'], 596 ); 597 } 598 return $element; 599 } 600 601 /** 602 * Implementation of hook_field_formatter_info(). 603 */ 604 function link_field_formatter_info() { 605 return array( 606 'default' => array( 607 'label' => t('Title, as link (default)'), 608 'field types' => array('link'), 609 'multiple values' => CONTENT_HANDLE_CORE, 610 ), 611 'title_plain' => array( 612 'label' => t('Title, as plain text'), 613 'field types' => array('link'), 614 'multiple values' => CONTENT_HANDLE_CORE, 615 ), 616 'url' => array( 617 'label' => t('URL, as link'), 618 'field types' => array('link'), 619 'multiple values' => CONTENT_HANDLE_CORE, 620 ), 621 'plain' => array( 622 'label' => t('URL, as plain text'), 623 'field types' => array('link'), 624 'multiple values' => CONTENT_HANDLE_CORE, 625 ), 626 'short' => array( 627 'label' => t('Short, as link with title "Link"'), 628 'field types' => array('link'), 629 'multiple values' => CONTENT_HANDLE_CORE, 630 ), 631 'label' => array( 632 'label' => t('Label, as link with label as title'), 633 'field types' => array('link'), 634 'multiple values' => CONTENT_HANDLE_CORE, 635 ), 636 'separate' => array( 637 'label' => t('Separate title and URL'), 638 'field types' => array('link'), 639 'multiple values' => CONTENT_HANDLE_CORE, 640 ), 641 ); 642 } 643 644 /** 645 * Theme function for 'default' text field formatter. 646 */ 647 function theme_link_formatter_default($element) { 648 // Display a normal link if both title and URL are available. 649 if (!empty($element['#item']['display_title']) && !empty($element['#item']['url'])) { 650 return l($element['#item']['display_title'], $element['#item']['url'], $element['#item']); 651 } 652 // If only a title, display the title. 653 elseif (!empty($element['#item']['display_title'])) { 654 return check_plain($element['#item']['display_title']); 655 } 656 } 657 658 /** 659 * Theme function for 'plain' text field formatter. 660 */ 661 function theme_link_formatter_plain($element) { 662 return empty($element['#item']['url']) ? check_plain($element['#item']['title']) : url($element['#item']['url'], $element['#item']); 663 } 664 665 /** 666 * Theme function for 'title_plain' text field formatter. 667 */ 668 function theme_link_formatter_title_plain($element) { 669 return empty($element['#item']['title']) ? '' : check_plain($element['#item']['title']); 670 } 671 672 /** 673 * Theme function for 'url' text field formatter. 674 */ 675 function theme_link_formatter_url($element) { 676 return $element['#item']['url'] ? l($element['#item']['display_url'], $element['#item']['url'], $element['#item']) : ''; 677 } 678 679 /** 680 * Theme function for 'short' text field formatter. 681 */ 682 function theme_link_formatter_short($element) { 683 return $element['#item']['url'] ? l(t('Link'), $element['#item']['url'], $element['#item']) : ''; 684 } 685 686 /** 687 * Theme function for 'label' text field formatter. 688 */ 689 function theme_link_formatter_label($element) { 690 return $element['#item']['url'] ? l($element['#item']['label'], $element['#item']['url'], $element['#item']) : ''; 691 } 692 693 /** 694 * Theme function for 'separate' text field formatter. 695 */ 696 function theme_link_formatter_separate($element) { 697 $class = empty($element['#item']['attributes']['class']) ? '' : ' '. $element['#item']['attributes']['class']; 698 unset($element['#item']['attributes']['class']); 699 $title = empty($element['#item']['title']) ? '' : check_plain($element['#item']['title']); 700 701 $output = ''; 702 $output .= '<div class="link-item '. $class .'">'; 703 if (!empty($title)) { 704 $output .= '<div class="link-title">'. $title .'</div>'; 705 } 706 $output .= '<div class="link-url">'. l($element['#item']['display_url'], $element['#item']['url'], $element['#item']) .'</div>'; 707 $output .= '</div>'; 708 return $output; 709 } 710 711 function link_token_list($type = 'all') { 712 if ($type == 'field' || $type == 'all') { 713 $tokens = array(); 714 715 $tokens['link']['url'] = t("Link URL"); 716 $tokens['link']['title'] = t("Link title"); 717 $tokens['link']['view'] = t("Formatted html link"); 718 719 return $tokens; 720 } 721 } 722 723 function link_token_values($type, $object = NULL) { 724 if ($type == 'field') { 725 $item = $object[0]; 726 727 $tokens['url'] = $item['url']; 728 $tokens['title'] = $item['title']; 729 $tokens['view'] = isset($item['view']) ? $item['view'] : ''; 730 731 return $tokens; 732 } 733 } 734 735 /** 736 * Implementation of hook_views_api(). 737 */ 738 function link_views_api() { 739 return array( 740 'api' => 2, 741 'path' => drupal_get_path('module', 'link') .'/views', 742 ); 743 } 744 745 /** 746 * Forms a valid URL if possible from an entered address. 747 * Trims whitespace and automatically adds an http:// to addresses without a protocol specified 748 * 749 * @param string $url 750 * @param string $protocol The protocol to be prepended to the url if one is not specified 751 */ 752 function link_cleanup_url($url, $protocol = "http") { 753 $url = trim($url); 754 $type = link_validate_url($url); 755 756 if ($type == LINK_EXTERNAL) { 757 // Check if there is no protocol specified. 758 $protocol_match = preg_match("/^([a-z0-9][a-z0-9\.\-_]*:\/\/)/i", $url); 759 if (empty($protocol_match)) { 760 // But should there be? Add an automatic http:// if it starts with a domain name. 761 $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)('. LINK_DOMAINS .'|[a-z]{2}))/i', $url); 762 if (!empty($domain_match)) { 763 $url = $protocol ."://". $url; 764 } 765 } 766 } 767 768 return $url; 769 } 770 771 /** 772 * Wrapper around html_entity_decode to handle problems with PHP 4. 773 * 774 * See http://drupal.org/node/739650 775 * See http://bugs.php.net/bug.php?id=25670 776 * 777 * We've taken this away from the beginning of file define() step, as this is 778 * going to be slower for PHP4, and we don't want to run this on every page load, 779 * just when we're doing a validate. 780 */ 781 function _link_html_entity_decode($html_string, $quote_style = ENT_COMPAT, $charset) { 782 if (defined('PHP_VERSION')) { 783 $version = explode('.', PHP_VERSION); 784 if ($version[0] == '5') { 785 return html_entity_decode($html_string, $quote_style, $charset); // PHP 5, use default. 786 } 787 } 788 else { 789 $version = explode('.', PHP_VERSION); 790 if ($version[0] == '5') { 791 return html_entity_decode($html_string, $quote_style, $charset); 792 } 793 } 794 // use suggested code from http://drupal.org/node/739650 795 // replace numeric entities 796 $string = preg_replace('~&#x([0-9a-f]+);~ei', "_link_code2utf(hexdec('\\1'))", $html_string); 797 $string = preg_replace('~&#([0-9]+);~e', '_link_code2utf("\\1")', $string); 798 799 // replace literal entities. 800 $trans_tbl = get_html_translation_table(HTML_ENTITIES); 801 $trans_tbl = array_flip($trans_tbl); 802 803 return strtr($string, $trans_tbl); 804 } 805 806 /** 807 * Returns the utf string corresponding to the unicode value. 808 * 809 * Needed for handling utf characters in PHP4. 810 * 811 * @see http://www.php.net/manual/en/function.html-entity-decode.php#75153 812 */ 813 function _link_code2utf($num) { 814 if ($num < 128) return chr($num); 815 if ($num < 2048) return chr(($num >> 6) + 192) . chr(($num & 63) + 128); 816 if ($num < 65536) return chr(($num >> 12) + 224) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); 817 if ($num < 2097152) return chr(($num >> 18) + 240) . chr((($num >> 12) & 63) + 128) . chr((($num >> 6) & 63) + 128) . chr(($num & 63) + 128); 818 return ''; 819 } 820 821 /** 822 * A lenient verification for URLs. Accepts all URLs following RFC 1738 standard 823 * for URL formation and all email addresses following the RFC 2368 standard for 824 * mailto address formation. 825 * 826 * @param string $text 827 * @return mixed Returns boolean FALSE if the URL is not valid. On success, returns an object with 828 * the following attributes: protocol, hostname, ip, and port. 829 */ 830 function link_validate_url($text) { 831 $LINK_ICHARS_DOMAIN = (string) _link_html_entity_decode(implode("", array( 832 "æ", // æ 833 "Æ", // Æ 834 "ø", // ø 835 "Ø", // Ø 836 "å", // å 837 "Å", // Å 838 "ä", // ä 839 "Ä", // Ä 840 "ö", // ö 841 "Ö", // Ö 842 "ü", // ü 843 "Ü", // Ü 844 "Ñ", // Ñ 845 "ñ", // ñ 846 )), ENT_QUOTES, 'UTF-8'); 847 848 $LINK_ICHARS = $LINK_ICHARS_DOMAIN . (string) _link_html_entity_decode(implode("", array( 849 "ß", // ß 850 )), ENT_QUOTES, 'UTF-8'); 851 $allowed_protocols = variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal')); 852 853 $protocol = '(('. implode("|", $allowed_protocols) .'):\/\/)'; 854 $authentication = '(([a-z0-9%' . $LINK_ICHARS . ']+(:[a-z0-9%'. $LINK_ICHARS . '!]*)?)?@)'; 855 $domain = '(([a-z0-9' . $LINK_ICHARS_DOMAIN . ']([a-z0-9'. $LINK_ICHARS_DOMAIN . '\-_\[\]])*)(\.(([a-z0-9' . $LINK_ICHARS_DOMAIN . '\-_\[\]])+\.)*('. LINK_DOMAINS .'|[a-z]{2}))?)'; 856 $ipv4 = '([0-9]{1,3}(\.[0-9]{1,3}){3})'; 857 $ipv6 = '([0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7})'; 858 $port = '(:([0-9]{1,5}))'; 859 860 // Pattern specific to external links. 861 $external_pattern = '/^'. $protocol .'?'. $authentication .'?('. $domain .'|'. $ipv4 .'|'. $ipv6 .' |localhost)'. $port .'?'; 862 863 // Pattern specific to internal links. 864 $internal_pattern = "/^([a-z0-9". $LINK_ICHARS ."_\-+\[\]]+)"; 865 $internal_pattern_file = "/^([a-z0-9". $LINK_ICHARS ."_\-+\[\]\.]+)$/i"; 866 867 $directories = "(\/[a-z0-9". $LINK_ICHARS ."_\-\.~+%=&,$'!():;*@\[\]]*)*"; 868 // Yes, four backslashes == a single backslash. 869 $query = "(\/?\?([?a-z0-9". $LINK_ICHARS ."+_|\-\.\/\\\\%=&,$'():;*@\[\]{} ]*))"; 870 $anchor = "(#[a-z0-9". $LINK_ICHARS ."_\-\.~+%=&,$'():;*@\[\]\/\?]*)"; 871 872 // The rest of the path for a standard URL. 873 $end = $directories .'?'. $query .'?'. $anchor .'?'.'$/i'; 874 875 $message_id = '[^@].*@'. $domain; 876 $newsgroup_name = '([0-9a-z+-]*\.)*[0-9a-z+-]*'; 877 $news_pattern = '/^news:('. $newsgroup_name .'|'. $message_id .')$/i'; 878 879 $user = '[a-zA-Z0-9'. $LINK_ICHARS .'_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+'; 880 $email_pattern = '/^mailto:'. $user .'@'.'('. $domain .'|'. $ipv4 .'|'. $ipv6 .'|localhost)'. $query .'?$/'; 881 882 if (strpos($text, '<front>') === 0) { 883 return LINK_FRONT; 884 } 885 if (in_array('mailto', $allowed_protocols) && preg_match($email_pattern, $text)) { 886 return LINK_EMAIL; 887 } 888 if (in_array('news', $allowed_protocols) && preg_match($news_pattern, $text)) { 889 return LINK_NEWS; 890 } 891 if (preg_match($internal_pattern . $end, $text)) { 892 return LINK_INTERNAL; 893 } 894 if (preg_match($external_pattern . $end, $text)) { 895 return LINK_EXTERNAL; 896 } 897 if (preg_match($internal_pattern_file, $text)) { 898 return LINK_INTERNAL; 899 } 900 901 return FALSE; 902 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Jul 9 18:01:44 2012 | Cross-referenced by PHPXref 0.7 |