url.module in URL field 7
Same filename and directory in other branches
Provides a URL field type that stores external links with optional titles.
File
url.moduleView source
<?php
/**
* @file
* Provides a URL field type that stores external links with optional titles.
*/
/**
* Implements hook_help().
*/
function url_help($path, $arg) {
switch ($path) {
case 'admin/help#url':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('The URL module defines a simple link field type for the Field module. Links are external URLs, can have an optional title for each link, and they can be formatted when displayed. See the <a href="@field-help">Field module help page</a> for more information about fields.', array(
'@field-help' => url('admin/help/field'),
)) . '</p>';
return $output;
}
}
/**
* Implements hook_field_info().
*/
function url_field_info() {
$info['url'] = array(
'label' => t('URL'),
'description' => t('This field stores URLs with an optional title.'),
'instance_settings' => array(
'title_field' => 0,
'title_fetch' => 0,
),
'default_widget' => 'url_external',
'default_formatter' => 'url_default',
);
return $info;
}
/**
* Implements hook_field_instance_settings_form().
*/
function url_field_instance_settings_form($field, $instance) {
$settings = $instance['settings'];
$form['title_field'] = array(
'#type' => 'checkbox',
'#title' => t('Enable <em>Title</em> field'),
'#default_value' => $settings['title_field'],
'#description' => t('The title attribute is displayed when the link is displayed.'),
);
$form['title_fetch'] = array(
'#type' => 'checkbox',
'#title' => t('Attempt to fetch the title value from the URL if the <em>Title field</em> is empty.'),
'#default_value' => $settings['title_fetch'],
);
return $form;
}
/**
* Implements hook_field_load().
*/
function url_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => &$item) {
if (empty($item['attributes'])) {
$item['attributes'] = array();
}
else {
$item['attributes'] = unserialize($item['attributes']);
}
}
}
}
/**
* Implements hook_field_is_empty().
*/
function url_field_is_empty($item, $field) {
return !isset($item['value']) || $item['value'] === '';
}
/**
* Implements hook_field_presave().
*/
function url_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
$settings = $instance['settings'];
foreach ($items as $delta => &$item) {
// If the link title is empty, and auto-fetching is enabled, then attempt
// to extract the title from a request to the URL.
// Skip this process if $entity is empty (field default value form)
if (!empty($entity) && !empty($settings['title_fetch']) && !empty($item['value']) && empty($item['title'])) {
// Parse the URL into a form ready for url().
$parsed = drupal_parse_url($item['value']);
$url = url($parsed['path'], array(
'query' => $parsed['query'],
'fragment' => $parsed['fragment'],
'absolute' => TRUE,
));
$item['title'] = url_fetch_title($url);
}
// Trim any spaces around the URL title.
$item['title'] = isset($item['title']) && is_string($item['title']) ? trim($item['title']) : '';
// Serialize the attributes array.
$item['attributes'] = !empty($item['attributes']) && is_array($item['attributes']) ? serialize($item['attributes']) : NULL;
}
}
/**
* Implements hook_field_widget_info().
*/
function url_field_widget_info() {
$info['url_external'] = array(
'label' => t('External URL field'),
'field types' => array(
'url',
),
'settings' => array(
'size' => 60,
),
);
return $info;
}
/**
* Implements hook_field_widget_settings_form().
*/
function url_field_widget_settings_form($field, $instance) {
$widget = $instance['widget'];
$settings = $widget['settings'];
$form['size'] = array(
'#type' => 'textfield',
'#title' => t('Size of URL field'),
'#default_value' => $settings['size'],
'#required' => TRUE,
'#element_validate' => array(
'element_validate_integer_positive',
),
'#weight' => -1,
);
return $form;
}
/**
* Implements hook_field_widget_form().
*/
function url_field_widget_form($form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
$settings = $instance['settings'];
$single_field = $field['cardinality'] == 1 && empty($settings['title_field']);
// Display this element in a fieldset if there is only one value.
if ($field['cardinality'] == 1 && !$single_field) {
$element['#type'] = 'fieldset';
}
// URL field.
$element['value'] = array(
'#type' => 'urlfield',
// Assumes elements module is enabled.
'#title' => $single_field ? $element['#title'] : t('URL'),
'#default_value' => isset($items[$delta]['value']) ? $items[$delta]['value'] : '',
'#size' => $instance['widget']['settings']['size'],
'#maxlength' => 2048,
'#required' => !empty($element['#required']),
'#prefix' => '<div class="field-value field-value-url">',
'#suffix' => '</div>',
'#description' => $single_field ? $element['#description'] : '',
);
if (!module_exists('elements')) {
$element['value']['#type'] = 'textfield';
$element['value']['#element_validate'] = array(
'url_validate_url',
);
}
// Title field.
if (!empty($settings['title_field'])) {
$element['title'] = array(
'#type' => 'textfield',
'#title' => t('Title'),
'#default_value' => isset($items[$delta]['title']) ? $items[$delta]['title'] : '',
'#maxlength' => 1024,
'#weight' => 10,
'#prefix' => '<div class="field-value field-value-title">',
'#suffix' => '</div>',
);
// Add additional styling to make both fields work together visually.
$element['#attached']['css'][] = drupal_get_path('module', 'url') . '/url.field.css';
}
else {
$element['title'] = array(
'#type' => 'value',
'#default_value' => isset($items[$delta]['title']) ? $items[$delta]['title'] : '',
);
}
// Exposing the attributes array in the widget is left for alternate and more
// advanced field widgets.
$element['attributes'] = array(
'#type' => 'value',
'#value' => !empty($items[$delta]['attributes']) ? $items[$delta]['attributes'] : array(),
);
// If the widget is being used on a default value form, due to a core bug we
// need to set the attributes default value to be an already serialized
// value so that it saves properly.
// @todo Remove when http://drupal.org/node/1899498 is fixed.
if (empty($element['#entity']) && !isset($items[$delta]['attributes'])) {
$element['attributes']['#value'] = NULL;
}
return $element;
}
/**
* Implements hook_field_prepare_view().
*/
function url_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
foreach ($entities as $id => $entity) {
foreach ($items[$id] as $delta => &$item) {
// Split out the link into the parts required for url(): path and options.
$parsed = drupal_parse_url($item['value']);
$item['path'] = $parsed['path'];
$item['options'] = array(
'query' => $parsed['query'],
'fragment' => $parsed['fragment'],
'attributes' => &$item['attributes'],
);
}
}
}
/**
* Implements hook_field_formatter_info().
*/
function url_field_formatter_info() {
$info['url_default'] = array(
'label' => t('Link'),
'field types' => array(
'url',
),
'settings' => array(
'trim_length' => 80,
'nofollow' => FALSE,
),
);
return $info;
}
/**
* Implements hook_field_formatter_settings_form().
*/
function url_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$element['trim_length'] = array(
'#type' => 'textfield',
'#title' => t('Trim the link text to certain number of characters'),
'#description' => t('To leave long link text alone, leave this blank.'),
'#default_value' => $settings['trim_length'],
'#size' => 10,
'#element_validate' => array(
'element_validate_integer_positive',
),
);
$element['nofollow'] = array(
'#type' => 'checkbox',
'#title' => t('Add rel="nofollow" to all links'),
'#default_value' => $settings['nofollow'],
);
return $element;
}
/**
* Implements hook_field_formatter_settings_form().
*/
function url_field_formatter_settings_summary($field, $instance, $view_mode) {
$display = $instance['display'][$view_mode];
$settings = $display['settings'];
$summary = array();
if (!empty($settings['trim_length'])) {
$summary[] = t('Link text trimmed to @char characters.', array(
'@char' => $settings['trim_length'],
));
}
else {
$summary[] = t('Link text not trimmed.');
}
if (!empty($settings['nofollow'])) {
$summary[] = t('Add rel="nofollow"');
}
return implode('<br />', $summary);
}
/**
* Implements hook_field_formatter_prepare_view().
*/
function url_field_formatter_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items, $displays) {
foreach ($entities as $id => $entity) {
$settings = $displays[$id]['settings'];
foreach ($items[$id] as $delta => &$item) {
// If the formatter is configured to add rel="nofollow" to links, then
// add them here.
if (!empty($settings['nofollow'])) {
$item['options']['attributes']['rel'] = 'nofollow';
}
}
}
}
/**
* Implements hook_field_formatter_view().
*/
function url_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
$intance_settings = $instance['settings'];
$settings = $display['settings'];
foreach ($items as $delta => $item) {
// By default use the full URL as the link title.
$link_title = $item['value'];
// If the title field value is available, use it for the link title.
if (!empty($item['title'])) {
// Unsanitizied token replacement here because $options['HTML'] is FALSE
// by default in theme_link().
$link_title = token_replace($item['title'], array(
$entity_type => $entity,
), array(
'sanitize' => FALSE,
'clear' => TRUE,
));
}
// Trim the link title to the desired length.
if (!empty($settings['trim_length'])) {
$link_title = truncate_utf8($link_title, $settings['trim_length'], FALSE, TRUE);
}
$element[$delta] = array(
'#type' => 'link',
'#title' => $link_title,
'#href' => $item['path'],
'#options' => $item['options'],
);
}
return $element;
}
/**
* Return the HTML title from an URL.
*
* @param string $url
* An URL to request with drupal_http_request().
*
* @return string
* The HTML title of the URL if found.
*/
function url_fetch_title($url) {
$request = drupal_http_request($url);
if (empty($request->error) && $request->code == 200 && !empty($request->data)) {
if (preg_match('!<title>(.*?)</title>!iu', $request->data, $matches)) {
// Title tags should be encoded, but we want the raw value.
return decode_entities($matches[1]);
}
}
}
/**
* This is a copy of the form_validate_url() function from Drupal 8.
*
* Note that #maxlength and #required is validated by _form_validate() already.
*/
function url_validate_url(&$element, &$form_state) {
$value = trim($element['#value']);
form_set_value($element, $value, $form_state);
if ($value !== '' && !valid_url($value, TRUE)) {
form_error($element, t('The URL %url is not valid.', array(
'%url' => $value,
)));
}
}