Mercurial > hg > rr-repo
view sites/all/modules/link/link.module @ 4:ce11bbd8f642
added modules
author | danieleb <danielebarchiesi@me.com> |
---|---|
date | Thu, 19 Sep 2013 10:38:44 +0100 |
parents | |
children |
line wrap: on
line source
<?php /** * @file * Defines simple link field types. */ define('LINK_EXTERNAL', 'external'); define('LINK_INTERNAL', 'internal'); define('LINK_FRONT', 'front'); define('LINK_EMAIL', 'email'); define('LINK_NEWS', 'news'); 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'); define('LINK_TARGET_DEFAULT', 'default'); define('LINK_TARGET_NEW_WINDOW', '_blank'); define('LINK_TARGET_TOP', '_top'); define('LINK_TARGET_USER', 'user'); /** * Maximum URLs length - needs to match value in link.install. */ define('LINK_URL_MAX_LENGTH', 2048); /** * Implements hook_field_info(). */ function link_field_info() { return array( 'link_field' => array( 'label' => t('Link'), 'description' => t('Store a title, href, and attributes in the database to assemble a link.'), 'settings' => array( 'attributes' => _link_default_attributes(), 'url' => 0, 'title' => 'optional', 'title_value' => '', 'title_maxlength' => 128, 'enable_tokens' => 1, 'display' => array( 'url_cutoff' => 80, ), ), 'instance_settings' => array( 'attributes' => _link_default_attributes(), 'url' => 0, 'title' => 'optional', 'title_value' => '', 'title_maxlength' => 128, 'enable_tokens' => 1, 'display' => array( 'url_cutoff' => 80, ), 'validate_url' => 1, ), 'default_widget' => 'link_field', 'default_formatter' => 'link_default', // Support hook_entity_property_info() from contrib "Entity API". 'property_type' => 'field_item_link', 'property_callbacks' => array('link_field_property_info_callback'), ), ); } /** * Implements hook_field_instance_settings_form(). */ function link_field_instance_settings_form($field, $instance) { $form = array( '#element_validate' => array('link_field_settings_form_validate'), ); $form['validate_url'] = array( '#type' => 'checkbox', '#title' => t('Validate URL'), '#default_value' => isset($instance['settings']['validate_url']) && ($instance['settings']['validate_url'] !== '') ? $instance['settings']['validate_url'] : TRUE, '#description' => t('If checked, the URL field will be verified as a valid URL during validation.'), ); $form['url'] = array( '#type' => 'checkbox', '#title' => t('Optional URL'), '#default_value' => isset($instance['settings']['url']) ? $instance['settings']['url'] : '', '#return_value' => 'optional', '#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.'), ); $title_options = array( 'optional' => t('Optional Title'), 'required' => t('Required Title'), 'value' => t('Static Title'), 'none' => t('No Title'), ); $form['title'] = array( '#type' => 'radios', '#title' => t('Link Title'), '#default_value' => isset($instance['settings']['title']) ? $instance['settings']['title'] : 'optional', '#options' => $title_options, '#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 entity 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.'), ); $form['title_value'] = array( '#type' => 'textfield', '#title' => t('Static title'), '#default_value' => isset($instance['settings']['title_value']) ? $instance['settings']['title_value'] : '', '#description' => t('This title will always be used if “Static Title” is selected above.'), ); $form['title_maxlength'] = array( '#type' => 'textfield', '#title' => t('Max length of title field'), '#default_value' => isset($instance['settings']['title_maxlength']) ? $instance['settings']['title_maxlength'] : '128', '#description' => t('Set a maximum length on the title field (applies only if Link Title is optional or required). The maximum limit is 255 characters.'), '#maxlength' => 3, '#size' => 3, ); if (module_exists('token')) { // Add token module replacements fields $form['tokens'] = array( '#type' => 'fieldset', '#collapsible' => TRUE, '#collapsed' => TRUE, '#title' => t('Placeholder tokens'), '#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."), ); $entity_info = entity_get_info($instance['entity_type']); $form['tokens']['help'] = array( '#theme' => 'token_tree', '#token_types' => array($entity_info['token type']), '#global_types' => TRUE, '#click_insert' => TRUE, ); $form['enable_tokens'] = array( '#type' => 'checkbox', '#title' => t('Allow user-entered tokens'), '#default_value' => isset($instance['settings']['enable_tokens']) ? $instance['settings']['enable_tokens'] : 1, '#description' => t('Checking will allow users to enter tokens in URLs and Titles on the entity edit form. This does not affect the field settings on this page.'), ); } $form['display'] = array( '#tree' => TRUE, ); $form['display']['url_cutoff'] = array( '#type' => 'textfield', '#title' => t('URL Display Cutoff'), '#default_value' => isset($instance['settings']['display']['url_cutoff']) ? $instance['settings']['display']['url_cutoff'] : '80', '#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.'), '#maxlength' => 3, '#size' => 3, ); $target_options = array( LINK_TARGET_DEFAULT => t('Default (no target attribute)'), LINK_TARGET_TOP => t('Open link in window root'), LINK_TARGET_NEW_WINDOW => t('Open link in new window'), LINK_TARGET_USER => t('Allow the user to choose'), ); $form['attributes'] = array( '#tree' => TRUE, ); $form['attributes']['target'] = array( '#type' => 'radios', '#title' => t('Link Target'), '#default_value' => empty($instance['settings']['attributes']['target']) ? LINK_TARGET_DEFAULT : $instance['settings']['attributes']['target'], '#options' => $target_options, ); $form['attributes']['rel'] = array( '#type' => 'textfield', '#title' => t('Rel Attribute'), '#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.'), '#default_value' => empty($instance['settings']['attributes']['rel']) ? '' : $instance['settings']['attributes']['rel'], '#field_prefix' => 'rel = "', '#field_suffix' => '"', '#size' => 20, ); $rel_remove_options = array( 'default' => t('Keep rel as set up above (untouched/default)'), 'rel_remove_external' => t('Remove rel if given link is external'), 'rel_remove_internal' => t('Remove rel if given link is internal'), ); $form['rel_remove'] = array( '#type' => 'radios', '#title' => t('Remove rel attribute automatically'), '#default_value' => !isset($instance['settings']['rel_remove']) ? 'default' : $instance['settings']['rel_remove'], '#description' => t('Turn on/off if rel attribute should be removed automatically, if user given link is internal/external'), '#options' => $rel_remove_options, ); $form['attributes']['class'] = array( '#type' => 'textfield', '#title' => t('Additional CSS Class'), '#description' => t('When output, this link will have this class attribute. Multiple classes should be separated by spaces.'), '#default_value' => empty($instance['settings']['attributes']['class']) ? '' : $instance['settings']['attributes']['class'], ); $form['attributes']['configurable_title'] = array( '#title' => t("Allow the user to enter a link 'title' attribute"), '#type' => 'checkbox', '#default_value' => empty($instance['settings']['attributes']['configurable_title']) ? '' : $instance['settings']['attributes']['configurable_title'], ); $form['attributes']['title'] = array( '#title' => t("Default link 'title' Attribute"), '#type' => 'textfield', '#description' => t('When output, links will use this "title" attribute if the user does not provide one and 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.'), '#default_value' => empty($instance['settings']['attributes']['title']) ? '' : $instance['settings']['attributes']['title'], '#field_prefix' => 'title = "', '#field_suffix' => '"', '#size' => 20, ); return $form; } /** * #element_validate handler for link_field_instance_settings_form(). */ function link_field_settings_form_validate($element, &$form_state, $complete_form) { if ($form_state['values']['instance']['settings']['title'] === 'value' && empty($form_state['values']['instance']['settings']['title_value'])) { form_set_error('title_value', t('A default title must be provided if the title is a static value.')); } if (!empty($form_state['values']['instance']['settings']['display']['url_cutoff']) && !is_numeric($form_state['values']['instance']['settings']['display']['url_cutoff'])) { form_set_error('display', t('URL Display Cutoff value must be numeric.')); } if (empty($form_state['values']['instance']['settings']['title_maxlength'])) { form_set_value($element['title_maxlength'], '128', $form_state); } elseif (!is_numeric($form_state['values']['instance']['settings']['title_maxlength'])) { form_set_error('title_maxlength', t('The max length of the link title must be numeric.')); } elseif ($form_state['values']['instance']['settings']['title_maxlength'] > 255) { form_set_error('title_maxlength', t('The max length of the link title cannot be greater than 255 characters.')); } } /** * Implements hook_field_is_empty(). */ function link_field_is_empty($item, $field) { return empty($item['title']) && empty($item['url']); } /** * Implements hook_field_load(). */ function link_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { foreach ($entities as $id => $entity) { foreach ($items[$id] as $delta => $item) { $items[$id][$delta]['attributes'] = _link_load($field, $item, $instances[$id]); } } } /** * Implements hook_field_validate(). */ function link_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) { $optional_field_found = FALSE; if ($instance['settings']['validate_url'] !== 0 || is_null($instance['settings']['validate_url']) || !isset($instance['settings']['validate_url'])) { foreach ($items as $delta => $value) { _link_validate($items[$delta], $delta, $field, $entity, $instance, $langcode, $optional_field_found); } } if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] && !$optional_field_found) { form_set_error($field['field_name'] . '][' . $langcode . '][0][title', t('At least one title or URL must be entered.')); } } /** * Implements hook_field_insert(). */ function link_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) { foreach ($items as $delta => $value) { _link_process($items[$delta], $delta, $field, $entity); } } /** * Implements hook_field_update(). */ function link_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) { foreach ($items as $delta => $value) { _link_process($items[$delta], $delta, $field, $entity); } } /** * Implements hook_field_prepare_view(). */ function link_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) { foreach ($items as $entity_id => $entity_items) { foreach ($entity_items as $delta => $value) { _link_sanitize($items[$entity_id][$delta], $delta, $field, $instances[$entity_id], $entities[$entity_id]); } } } /** * Implements hook_field_widget_info(). */ function link_field_widget_info() { return array( 'link_field' => array( 'label' => 'Link', 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), ); } /** * Implements hook_field_widget_form(). */ function link_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { $element += array( '#type' => $instance['widget']['type'], '#default_value' => isset($items[$delta]) ? $items[$delta] : '', ); return $element; } /** * Unpacks the item attributes for use. */ function _link_load($field, $item, $instance) { if (isset($item['attributes'])) { if (!is_array($item['attributes'])) { $item['attributes'] = unserialize($item['attributes']); } return $item['attributes']; } elseif (isset($instance['settings']['attributes'])) { return $instance['settings']['attributes']; } else { return $field['settings']['attributes']; } } /** * Prepares the item attributes and url for storage. */ function _link_process(&$item, $delta = 0, $field, $entity) { // Trim whitespace from URL. $item['url'] = trim($item['url']); // If no attributes are set then make sure $item['attributes'] is an empty // array, so $field['attributes'] can override it. if (empty($item['attributes'])) { $item['attributes'] = array(); } // Serialize the attributes array. if (!is_string($item['attributes'])) { $item['attributes'] = serialize($item['attributes']); } // Don't save an invalid default value (e.g. 'http://'). if ((isset($field['widget']['default_value'][$delta]['url']) && $item['url'] == $field['widget']['default_value'][$delta]['url']) && is_object($entity)) { if (!link_validate_url($item['url'])) { unset($item['url']); } } } /** * Validates that the link field has been entered properly. */ function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, &$optional_field_found) { if ($item['url'] && !(isset($instance['default_value'][$delta]['url']) && $item['url'] === $instance['default_value'][$delta]['url'] && !$instance['required'])) { // Validate the link. if (link_validate_url(trim($item['url'])) == FALSE) { form_set_error($field['field_name'] . '][' . $langcode . '][' . $delta . '][url', t('The value provided for %field is not a valid URL.', array('%field' => $instance['label']))); } // Require a title for the link if necessary. if ($instance['settings']['title'] == 'required' && strlen(trim($item['title'])) == 0) { form_set_error($field['field_name'] . '][' . $langcode . '][' . $delta . '][title', t('Titles are required for all links.')); } } // Require a link if we have a title. if ($instance['settings']['url'] !== 'optional' && strlen(isset($item['title']) ? $item['title'] : NULL) > 0 && strlen(trim($item['url'])) == 0) { form_set_error($field['field_name'] . '][' . $langcode . '][' . $delta . '][url', t('You cannot enter a title without a link url.')); } // In a totally bizzaro case, where URLs and titles are optional but the field is required, ensure there is at least one link. if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && (strlen(trim($item['url'])) !== 0 || strlen(trim($item['title'])) !== 0)) { $optional_field_found = TRUE; } // Require entire field if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] == 1 && !$optional_field_found && isset($instance['id'])) { form_set_error($instance['field_name'] . '][' . $langcode . '][0][title', t('At least one title or URL must be entered.')); } } /** * Clean up user-entered values for a link field according to field settings. * * @param $item * A single link item, usually containing url, title, and attributes. * @param $delta * The delta value if this field is one of multiple fields. * @param $field * The CCK field definition. * @param $entity * The entity containing this link. */ function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) { // Don't try to process empty links. if (empty($item['url']) && empty($item['title'])) { return; } // Replace URL tokens. $entity_type = $instance['entity_type']; $entity_info = entity_get_info($entity_type); $property_id = $entity_info['entity keys']['id']; $entity_token_type = isset($entity_info['token type']) ? $entity_info['token type'] : ( $entity_type == 'taxonomy_term' || $entity_type == 'taxonomy_vocabulary' ? str_replace('taxonomy_', '', $entity_type) : $entity_type ); if (isset($instance['settings']['enable_tokens']) && $instance['settings']['enable_tokens']) { global $user; // Load the entity if necessary for entities in views. if (isset($entity->{$property_id})) { $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); $entity_loaded = array_pop($entity_loaded); } else { $entity_loaded = $entity; } $item['url'] = token_replace($item['url'], array($entity_token_type => $entity_loaded)); } $type = link_validate_url($item['url']); // If the type of the URL cannot be determined and URL validation is disabled, // then assume LINK_EXTERNAL for later processing. if ($type == FALSE && $instance['settings']['validate_url'] === 0) { $type = LINK_EXTERNAL; } $url = link_cleanup_url($item['url']); $url_parts = _link_parse_url($url); // We can't check_plain('<front>') because it'll break. if ($type != LINK_FRONT) { $url_parts['url'] = check_plain($url_parts['url']); } if (!empty($url_parts['url'])) { $item['url'] = url($url_parts['url'], array( 'query' => isset($url_parts['query']) ? $url_parts['query'] : NULL, 'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : NULL, 'absolute' => TRUE, 'html' => TRUE, ) ); } // Create a shortened URL for display. if ($type == LINK_EMAIL) { $display_url = str_replace('mailto:', '', $url); } else { $display_url = url($url_parts['url'], array( 'query' => isset($url_parts['query']) ? $url_parts['query'] : NULL, 'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : NULL, 'absolute' => TRUE, ) ); } if ($instance['settings']['display']['url_cutoff'] && strlen($display_url) > $instance['settings']['display']['url_cutoff']) { $display_url = substr($display_url, 0, $instance['settings']['display']['url_cutoff']) . "..."; } $item['display_url'] = $display_url; // Use the title defined at the instance level. if ($instance['settings']['title'] == 'value' && strlen(trim($instance['settings']['title_value']))) { $title = $instance['settings']['title_value']; if (function_exists('i18n_string_translate')) { $i18n_string_name = "field:{$instance['field_name']}:{$instance['bundle']}:title_value"; $title = i18n_string_translate($i18n_string_name, $title); } } // Use the title defined by the user at the widget level. elseif (isset($item['title'])) { $title = $item['title']; } else { $title = ''; } // Replace tokens. if ($title && ($instance['settings']['title'] == 'value' || $instance['settings']['enable_tokens'])) { // Load the entity if necessary for entities in views. if (isset($entity->{$property_id})) { $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); $entity_loaded = array_pop($entity_loaded); } else { $entity_loaded = $entity; } $title = token_replace($title, array($entity_token_type => $entity_loaded)); $title = filter_xss($title, array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); $item['html'] = TRUE; } $item['title'] = empty($title) ? $item['display_url'] : $title; if (!isset($item['attributes'])) { $item['attributes'] = array(); } // Unserialize attributtes array if it has not been unserialized yet. if (!is_array($item['attributes'])) { $item['attributes'] = (array)unserialize($item['attributes']); } // Add default attributes. if (!is_array($instance['settings']['attributes'])) { $instance['settings']['attributes'] = _link_default_attributes(); } else { $instance['settings']['attributes'] += _link_default_attributes(); } // Merge item attributes with attributes defined at the field level. $item['attributes'] += $instance['settings']['attributes']; // If user is not allowed to choose target attribute, use default defined at // field level. if ($instance['settings']['attributes']['target'] != LINK_TARGET_USER) { $item['attributes']['target'] = $instance['settings']['attributes']['target']; } elseif ($item['attributes']['target'] == LINK_TARGET_USER) { $item['attributes']['target'] = LINK_TARGET_DEFAULT; } // Remove the target attribute if the default (no target) is selected. if (empty($item['attributes']) || (isset($item['attributes']['target']) && $item['attributes']['target'] == LINK_TARGET_DEFAULT)) { unset($item['attributes']['target']); } // Remove rel attribute for internal or external links if selected. if (isset($item['attributes']['rel']) && isset($instance['settings']['rel_remove']) && $instance['settings']['rel_remove'] != 'default') { if (($instance['settings']['rel_remove'] != 'rel_remove_internal' && $type != LINK_INTERNAL) || ($instance['settings']['rel_remove'] != 'rel_remove_external' && $type != LINK_EXTERNAL)) { unset($item['attributes']['rel']); } } // Handle "title" link attribute. if (!empty($item['attributes']['title']) && module_exists('token')) { // Load the entity (necessary for entities in views). if (isset($entity->{$property_id})) { $entity_loaded = entity_load($entity_type, array($entity->{$property_id})); $entity_loaded = array_pop($entity_loaded); } else { $entity_loaded = $entity; } $item['attributes']['title'] = token_replace($item['attributes']['title'], array($entity_token_type => $entity_loaded)); $item['attributes']['title'] = filter_xss($item['attributes']['title'], array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u')); } // Remove title attribute if it's equal to link text. if (isset($item['attributes']['title']) && $item['attributes']['title'] == $item['title']) { unset($item['attributes']['title']); } unset($item['attributes']['configurable_title']); // Remove empty attributes. $item['attributes'] = array_filter($item['attributes']); // Sets title to trimmed url if one exists // @todo: Obsolete? /*if(!empty($item['display_url']) && empty($item['title'])) { $item['title'] = $item['display_url']; } elseif(!isset($item['title'])) { $item['title'] = $item['url']; }*/ } /** * Because parse_url doesn't work with relative urls. * * @param string $url * URL to parse. * * @return Array * Array of url pieces - only 'url', 'query', and 'fragment'. */ function _link_parse_url($url) { $url_parts = array(); // Separate out the anchor, if any. if (strpos($url, '#') !== FALSE) { $url_parts['fragment'] = substr($url, strpos($url, '#') + 1); $url = substr($url, 0, strpos($url, '#')); } // Separate out the query string, if any. if (strpos($url, '?') !== FALSE) { $query = substr($url, strpos($url, '?') + 1); parse_str($query, $query_array); // See http://drupal.org/node/1710578 foreach ($query_array as $key=> &$value) { if ($value === '' && FALSE === strpos($query, $key . '=')) { $value = NULL; } } $url_parts['query'] = $query_array; $url = substr($url, 0, strpos($url, '?')); } $url_parts['url'] = $url; return $url_parts; } /** * Implements hook_theme(). */ function link_theme() { return array( 'link_formatter_link_default' => array( 'variables' => array('element' => NULL), ), 'link_formatter_link_plain' => array( 'variables' => array('element' => NULL), ), 'link_formatter_link_absolute' => array( 'variables' => array('element' => NULL), ), 'link_formatter_link_domain' => array( 'variables' => array('element' => NULL, 'display' => NULL), ), 'link_formatter_link_title_plain' => array( 'variables' => array('element' => NULL), ), 'link_formatter_link_url' => array( 'variables' => array('element' => NULL), ), 'link_formatter_link_short' => array( 'variables' => array('element' => NULL), ), 'link_formatter_link_label' => array( 'variables' => array('element' => NULL, 'field' => NULL), ), 'link_formatter_link_separate' => array( 'variables' => array('element' => NULL), ), 'link_field' => array( 'render element' => 'element', ), ); } /** * Formats a link field widget. */ function theme_link_field($vars) { drupal_add_css(drupal_get_path('module', 'link') . '/link.css'); $element = $vars['element']; // Prefix single value link fields with the name of the field. if (empty($element['#field']['multiple'])) { if (isset($element['url']) && !isset($element['title'])) { $element['url']['#title_display'] = 'invisible'; } } $output = ''; $output .= '<div class="link-field-subrow clearfix">'; if (isset($element['title'])) { $output .= '<div class="link-field-title link-field-column">' . drupal_render($element['title']) . '</div>'; } $output .= '<div class="link-field-url' . (isset($element['title']) ? ' link-field-column' : '') . '">'. drupal_render($element['url']) . '</div>'; $output .= '</div>'; if (!empty($element['attributes']['target'])) { $output .= '<div class="link-attributes">' . drupal_render($element['attributes']['target']) . '</div>'; } if (!empty($element['attributes']['title'])) { $output .= '<div class="link-attributes">' . drupal_render($element['attributes']['title']) . '</div>'; } return $output; } /** * Implements hook_element_info(). */ function link_element_info() { $elements = array(); $elements['link_field'] = array( '#input' => TRUE, '#process' => array('link_field_process'), '#theme' => 'link_field', '#theme_wrappers' => array('form_element'), ); return $elements; } /** * Returns the default attributes and their values. */ function _link_default_attributes() { return array( 'target' => LINK_TARGET_DEFAULT, 'class' => '', 'rel' => '', ); } /** * Processes the link type element before displaying the field. * * Build the form element. When creating a form using FAPI #process, * note that $element['#value'] is already set. * * The $fields array is in $complete_form['#field_info'][$element['#field_name']]. */ function link_field_process($element, $form_state, $complete_form) { $instance = field_widget_instance($element, $form_state); $settings = $instance['settings']; $element['url'] = array( '#type' => 'textfield', '#maxlength' => LINK_URL_MAX_LENGTH, '#title' => t('URL'), '#required' => ($element['#delta'] == 0 && $settings['url'] !== 'optional') ? $element['#required'] : FALSE, '#default_value' => isset($element['#value']['url']) ? $element['#value']['url'] : NULL, ); if ($settings['title'] !== 'none' && $settings['title'] !== 'value') { $element['title'] = array( '#type' => 'textfield', '#maxlength' => $settings['title_maxlength'], '#title' => t('Title'), '#description' => t('The link title is limited to @maxlength characters maximum.', array('@maxlength' => $settings['title_maxlength'])), '#required' => ($settings['title'] == 'required' && (($element['#delta'] == 0 && $element['#required']) || !empty($element['#value']['url']))) ? TRUE : FALSE, '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL, ); } // Initialize field attributes as an array if it is not an array yet. if (!is_array($settings['attributes'])) { $settings['attributes'] = array(); } // Add default attributes. $settings['attributes'] += _link_default_attributes(); $attributes = isset($element['#value']['attributes']) ? $element['#value']['attributes'] : $settings['attributes']; if (!empty($settings['attributes']['target']) && $settings['attributes']['target'] == LINK_TARGET_USER) { $element['attributes']['target'] = array( '#type' => 'checkbox', '#title' => t('Open URL in a New Window'), '#return_value' => LINK_TARGET_NEW_WINDOW, '#default_value' => isset($attributes['target']) ? $attributes['target'] : FALSE, ); } if (!empty($settings['attributes']['configurable_title']) && $settings['attributes']['configurable_title'] == 1) { $element['attributes']['title'] = array( '#type' => 'textfield', '#title' => t('Link "title" attribute'), '#default_value' => isset($attributes['title']) ? $attributes['title'] : '', '#field_prefix' => 'title = "', '#field_suffix' => '"', ); } // If the title field is avaliable or there are field accepts multiple values // then allow the individual field items display the required asterisk if needed. if (isset($element['title']) || isset($element['_weight'])) { // To prevent an extra required indicator, disable the required flag on the // base element since all the sub-fields are already required if desired. $element['#required'] = FALSE; } return $element; } /** * Implements hook_field_formatter_info(). */ function link_field_formatter_info() { return array( 'link_default' => array( 'label' => t('Title, as link (default)'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), 'link_title_plain' => array( 'label' => t('Title, as plain text'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), 'link_url' => array( 'label' => t('URL, as link'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), 'link_plain' => array( 'label' => t('URL, as plain text'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), 'link_absolute' => array( 'label' => t('URL, absolute'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), 'link_domain' => array( 'label' => t('Domain, as link'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, 'settings' => array( 'strip_www' => FALSE, ), ), 'link_short' => array( 'label' => t('Short, as link with title "Link"'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), 'link_label' => array( 'label' => t('Label, as link with label as title'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), 'link_separate' => array( 'label' => t('Separate title and URL'), 'field types' => array('link_field'), 'multiple values' => FIELD_BEHAVIOR_DEFAULT, ), ); } /** * Implements hook_field_formatter_settings_form(). */ function link_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; $element = array(); if ($display['type'] == 'link_domain') { $element['strip_www'] = array( '#title' => t('Strip www. from domain'), '#type' => 'checkbox', '#default_value' => $settings['strip_www'], ); } return $element; } /** * Implements hook_field_formatter_settings_summary(). */ function link_field_formatter_settings_summary($field, $instance, $view_mode) { $display = $instance['display'][$view_mode]; $settings = $display['settings']; if ($display['type'] == 'link_domain') { if ($display['settings']['strip_www']) { return t('Strip www. from domain'); } else { return t('Leave www. in domain'); } } return ''; } /** * Implements hook_field_formatter_view(). */ function link_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) { $elements = array(); foreach ($items as $delta => $item) { $elements[$delta] = array( '#theme' => 'link_formatter_' . $display['type'], '#element' => $item, '#field' => $instance, '#display' => $display, ); } return $elements; } /** * Formats a link. */ function theme_link_formatter_link_default($vars) { $link_options = $vars['element']; unset($link_options['title']); unset($link_options['url']); if (isset($link_options['attributes']['class'])) { $link_options['attributes']['class'] = array($link_options['attributes']['class']); } // Display a normal link if both title and URL are available. if (!empty($vars['element']['title']) && !empty($vars['element']['url'])) { return l($vars['element']['title'], $vars['element']['url'], $link_options); } // If only a title, display the title. elseif (!empty($vars['element']['title'])) { return check_plain($vars['element']['title']); } elseif (!empty($vars['element']['url'])) { return l($vars['element']['title'], $vars['element']['url'], $link_options); } } /** * Formats a link (or its title) as plain text. */ function theme_link_formatter_link_plain($vars) { $link_options = $vars['element']; if (isset($link_options['title'])) { unset($link_options['title']); } else { $vars['element']['title'] = ''; } unset($link_options['url']); return empty($vars['element']['url']) ? check_plain($vars['element']['title']) : url($vars['element']['url'], $link_options); } /** * Formats a link as an absolute URL */ function theme_link_formatter_link_absolute($vars) { $absolute = array('absolute' => TRUE); return empty($vars['element']['url']) ? '' : url($vars['element']['url'], $absolute + $vars['element']); } /** * Formats a link using the URL's domain for it's link text. */ function theme_link_formatter_link_domain($vars) { $link_options = $vars['element']; unset($link_options['title']); unset($link_options['url']); $domain = parse_url($vars['element']['display_url'], PHP_URL_HOST); if (!empty($vars['display']['settings']['strip_www'])) { $domain = str_replace('www.', '', $domain); } return $vars['element']['url'] ? l($domain, $vars['element']['url'], $link_options) : ''; } /** * Formats a link's title as plain text. */ function theme_link_formatter_link_title_plain($vars) { return empty($vars['element']['title']) ? '' : check_plain($vars['element']['title']); } /** * Formats a link using an alternate display URL for its link text. */ function theme_link_formatter_link_url($vars) { $link_options = $vars['element']; unset($link_options['title']); unset($link_options['url']); return $vars['element']['url'] ? l($vars['element']['display_url'], $vars['element']['url'], $link_options) : ''; } /** * Formats a link using "Link" as the link text. */ function theme_link_formatter_link_short($vars) { $link_options = $vars['element']; unset($link_options['title']); unset($link_options['url']); return $vars['element']['url'] ? l(t('Link'), $vars['element']['url'], $link_options) : ''; } /** * Formats a link using the field's label as link text. */ function theme_link_formatter_link_label($vars) { $link_options = $vars['element']; unset($link_options['title']); unset($link_options['url']); return $vars['element']['url'] ? l($vars['field']['label'], $vars['element']['url'], $link_options) : ''; } /** * Formats a link as separate title and URL elements. */ function theme_link_formatter_link_separate($vars) { $class = empty($vars['element']['attributes']['class']) ? '' : ' ' . $vars['element']['attributes']['class']; unset($vars['element']['attributes']['class']); $link_options = $vars['element']; unset($link_options['title']); unset($link_options['url']); $title = empty($vars['element']['title']) ? '' : check_plain($vars['element']['title']); /** * @TODO static html markup looks not very elegant * needs smarter output solution and an optional title/url seperator */ $url_parts = _link_parse_url($vars['element']['url']); $output = ''; $output .= '<div class="link-item ' . $class . '">'; if (!empty($title)) { $output .= '<div class="link-title">' . $title . '</div>'; } $output .= '<div class="link-url">' . l($url_parts['url'], $vars['element']['url'], $link_options) . '</div>'; $output .= '</div>'; return $output; } /** * Implements hook_token_list(). */ function link_token_list($type = 'all') { if ($type === 'field' || $type === 'all') { $tokens = array(); $tokens['link']['url'] = t("Link URL"); $tokens['link']['title'] = t("Link title"); $tokens['link']['view'] = t("Formatted html link"); return $tokens; } } function link_token_values($type, $object = NULL) { if ($type === 'field') { $item = $object[0]; $tokens['url'] = $item['url']; $tokens['title'] = $item['title']; $tokens['view'] = isset($item['view']) ? $item['view'] : ''; return $tokens; } } /** * Implements hook_views_api(). */ function link_views_api() { return array( 'api' => 2, 'path' => drupal_get_path('module', 'link') . '/views', ); } /** * Forms a valid URL if possible from an entered address. * * Trims whitespace and automatically adds an http:// to addresses without a * protocol specified * * @param string $url * @param string $protocol * The protocol to be prepended to the url if one is not specified */ function link_cleanup_url($url, $protocol = 'http') { $url = trim($url); $type = link_validate_url($url); if ($type === LINK_EXTERNAL) { // Check if there is no protocol specified. $protocol_match = preg_match("/^([a-z0-9][a-z0-9\.\-_]*:\/\/)/i", $url); if (empty($protocol_match)) { // But should there be? Add an automatic http:// if it starts with a domain name. $LINK_DOMAINS = _link_domains(); $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)(' . $LINK_DOMAINS . '|[a-z]{2}))/i', $url); if (!empty($domain_match)) { $url = $protocol . "://" . $url; } } } return $url; } /** * Validates a URL. * * Accepts all URLs following RFC 1738 standard for URL formation and all e-mail * addresses following the RFC 2368 standard for mailto address formation. * * @param string $text * * @return mixed * Returns boolean FALSE if the URL is not valid. On success, returns one of * the LINK_(linktype) constants. */ function link_validate_url($text) { // @TODO Complete letters. $LINK_ICHARS_DOMAIN = (string) html_entity_decode(implode("", array( "æ", // æ "Æ", // Æ "À", // À "à", // à "Á", // Á "á", // á "Â", //  "â", // â "å", // å "Å", // Å "ä", // ä "Ä", // Ä "Ç", // Ç "ç", // ç "Ð", // Ð "ð", // ð "È", // È "è", // è "É", // É "é", // é "Ê", // Ê "ê", // ê "Ë", // Ë "ë", // ë "Î", // Î "î", // î "Ï", // Ï "ï", // ï "ø", // ø "Ø", // Ø "ö", // ö "Ö", // Ö "Ô", // Ô "ô", // ô "Õ", // Õ "õ", // õ "Œ", // Œ "œ", // œ "ü", // ü "Ü", // Ü "Ù", // Ù "ù", // ù "Û", // Û "û", // û "Ÿ", // Ÿ "ÿ", // ÿ "Ñ", // Ñ "ñ", // ñ "þ", // þ "Þ", // Þ "ý", // ý "Ý", // Ý "¿", // ¿ )), ENT_QUOTES, 'UTF-8'); $LINK_ICHARS = $LINK_ICHARS_DOMAIN . (string) html_entity_decode(implode("", array( "ß", // ß )), ENT_QUOTES, 'UTF-8'); $allowed_protocols = variable_get('filter_allowed_protocols', array('http', 'https', 'ftp', 'news', 'nntp', 'telnet', 'mailto', 'irc', 'ssh', 'sftp', 'webcal')); $LINK_DOMAINS = _link_domains(); // Starting a parenthesis group with (?: means that it is grouped, but is not captured $protocol = '((?:' . implode("|", $allowed_protocols) . '):\/\/)'; $authentication = "(?:(?:(?:[\w\.\-\+!$&'\(\)*\+,;=" . $LINK_ICHARS . "]|%[0-9a-f]{2})+(?::(?:[\w" . $LINK_ICHARS . "\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})*)?)?@)"; $domain = '(?:(?:[a-z0-9' . $LINK_ICHARS_DOMAIN . ']([a-z0-9' . $LINK_ICHARS_DOMAIN . '\-_\[\]])*)(\.(([a-z0-9' . $LINK_ICHARS_DOMAIN . '\-_\[\]])+\.)*(' . $LINK_DOMAINS . '|[a-z]{2}))?)'; $ipv4 = '(?:[0-9]{1,3}(\.[0-9]{1,3}){3})'; $ipv6 = '(?:[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7})'; $port = '(?::([0-9]{1,5}))'; // Pattern specific to external links. $external_pattern = '/^' . $protocol . '?' . $authentication . '?(' . $domain . '|' . $ipv4 . '|' . $ipv6 . ' |localhost)' . $port . '?'; // Pattern specific to internal links. $internal_pattern = "/^(?:[a-z0-9" . $LINK_ICHARS . "_\-+\[\] ]+)"; $internal_pattern_file = "/^(?:[a-z0-9" . $LINK_ICHARS . "_\-+\[\]\. \/\(\)][a-z0-9" . $LINK_ICHARS . "_\-+\[\]\. \(\)][a-z0-9" . $LINK_ICHARS . "_\-+\[\]\. \/\(\)]+)$/i"; $directories = "(?:\/[a-z0-9" . $LINK_ICHARS . "_\-\.~+%=&,$'#!():;*@\[\]]*)*"; // Yes, four backslashes == a single backslash. $query = "(?:\/?\?([?a-z0-9" . $LINK_ICHARS . "+_|\-\.~\/\\\\%=&,$'():;*@\[\]{} ]*))"; $anchor = "(?:#[a-z0-9" . $LINK_ICHARS . "_\-\.~+%=&,$'():;*@\[\]\/\?]*)"; // The rest of the path for a standard URL. $end = $directories . '?' . $query . '?' . $anchor . '?' . '$/i'; $message_id = '[^@].*@' . $domain; $newsgroup_name = '(?:[0-9a-z+-]*\.)*[0-9a-z+-]*'; $news_pattern = '/^news:(' . $newsgroup_name . '|' . $message_id . ')$/i'; $user = '[a-zA-Z0-9' . $LINK_ICHARS . '_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+'; $email_pattern = '/^mailto:' . $user . '@'.'(?:' . $domain . '|' . $ipv4 . '|' . $ipv6 . '|localhost)' . $query . '?$/'; if (strpos($text, '<front>') === 0) { return LINK_FRONT; } if (in_array('mailto', $allowed_protocols) && preg_match($email_pattern, $text)) { return LINK_EMAIL; } if (in_array('news', $allowed_protocols) && preg_match($news_pattern, $text)) { return LINK_NEWS; } if (preg_match($internal_pattern . $end, $text)) { return LINK_INTERNAL; } if (preg_match($external_pattern . $end, $text)) { return LINK_EXTERNAL; } if (preg_match($internal_pattern_file, $text)) { return LINK_INTERNAL; } return FALSE; } /** * Returns the list of allowed domains, including domains added by admins via variable_set/$config. */ function _link_domains() { $link_extra_domains = variable_get('link_extra_domains', array()); return empty($link_extra_domains) ? LINK_DOMAINS : LINK_DOMAINS . '|' . implode('|', $link_extra_domains); } /** * Implements hook_migrate_field_alter(). */ function link_content_migrate_field_alter(&$field_value, $instance_value) { if ($field_value['type'] == 'link') { // Adjust the field type. $field_value['type'] = 'link_field'; // Remove settings that are now on the instance. foreach (array('attributes', 'display', 'url', 'title', 'title_value', 'enable_tokens', 'validate_url') as $setting) { unset($field_value['settings'][$setting]); } } } /** * Implements hook_migrate_instance_alter(). * * Widget type also changed to link_field. */ function link_content_migrate_instance_alter(&$instance_value, $field_value) { if ($field_value['type'] == 'link') { // Grab settings that were previously on the field. foreach (array('attributes', 'display', 'url', 'title', 'title_value', 'enable_tokens', 'validate_url') as $setting) { if (isset($field_value['settings'][$setting])) { $instance_value['settings'][$setting] = $field_value['settings'][$setting]; } } // Adjust widget type. if ($instance_value['widget']['type'] == 'link') { $instance_value['widget']['type'] = 'link_field'; } // Adjust formatter types. foreach ($instance_value['display'] as $context => $settings) { if (in_array($settings['type'], array('default', 'title_plain', 'url', 'plain', 'short', 'label', 'separate'))) { $instance_value['display'][$context]['type'] = 'link_' . $settings['type']; } } } } /** * Implements hook_field_settings_form(). */ function link_field_settings_form() { return array(); } /** * Additional callback to adapt the property info of link fields. * * @see entity_metadata_field_entity_property_info(). */ function link_field_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']]; // Define a data structure so it's possible to deal with both the link title // and URL. $property['getter callback'] = 'entity_metadata_field_verbatim_get'; $property['setter callback'] = 'entity_metadata_field_verbatim_set'; // Auto-create the field item as soon as a property is set. $property['auto creation'] = 'link_field_item_create'; $property['property info'] = link_field_item_property_info(); $property['property info']['url']['required'] = !$instance['settings']['url']; $property['property info']['title']['required'] = ($instance['settings']['title'] == 'required'); if ($instance['settings']['title'] == 'none') { unset($property['property info']['title']); } unset($property['query callback']); } /** * Callback for creating a new, empty link field item. * * @see link_field_property_info_callback() */ function link_field_item_create() { return array('title' => NULL, 'url' => NULL); } /** * Defines info for the properties of the link-field item data structure. */ function link_field_item_property_info() { $properties['title'] = array( 'type' => 'text', 'label' => t('The title of the link.'), 'setter callback' => 'entity_property_verbatim_set', ); $properties['url'] = array( 'type' => 'uri', 'label' => t('The URL of the link.'), 'setter callback' => 'entity_property_verbatim_set', ); return $properties; } /** * Implements hook_field_update_instance(). */ function link_field_update_instance($instance, $prior_instance) { if (function_exists('i18n_string_update') && $prior_instance['settings']['title_value'] != $instance['settings']['title_value']) { $i18n_string_name = "field:{$instance['field_name']}:{$instance['bundle']}:title_value"; i18n_string_update($i18n_string_name, $instance['settings']['title_value']); } } /** * Implements hook_i18n_string_list_TEXTGROUP_alter(). */ function link_i18n_string_list_field_alter(&$strings, $type = NULL, $object = NULL) { if ($type == 'field_instance' && $object && $object['widget']['type'] == 'link_field') { if (isset($object['settings']['title_value'])) { $strings['field'][$object['field_name']][$object['bundle']]['title_value']['string'] = $object['settings']['title_value']; } } }