linked_field.module in Linked Field 7
Same filename and directory in other branches
Main file of Linked Field module.
File
linked_field.moduleView source
<?php
/**
* @file
* Main file of Linked Field module.
*/
/**
* Implements hook_help().
*/
function linked_field_help($path, $arg) {
switch ($path) {
case 'admin/reports/fields/linked-fields':
return '<p>' . t('This list shows all fields which are linked via !linked-field module.', array(
'!linked-field' => l('Linked Field', 'https://drupal.org/project/linked_field'),
)) . '</p>';
}
}
/**
* Implements hook_permission().
*/
function linked_field_permission() {
return array(
'access linked field list' => array(
'title' => t('Access linked field list'),
'description' => user_access('access linked field list') ? t('Get an overview of <a href="@url">all linked fields</a>.', array(
'@url' => url('admin/reports/fields/linked-fields'),
)) : t('Get an overview of all linked fields.'),
),
);
}
/**
* Implements hook_menu().
*/
function linked_field_menu() {
$items = array();
$items['admin/reports/fields/linked-fields'] = array(
'title' => 'Linked fields',
'description' => 'Overview of all linked fields.',
'page callback' => 'linked_field_field_list',
'type' => MENU_LOCAL_TASK,
'access callback' => 'user_access',
'access arguments' => array(
'access linked field list',
),
'file' => 'linked_field.admin.inc',
'weight' => 0,
);
return $items;
}
/**
* Implements hook_token_info().
*
* Provides tokens for the current field item.
*/
function linked_field_token_info() {
$info = array();
$info['types']['field-item'] = array(
'name' => t('Field item'),
'description' => t('Tokens related to delta value of field.'),
'needs-data' => 'field-item',
);
$info['tokens']['field-item']['delta'] = array(
'name' => t('Field item delta'),
'description' => t('The delta of this field item.'),
);
$info['tokens']['field-item']['raw'] = array(
'name' => t('Raw item value'),
'description' => t('The raw value of this field item.'),
);
return $info;
}
/**
* Implements hook_tokens().
*
* Replace our tokens that we provide in linked_field_token_info().
*/
function linked_field_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'field-item') {
foreach ($tokens as $name => $original) {
$replacements[$original] = $data['field-item'][$name];
}
}
return $replacements;
}
/**
* Implements hook_menu_alter().
*/
function linked_field_menu_alter(&$items) {
$items['admin/reports/fields/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
}
/**
* Implements hook_field_formatter_info_alter().
*/
function linked_field_field_formatter_info_alter(&$infos) {
foreach ($infos as &$info) {
// Add a settings array if no settings were found.
if (!isset($info['settings']) || !is_array($info['settings'])) {
$info['settings'] = array();
}
$info['settings'] += array(
'linked_field' => array(
'linked' => 0,
'destination' => '',
'advanced' => array(
'title' => '',
'target' => '',
'class' => '',
'rel' => '',
'text' => '',
),
),
);
}
}
/**
* Implements hook_field_formatter_settings_summary_alter().
*/
function linked_field_field_formatter_settings_summary_alter(&$summary, $context) {
$display = $context['instance']['display'][$context['view_mode']];
$settings = $display['settings'];
// Break when no linked field settings were found.
if (empty($settings['linked_field']['linked'])) {
// We have to put something in the summary so that we can ever
// set Linked Field settings.
$summary .= ' ';
return;
}
// Normalize the settings.
$linked = $settings['linked_field']['linked'];
$destination = $settings['linked_field']['destination'];
$title = $settings['linked_field']['advanced']['title'];
$target = $settings['linked_field']['advanced']['target'];
$class = $settings['linked_field']['advanced']['class'];
$rel = $settings['linked_field']['advanced']['rel'];
$text = $settings['linked_field']['advanced']['text'];
$summary_items = array();
if ($destination) {
$summary_items[] = t('Destination: @destination', array(
'@destination' => $destination,
));
}
if ($title) {
$summary_items[] = t('Title: @title', array(
'@title' => $title,
));
}
if ($target) {
$summary_items[] = t('Target: <code>@target</code>', array(
'@target' => $target,
));
}
if ($class) {
$summary_items[] = t('Class: <code>@class</code>', array(
'@class' => $class,
));
}
if ($rel) {
$summary_items[] = t('Relationship: <code>@rel</code>', array(
'@rel' => $rel,
));
}
if ($text) {
$summary_items[] = t('Text: @text', array(
'@text' => $text,
));
}
if ($linked) {
$summary .= theme('item_list', array(
'items' => $summary_items,
'title' => 'Linked Field',
));
}
else {
$summary .= ' ';
}
}
/**
* Implements hook_field_formatter_settings_form_alter().
*/
function linked_field_field_formatter_settings_form_alter(&$settings_form, $context) {
$field = $context['field'];
$entity_type = $context['instance']['entity_type'];
$display = $context['instance']['display'][$context['view_mode']];
$settings = $display['settings'];
$settings_form['linked_field'] = array(
'#type' => 'container',
'#attributes' => array(
'class' => array(
'linked-field-linked-wrapper',
),
),
'#attached' => array(
'css' => array(
drupal_get_path('module', 'linked_field') . '/css/linked-field.css',
),
),
);
$settings_form['linked_field']['linked'] = array(
'#title' => t('Link this field'),
'#type' => 'checkbox',
'#default_value' => $settings['linked_field']['linked'],
);
$settings_form['linked_field']['destination'] = array(
'#title' => t('Destination'),
'#type' => 'textfield',
'#default_value' => $settings['linked_field']['destination'],
'#description' => t('Enter the link destination here. If you need a dynamic destination you can set it via the <a href="@alter-hook">alter hook</a>.', array(
'@alter-hook' => 'http://cgit.drupalcode.org/linked_field/tree/linked_field.api.php',
)),
'#states' => array(
'visible' => array(
'input[name$="[settings][linked_field][linked]"]' => array(
'checked' => TRUE,
),
),
),
);
$settings_form['linked_field']['advanced'] = array(
'#title' => t('Advanced'),
'#type' => 'fieldset',
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#states' => array(
'visible' => array(
'input[name$="[settings][linked_field][linked]"]' => array(
'checked' => TRUE,
),
),
),
);
$settings_form['linked_field']['advanced']['title'] = array(
'#title' => t('Title'),
'#type' => 'textfield',
'#default_value' => $settings['linked_field']['advanced']['title'],
'#description' => t('This value will be used as <em>title</em> attribute.'),
);
$settings_form['linked_field']['advanced']['target'] = array(
'#title' => t('Target'),
'#type' => 'textfield',
'#default_value' => $settings['linked_field']['advanced']['target'],
'#description' => t('This value will be used as <em>target</em> attribute.'),
);
$settings_form['linked_field']['advanced']['class'] = array(
'#title' => t('Class'),
'#type' => 'textfield',
'#default_value' => $settings['linked_field']['advanced']['class'],
'#description' => t('This value will be used as <em>class</em> attribute.'),
);
$settings_form['linked_field']['advanced']['rel'] = array(
'#title' => t('Relationship'),
'#type' => 'textfield',
'#default_value' => $settings['linked_field']['advanced']['rel'],
'#description' => t('This value will be used as <em>rel</em> attribute.'),
);
$settings_form['linked_field']['advanced']['text'] = array(
'#title' => t('Text'),
'#type' => 'textfield',
'#default_value' => $settings['linked_field']['advanced']['text'],
'#description' => t('This value will be used as link text and overrides the actual field output.'),
);
$settings_form['linked_field']['token'] = array(
'#states' => array(
'visible' => array(
'input[name$="[settings][linked_field][linked]"]' => array(
'checked' => TRUE,
),
),
),
);
// Display a token browser and show a warning to the user if Token
// module is not installed.
if (module_exists('token')) {
$settings_form['linked_field']['token'] += array(
'#type' => 'container',
'#theme' => 'token_tree',
'#token_types' => 'all',
'#dialog' => TRUE,
);
}
else {
$field_name = drupal_clean_css_identifier($field['field_name']);
$settings_form['linked_field']['token']['#prefix'] = '<div class="form-item" id="edit-fields-' . $field_name . '-settings-edit-form-settings-linked-field-token">';
$settings_form['linked_field']['token']['#prefix'] .= '<div style="display: inline-block;" class="messages warning">';
$settings_form['linked_field']['token']['#suffix'] = '</div></div>';
$settings_form['linked_field']['token']['#markup'] = t('Install the <a href="@url">Token</a> module to browse available tokens.', array(
'@url' => url('https://www.drupal.org/project/token'),
));
}
}
/**
* Implements hook_field_attach_view_alter().
*/
function linked_field_field_attach_view_alter(&$output, $context) {
foreach (element_children($output) as $field_name) {
$element = $output[$field_name];
if (empty($element['#entity_type']) || empty($element['#bundle'])) {
continue;
}
$view_modes = field_view_mode_settings($element['#entity_type'], $element['#bundle']);
// Check whether set view mode has custom settings.
if (isset($view_modes[$context['view_mode']]) && !$view_modes[$context['view_mode']]['custom_settings']) {
$context['view_mode'] = 'default';
}
$instance = field_info_instance($element['#entity_type'], $field_name, $element['#bundle']);
// Check whether view mode exists in the field instance.
if (isset($instance['display'][$context['view_mode']])) {
$display = $instance['display'][$context['view_mode']];
}
elseif ($context['view_mode'] == '_custom' && is_array($context['display'])) {
$display = $context['display'];
}
else {
$display = $instance['display']['default'];
}
$settings = $display['settings'];
// We need special behavior for Display Suite fields.
if ($element['#field_type'] == 'ds') {
$field_settings = ds_get_field_settings($element['#entity_type'], $element['#bundle'], $element['#view_mode']);
$settings = isset($field_settings[$element['#field_name']]['formatter_settings']) ? $field_settings[$element['#field_name']]['formatter_settings'] : array();
}
// Continue to next if no Linked Field settings were found.
if (empty($settings['linked_field']['linked'])) {
continue;
}
// Some entities don't have a language so we need a fallback.
$language = isset($element['#object']->language) ? $element['#object']->language : NULL;
// We need special behavior for Display Suite fields.
// @TODO: Where does this come from?
if ($element['#field_type'] != 'ds') {
$field_items = field_get_items($element['#entity_type'], $element['#object'], $field_name, $language);
}
foreach (element_children($element) as $delta) {
// Parse the destination to get queries and fragments working.
$destination_parsed = drupal_parse_url($settings['linked_field']['destination']);
// Generate a correct link.
$path = $destination_parsed['path'];
unset($destination_parsed['path']);
// Building an array which looks like the parameters you pass to l().
$linked_field_settings = array(
'linked' => $settings['linked_field']['linked'],
'text' => $settings['linked_field']['advanced']['text'],
'path' => $path,
'options' => array(
'attributes' => array(
'title' => $settings['linked_field']['advanced']['title'],
'target' => $settings['linked_field']['advanced']['target'],
'class' => $settings['linked_field']['advanced']['class'],
'rel' => $settings['linked_field']['advanced']['rel'],
),
) + $destination_parsed,
);
// Let other modules alter all the settings.
drupal_alter('linked_field_settings', $linked_field_settings, $context);
// If there is no destination or Linked Field is disabled continue.
if (!$linked_field_settings['path'] || !$linked_field_settings['linked'] && $linked_field_settings['path']) {
continue;
}
$replace_tokens = array(
$element['#entity_type'] => $element['#object'],
);
// We need special behavior for Display Suite fields.
// @TODO: Where does this come from?
if (isset($field_items[$delta])) {
// Get raw value of field.
$field_raw_value = array_shift($field_items[$delta]);
// Replace tokens and filter the value. The field tokens have to be
// first in the array so they are processed first.
$replace_tokens = array(
'field-item' => array(
'delta' => $delta,
'raw' => $field_raw_value,
),
) + $replace_tokens;
}
$options = array();
if ($context['language'] != LANGUAGE_NONE) {
$languages = language_list();
if (isset($languages[$context['language']])) {
$options['language'] = $languages[$context['language']];
}
}
/*
* Replace all field tokens and then entity tokens. This allows field
* tokens to be nested such as [comment:field-tags:[field:delta]:name].
*/
foreach ($replace_tokens as $type => $data) {
$replace_token = array(
$type => $data,
);
if ($type == 'field-item') {
// The tokens should not be cleared when replacing field item tokens.
$options['clear'] = FALSE;
}
else {
$options['clear'] = TRUE;
}
$linked_field_settings['path'] = token_replace($linked_field_settings['path'], $replace_token, $options);
$linked_field_settings['options']['attributes']['title'] = $linked_field_settings['options']['attributes']['title'] ? filter_xss_admin(token_replace($linked_field_settings['options']['attributes']['title'], $replace_token, $options)) : '';
$linked_field_settings['options']['attributes']['target'] = $linked_field_settings['options']['attributes']['target'] ? check_plain(token_replace($linked_field_settings['options']['attributes']['target'], $replace_token, $options)) : '';
$linked_field_settings['options']['attributes']['class'] = $linked_field_settings['options']['attributes']['class'] ? check_plain(token_replace($linked_field_settings['options']['attributes']['class'], $replace_token, $options)) : '';
$linked_field_settings['options']['attributes']['rel'] = $linked_field_settings['options']['attributes']['rel'] ? check_plain(token_replace($linked_field_settings['options']['attributes']['rel'], $replace_token, $options)) : '';
// Would be better to have own set with allowed tags so that only
// inline elements are allowed.
$linked_field_settings['text'] = $linked_field_settings['text'] ? filter_xss_admin(token_replace($linked_field_settings['text'], $replace_token, $options)) : '';
}
// Continue to next field if destination is empty.
if (!$linked_field_settings['path']) {
continue;
}
// We need the destination as link attribute so let's move it.
$linked_field_settings['options']['attributes']['href'] = url($linked_field_settings['path'], $linked_field_settings['options']);
unset($linked_field_settings['path']);
if (!$linked_field_settings['text']) {
$rendered = drupal_render($element[$delta]);
}
else {
$rendered = $linked_field_settings['text'];
}
// Convert HTML code to a DOMDocument object.
$html_dom = filter_dom_load($rendered);
// Getting the <body> element.
$body = $html_dom
->getElementsByTagName('body');
$nodes = $body
->item(0);
// Recursively walk over the DOMDocument body and place the links.
linked_field_link_field($nodes, $html_dom, $linked_field_settings['options']['attributes']);
// Converting the DOMDocument object back to HTML code.
$rendered = filter_dom_serialize($html_dom);
$output[$field_name][$delta] = array(
'#markup' => $rendered,
);
}
}
}
/**
* Recursive function for linking text parts and images in DOMNodes.
*
* @param DOMNode $node
* An object which gets investigated.
* @param DOMDocument $dom
* An object which represents an entire HTML or XML document.
* @param array $attributes
* An array containing element attributes.
*/
function linked_field_link_field($node, $dom, $attributes) {
// Some elements need the <a> as wrapper.
$wrapped_elements = array(
'img',
'picture',
);
if ($node
->hasChildNodes() && $node->nodeName != 'a') {
$c = $node->childNodes->length;
for ($i = $c; $i > 0; --$i) {
$child = $node->childNodes
->item($i - 1);
if (in_array($child->nodeName, $wrapped_elements)) {
// Create new <a> element, set the href and append the wrapped element.
$element = $dom
->createElement('a');
// Adding the attributes.
foreach ($attributes as $name => $value) {
if ($value) {
// Convert all HTML entities back to their applicable characters.
$value = html_entity_decode($value, ENT_QUOTES);
$element
->setAttribute($name, $value);
}
}
$node
->replaceChild($element, $child);
$element
->appendChild($child);
}
else {
linked_field_link_field($child, $dom, $attributes);
if ($child->nodeType == XML_TEXT_NODE) {
$text = $child->textContent;
if (strlen(trim($text))) {
// Convert all applicable characters to HTML entities.
$text = htmlentities($text, ENT_QUOTES, 'UTF-8');
// Create new <a> element, set the text and the href attribute.
$element = $dom
->createElement('a', $text);
// Adding the attributes.
foreach ($attributes as $name => $value) {
if ($value) {
// Convert all HTML entities back to their applicable characters.
$value = html_entity_decode($value, ENT_QUOTES);
$element
->setAttribute($name, $value);
}
}
// Replace the the original element with the new one.
$node
->replaceChild($element, $child);
}
}
}
}
}
}
Functions
Name | Description |
---|---|
linked_field_field_attach_view_alter | Implements hook_field_attach_view_alter(). |
linked_field_field_formatter_info_alter | Implements hook_field_formatter_info_alter(). |
linked_field_field_formatter_settings_form_alter | Implements hook_field_formatter_settings_form_alter(). |
linked_field_field_formatter_settings_summary_alter | Implements hook_field_formatter_settings_summary_alter(). |
linked_field_help | Implements hook_help(). |
linked_field_link_field | Recursive function for linking text parts and images in DOMNodes. |
linked_field_menu | Implements hook_menu(). |
linked_field_menu_alter | Implements hook_menu_alter(). |
linked_field_permission | Implements hook_permission(). |
linked_field_tokens | Implements hook_tokens(). |
linked_field_token_info | Implements hook_token_info(). |