Newer
Older
<?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');
dropcube
committed
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');
John C Fiala
committed
* Maximum URLs length - needs to match value in link.install.
John C Fiala
committed
*/
define('LINK_URL_MAX_LENGTH', 2048);
/**
* Implements hook_field_info().
John C Fiala
committed
'link_field' => array(
dropcube
committed
'label' => t('Link'),
'description' => t('Store a title, href, and attributes in the database to assemble a link.'),
'attributes' => _link_default_attributes(),
'url' => 0,
'title' => 'optional',
'title_value' => '',
'title_maxlength' => 128, //patch #1307788 from nmc
'enable_tokens' => 1,
'display' => array(
'url_cutoff' => 80,
),
),
'instance_settings' => array(
'attributes' => _link_default_attributes(),
'url' => 0,
'title' => 'optional',
'title_value' => '',
'title_maxlength' => 128, // patch #1307788 from nmc
'enable_tokens' => 1,
'display' => array(
'url_cutoff' => 80,
),
John C Fiala
committed
'validate_url' => 1,
John C Fiala
committed
'default_widget' => 'link_field',
John C Fiala
committed
'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'),
dropcube
committed
),
John C Fiala
committed
/**
* Implements hook_field_instance_settings_form().
John C Fiala
committed
*/
function link_field_instance_settings_form($field, $instance) {
John C Fiala
committed
$form = array(
'#element_validate' => array('link_field_settings_form_validate'),
);
Peter Philipp
committed
John C Fiala
committed
$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.'),
);
John C Fiala
committed
John C Fiala
committed
$form['url'] = array(
'#type' => 'checkbox',
'#title' => t('Optional URL'),
'#default_value' => isset($instance['settings']['url']) ? $instance['settings']['url'] : '',
John C Fiala
committed
'#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.'),
);
John C Fiala
committed
John C Fiala
committed
$title_options = array(
'optional' => t('Optional Title'),
'required' => t('Required Title'),
'value' => t('Static Title'),
'none' => t('No Title'),
);
John C Fiala
committed
John C Fiala
committed
$form['title'] = array(
'#type' => 'radios',
'#title' => t('Link Title'),
'#default_value' => isset($instance['settings']['title']) ? $instance['settings']['title'] : 'optional',
John C Fiala
committed
'#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 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.'),
);
John C Fiala
committed
John C Fiala
committed
$form['title_value'] = array(
'#type' => 'textfield',
'#title' => t('Static title'),
'#default_value' => isset($instance['settings']['title_value']) ? $instance['settings']['title_value'] : '',
John C Fiala
committed
'#description' => t('This title will always be used if “Static Title” is selected above.'),
);
John C Fiala
committed
$form['title_maxlength'] = array( // patch #1307788 from nmc
'#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,
);
John C Fiala
committed
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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."),
);
$token_type = array(
'theme' => 'token_tree',
'token_types' => array($instance['entity_type']),
'global_types' => TRUE,
'click_insert' => TRUE,
'recursion_limit' => 2,
);
$form['tokens']['help'] = array(
'#type' => 'markup',
'#markup' => theme('token_tree', $token_type),
);
$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 node edit form. This does not affect the field settings on this page.'),
);
}
John C Fiala
committed
John C Fiala
committed
$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',
John C Fiala
committed
'#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,
);
John C Fiala
committed
John C Fiala
committed
$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'],
John C Fiala
committed
'#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'],
John C Fiala
committed
'#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 automaticly'),
'#default_value' => !isset($instance['settings']['rel_remove']) ? 'default' : $instance['settings']['rel_remove'],
'#description' => t('Turn on/off if rel attribute should be removed automaticly, if user given link is internal/external'),
'#options' => $rel_remove_options,
);
John C Fiala
committed
$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'],
John C Fiala
committed
);
$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,
);
John C Fiala
committed
return $form;
}
/**
* Validate the field 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'])) {
John C Fiala
committed
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']) // patch #1307788 from nmc
&& !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'])) { // patch #1307788 from nmc
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.'));
}
John C Fiala
committed
}
function link_field_is_empty($item, $field) {
John C Fiala
committed
return empty($item['title']) && empty($item['url']);
}
/**
* Implements hook_field_load().
*/
John C Fiala
committed
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]);
John C Fiala
committed
}
John C Fiala
committed
* Implements hook_field_validate().
John C Fiala
committed
function link_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
$optional_field_found = FALSE;
John C Fiala
committed
if ($instance['settings']['validate_url'] !== 0 || is_null($instance['settings']['validate_url']) || !isset($instance['settings']['validate_url'])) {
foreach ($items as $delta => $value) {
Alex Bronstein
committed
_link_validate($items[$delta], $delta, $field, $entity, $instance, $langcode, $optional_field_found);
John C Fiala
committed
}
}
if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] && !$optional_field_found) {
Alex Bronstein
committed
form_set_error($field['field_name'] . '][' . $langcode . '][0][title', t('At least one title or URL must be entered.'));
John C Fiala
committed
}
}
John C Fiala
committed
/**
* Implements hook_field_presave().
*/
function link_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
foreach ($items as $delta => $value) {
_link_process($items[$delta], $delta, $field, $entity);
John C Fiala
committed
/**
* 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]);
}
}
}
John C Fiala
committed
* Implements hook_field_widget_info().
John C Fiala
committed
function link_field_widget_info() {
John C Fiala
committed
'link_field' => array(
dropcube
committed
'label' => 'Link',
John C Fiala
committed
'field types' => array('link_field'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
John C Fiala
committed
* Implements hook_field_widget_form().
John C Fiala
committed
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] : '',
Nate Lampton
committed
);
Nate Lampton
committed
}
John C Fiala
committed
/**
* Unpacks the item attributes for use.
John C Fiala
committed
*/
function _link_load($field, $item, $instance) {
/*return $item['attributes'] = isset($item['attributes']) ?
unserialize($item['attributes']) :
$instance['settings']['attributes'];*/
if (isset($item['attributes'])) {
Peter Philipp
committed
if (!is_array($item['attributes'])) {
$item['attributes'] = unserialize($item['attributes']);
}
return $item['attributes'];
}
else if (isset($instance['settings']['attributes'])) {
return $instance['settings']['attributes'];
}
else {
return $field['settings']['attributes'];
dropcube
committed
}
/**
* Prepares the item attributes and url for storage.
*/
John C Fiala
committed
function _link_process(&$item, $delta = 0, $field, $entity) {
// Trim whitespace from URL.
$item['url'] = trim($item['url']);
dropcube
committed
// if no attributes are set then make sure $item['attributes'] is an empty array - this lets $field['attributes'] override it.
if (empty($item['attributes'])) {
dropcube
committed
}
// Serialize the attributes array.
dqd
committed
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($node)) {
if (!link_validate_url($item['url'])) {
unset($item['url']);
// token from the patch http://drupal.org/files/link_query_fragment.patch @ Issue #1321482
if (!empty($item['query'])) {
$item['url'] .= '?' . http_build_query($item['query']);
}
if (!empty($item['fragment'])) {
$item['url'] .= '#' . $item['fragment'];
}
/**
* Validates that the link field has been entered properly.
*/
Alex Bronstein
committed
function _link_validate(&$item, $delta, $field, $node, $instance, $langcode, &$optional_field_found) {
John C Fiala
committed
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) {
Alex Bronstein
committed
form_set_error($field['field_name'] . '][' . $langcode . ']['. $delta .'][url', t('Not a valid URL.'));
}
// Require a title for the link if necessary.
if ($instance['settings']['title'] == 'required' && strlen(trim($item['title'])) == 0) {
Alex Bronstein
committed
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) {
Alex Bronstein
committed
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;
John C Fiala
committed
// Require entire field
if ($instance['settings']['url'] === 'optional'
&& $instance['settings']['title'] === 'optional'
&& $instance['required'] == 1
&& !$optional_field_found
&& isset($instance['id'])) {
Alex Bronstein
committed
form_set_error($instance['field_name'] . '][' . $langcode . '][0][title',
John C Fiala
committed
t('At least one title or URL must be entered.'));
}
}
/**
* Cleanup user-entered values for a link field according to field settings.
dropcube
committed
*
* @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 $node
* The node containing this link.
function _link_sanitize(&$item, $delta, &$field, $instance, &$node) {
dropcube
committed
// Don't try to process empty links.
if (empty($item['url']) && empty($item['title'])) {
return;
}
John C Fiala
committed
if (isset($instance['settings']['enable_tokens']) && $instance['settings']['enable_tokens']) {
John C Fiala
committed
global $user;
dropcube
committed
// Load the node if necessary for nodes in views.
$token_node = isset($node->nid) ? node_load($node->nid) : $node;
$item['url'] = token_replace($item['url'], array('node' => $token_node));
}
$type = link_validate_url($item['url']);
John C Fiala
committed
// If we can't determine the type of url, and we've been told not to validate it,
// then we assume it's a LINK_EXTERNAL type for later processing. #357604
if ($type == FALSE && $instance['settings']['validate_url'] === 0) {
$type = LINK_EXTERNAL;
}
$url = link_cleanup_url($item['url']);
John C Fiala
committed
dropcube
committed
// Separate out the anchor if any.
if (strpos($url, '#') !== FALSE) {
$item['fragment'] = substr($url, strpos($url, '#') + 1);
$url = substr($url, 0, strpos($url, '#'));
dropcube
committed
// Separate out the query string if any.
if (strpos($url, '?') !== FALSE) {
$query = substr($url, strpos($url, '?') + 1);
parse_str($query, $query_array);
$item['query'] = $query_array;
$url = substr($url, 0, strpos($url, '?'));
}
John C Fiala
committed
$item['url'] = check_plain($url);
// Create a shortened URL for display.
$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));
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'];
}
// Use the title defined by the user at the widget level.
else if (isset($item['title'])) {
$title = $item['title'];
}
else {
$title = '';
}
John C Fiala
committed
if ($title && ($instance['settings']['title'] == 'value' || $instance['settings']['enable_tokens'])) {
dropcube
committed
// Load the node if necessary for nodes in views.
$token_node = isset($node->nid) ? node_load($node->nid) : $node;
$title = filter_xss(token_replace($title, array('node' => $token_node)),
array('b', 'br', 'code', 'em', 'i', 'img', 'span', 'strong', 'sub', 'sup', 'tt', 'u'));
dropcube
committed
$item['html'] = TRUE;
$item['title'] = empty($title) ? $item['display_url'] : $title;
dropcube
committed
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();
}
dropcube
committed
// Merge item attributes with attributes defined at the field level.
$item['attributes'] += $instance['settings']['attributes'];
dropcube
committed
// 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'];
dropcube
committed
}
// Remove the target attribute if the default (no target) is selected.
if (empty($item['attributes']) || $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']);
}
dropcube
committed
// Handle "title" link attribute.
if (!empty($item['attributes']['title']) && module_exists('token')) {
// Load the node (necessary for nodes in views).
$token_node = isset($node->nid) ? node_load($node->nid) : $node;
$item['attributes']['title'] = filter_xss(token_replace($item['attributes']['title'], array('node' => $token_node)),
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']);
dropcube
committed
// Remove empty attributes.
$item['attributes'] = array_filter($item['attributes']);
John C Fiala
committed
// Sets title to trimmed url if one exists
// @TODO: Do we need this? It seems not.
/*if(!empty($item['display_url']) && empty($item['title'])) {
John C Fiala
committed
$item['title'] = $item['display_url'];
}
elseif(!isset($item['title'])) {
$item['title'] = $item['url'];
John C Fiala
committed
}
/**
John C Fiala
committed
* Implements hook_theme().
function link_theme() {
return array(
John C Fiala
committed
/*'link_field_settings' => array(
'variables' => array('element' => NULL),
),*/
John C Fiala
committed
'link_formatter_link_default' => array(
John C Fiala
committed
'variables' => array('element' => NULL),
John C Fiala
committed
'link_formatter_link_plain' => array(
John C Fiala
committed
'variables' => array('element' => NULL),
'link_formatter_link_absolute' => array(
'variables' => array('element' => NULL),
),
John C Fiala
committed
'link_formatter_link_title_plain' => array(
'variables' => array('element' => NULL),
),
John C Fiala
committed
'link_formatter_link_url' => array(
John C Fiala
committed
'variables' => array('element' => NULL),
dropcube
committed
),
John C Fiala
committed
'link_formatter_link_short' => array(
John C Fiala
committed
'variables' => array('element' => NULL),
John C Fiala
committed
'link_formatter_link_label' => array(
John C Fiala
committed
'variables' => array('element' => NULL),
John C Fiala
committed
'link_formatter_link_separate' => array(
John C Fiala
committed
'variables' => array('element' => NULL),
dropcube
committed
),
John C Fiala
committed
'link_field' => array(
John C Fiala
committed
'render element' => 'element',
),
);
}
/**
* FAPI theme for an individual text elements.
*/
John C Fiala
committed
function theme_link_field($vars) {
drupal_add_css(drupal_get_path('module', 'link') .'/link.css');
John C Fiala
committed
$element = $vars['element'];
dropcube
committed
// Prefix single value link fields with the name of the field.
if (empty($element['#field']['multiple'])) {
John C Fiala
committed
if (isset($element['url']) && !isset($element['title'])) {
unset($element['url']['#title']);
dropcube
committed
}
$output = '';
John C Fiala
committed
$output .= '<div class="link-field-subrow clearfix">';
dropcube
committed
if (isset($element['title'])) {
John C Fiala
committed
$output .= '<div class="link-field-title link-field-column">'. drupal_render($element['title']) .'</div>';
John C Fiala
committed
$output .= '<div class="link-field-url'. (isset($element['title']) ? ' link-field-column' : '') .'">'. drupal_render($element['url']) .'</div>';
$output .= '</div>';
dropcube
committed
if (!empty($element['attributes']['target'])) {
John C Fiala
committed
$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;
Nate Lampton
committed
}
dropcube
committed
/**
John C Fiala
committed
* Implements hook_element_info().
John C Fiala
committed
function link_element_info() {
John C Fiala
committed
$elements['link_field'] = array(
John C Fiala
committed
'#process' => array('link_field_process'),
John C Fiala
committed
'#theme' => 'link_field',
'#theme_wrappers' => array('form_element'),
);
return $elements;
}
dropcube
committed
function _link_default_attributes() {
return array(
'target' => LINK_TARGET_DEFAULT,
'class' => '',
'rel' => '',
);
}
/**
* Process 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) {
John C Fiala
committed
$instance = field_widget_instance($element, $form_state);
$settings = $instance['settings'];
John C Fiala
committed
$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'], // patch #1307788 from nmc
John C Fiala
committed
'#title' => t('Title'),
dqd
committed
'#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, // davereids patch from jan 2011
John C Fiala
committed
'#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.
John C Fiala
committed
$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,
John C Fiala
committed
'#default_value' => isset($attributes['target']) ? $attributes['target'] : FALSE,
John C Fiala
committed
);
}
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' => '"',
);
}
// 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; // davereids patch from jan 2011
John C Fiala
committed
return $element;
Nate Lampton
committed
* Implementation of hook_field_formatter_info().
*/
function link_field_formatter_info() {
return array(
John C Fiala
committed
'link_default' => array(
dropcube
committed
'label' => t('Title, as link (default)'),
John C Fiala
committed
'field types' => array('link_field'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
dropcube
committed
),
John C Fiala
committed
'link_title_plain' => array(
'label' => t('Title, as plain text'),
'field types' => array('link_field'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
),
John C Fiala
committed
'link_url' => array(
dropcube
committed
'label' => t('URL, as link'),
John C Fiala
committed
'field types' => array('link_field'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
Nate Lampton
committed
),
John C Fiala
committed
'link_plain' => array(
dropcube
committed
'label' => t('URL, as plain text'),
John C Fiala
committed
'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,
),
John C Fiala
committed
'link_short' => array(
'label' => t('Short, as link with title "Link"'),
John C Fiala
committed
'field types' => array('link_field'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
John C Fiala
committed
'link_label' => array(
'label' => t('Label, as link with label as title'),
John C Fiala
committed
'field types' => array('link_field'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
Nate Lampton
committed
),
John C Fiala
committed
'link_separate' => array(
dropcube
committed
'label' => t('Separate title and URL'),
John C Fiala
committed
'field types' => array('link_field'),
'multiple values' => FIELD_BEHAVIOR_DEFAULT,
dropcube
committed
),
Nate Lampton
committed
);
}
/**
* Implements hook_field_formatter_view().
*/
John C Fiala
committed
function link_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$elements = array();
foreach ($items as $delta => $item) {
$elements[$delta] = array(
John C Fiala
committed
'#markup' => theme('link_formatter_'. $display['type'], array('element' => $item, 'field' => $instance)),
John C Fiala
committed
);
}
return $elements;
}
Nate Lampton
committed
/**
* Theme function for 'default' text field formatter.
John C Fiala
committed
function theme_link_formatter_link_default($vars) {
$link_options = $vars['element'];
unset($link_options['element']['title']);
unset($link_options['element']['url']);
Peter Philipp
committed
dqd
committed
// Issue #1199806 by ss81: Fixes fatal error when the link URl is equal to page URL
if (isset($link_options['attributes']['class'])) {
$link_options['attributes']['class'] = array($link_options['attributes']['class']);
}
Peter Philipp
committed
// 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']);
John C Fiala
committed
}
elseif (!empty($vars['element']['url'])) {
return l($vars['element']['title'], $vars['element']['url'], $link_options);
/**
* Theme function for 'plain' text field formatter.
*/
John C Fiala
committed
function theme_link_formatter_link_plain($vars) {
$link_options = $vars['element'];
unset($link_options['element']['title']);
unset($link_options['element']['url']);
return empty($vars['element']['url']) ?
check_plain($vars['element']['title']) :
url($vars['element']['url'], $link_options);
dropcube
committed
}
/**
* Theme function for 'absolute' text field formatter.
*/
function theme_link_formatter_link_absolute($vars) {
$absolute = array('absolute' => TRUE);
return empty($vars['element']['url']) ? '' : url($vars['element']['url'], $absolute + $vars['element']);
}
John C Fiala
committed
/**
* Theme function for 'title_plain' text field formatter.
*/
function theme_link_formatter_link_title_plain($vars) {
return empty($vars['element']['title']) ? '' : check_plain($vars['element']['title']);
}
dropcube
committed
/**
* Theme function for 'url' text field formatter.
*/
John C Fiala
committed
function theme_link_formatter_link_url($vars) {
$link_options = $vars['element'];
unset($link_options['element']['title']);
unset($link_options['element']['url']);
return $vars['element']['url'] ? l($vars['element']['display_url'], $vars['element']['url'], $link_options) : '';
/**
* Theme function for 'short' text field formatter.
*/
John C Fiala
committed
function theme_link_formatter_link_short($vars) {
$link_options = $vars['element'];
unset($link_options['element']['title']);
unset($link_options['element']['url']);
return $vars['element']['url'] ? l(t('Link'), $vars['element']['url'], $link_options) : '';
dropcube
committed
* Theme function for 'label' text field formatter.
John C Fiala
committed
function theme_link_formatter_link_label($vars) {
$link_options = $vars['element'];
unset($link_options['element']['title']);
unset($link_options['element']['url']);
return $vars['element']['url'] ? l($vars['field']['label'], $vars['element']['url'], $link_options) : '';
dropcube
committed
dropcube
committed
* Theme function for 'separate' text field formatter.
John C Fiala
committed
John C Fiala
committed
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['element']['title']);
unset($link_options['element']['url']);
$title = empty($vars['element']['title']) ? '' : check_plain($vars['element']['title']);
Peter Philipp
committed
/**
* @TODO static html markup looks not very elegant
* needs smarter output solution and an optional title/url seperator
dropcube
committed
$output = '';
$output .= '<div class="link-item '. $class .'">';
if (!empty($title)) {
$output .= '<div class="link-title">'. $title .'</div>';
$output .= '<div class="link-url">'. l($vars['element']['url'], $vars['element']['url'], $link_options) .'</div>';
dropcube
committed
$output .= '</div>';
return $output;
}
John C Fiala
committed
dropcube
committed
function link_token_list($type = 'all') {
John C Fiala
committed
if ($type === 'field' || $type === 'all') {
dropcube
committed
$tokens = array();
dropcube
committed
$tokens['link']['url'] = t("Link URL");
$tokens['link']['title'] = t("Link title");
$tokens['link']['view'] = t("Formatted html link");
dropcube
committed
return $tokens;
}
}
dropcube
committed
function link_token_values($type, $object = NULL) {
John C Fiala
committed
if ($type === 'field') {
dropcube
committed
$item = $object[0];
dropcube
committed
$tokens['url'] = $item['url'];
$tokens['title'] = $item['title'];
$tokens['view'] = isset($item['view']) ? $item['view'] : '';
dropcube
committed
return $tokens;
}
}
/**
John C Fiala
committed
* Implements hook_views_api().
dropcube
committed
function link_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'link') .'/views',
);
/**
* Implements hook_migrate_api().
*/
function link_migrate_api() {
return array('api' => 2);
}
Nate Lampton
committed
/**
* 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
Nate Lampton
committed
*/
function link_cleanup_url($url, $protocol = "http") {
Nate Lampton
committed
$url = trim($url);
$type = link_validate_url($url);
John C Fiala
committed
if ($type === LINK_EXTERNAL) {
// Check if there is no protocol specified.
dropcube
committed
$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.
dropcube
committed
$domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)('. LINK_DOMAINS .'|[a-z]{2}))/i', $url);
if (!empty($domain_match)) {
dropcube
committed
$url = $protocol ."://". $url;
Nate Lampton
committed
}
}
return $url;
Nate Lampton
committed
}
/**
* A lenient verification for URLs. Accepts all URLs following RFC 1738 standard
* for URL formation and all email addresses following the RFC 2368 standard for
* mailto address formation.
Nate Lampton
committed
*
* @param string $text
* @return mixed Returns boolean FALSE if the URL is not valid. On success, returns an object with
* the following attributes: protocol, hostname, ip, and port.
*/
function link_validate_url($text) {
$LINK_ICHARS_DOMAIN = (string) html_entity_decode(implode("", array( // @TODO completing letters ...
"æ", // æ
"Æ", // Æ
"À", // À
"à", // à
"Á", // Á
"á", // á
"Â", // Â
"â", // â
"å", // å
"Å", // Å
"ä", // ä
"Ä", // Ä
"Ç", // Ç
"ç", // ç
"Ð", // Ð
"ð", // ð
"È", // È
"è", // è
"É", // É
"é", // é
"Ê", // Ê
"ê", // ê
"Ë", // Ë
"ë", // ë
"Î", // Î
"î", // î
"Ï", // Ï
"ï", // ï
"ø", // ø
"Ø", // Ø
"ö", // ö
"Ö", // Ö
"Ô", // Ô
"ô", // ô
"Õ", // Õ