Projet

Général

Profil

Paste
Télécharger (64,4 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / link / link.module @ 8e7483ab

1 85ad3d82 Assos Assos
<?php
2
3
/**
4
 * @file
5
 * Defines simple link field types.
6
 */
7
8
define('LINK_EXTERNAL', 'external');
9
define('LINK_INTERNAL', 'internal');
10
define('LINK_FRONT', 'front');
11 8e7483ab Assos Assos
define('LINK_FRAGMENT', 'fragment');
12
define('LINK_QUERY', 'query');
13 85ad3d82 Assos Assos
define('LINK_EMAIL', 'email');
14 8e7483ab Assos Assos
define('LINK_TEL', 'tel');
15 85ad3d82 Assos Assos
define('LINK_NEWS', 'news');
16 8e7483ab Assos Assos
define('LINK_FILE', 'file');
17 85ad3d82 Assos Assos
define('LINK_TARGET_DEFAULT', 'default');
18
define('LINK_TARGET_NEW_WINDOW', '_blank');
19
define('LINK_TARGET_TOP', '_top');
20
define('LINK_TARGET_USER', 'user');
21
22
/**
23
 * Maximum URLs length - needs to match value in link.install.
24
 */
25
define('LINK_URL_MAX_LENGTH', 2048);
26
27 39a181a4 Assos Assos
/**
28
 * Implements hook_help().
29
 */
30
function link_help($path, $arg) {
31
  switch ($path) {
32
    case 'admin/help#link':
33 8e7483ab Assos Assos
      $output = '<p><strong>' . t('About') . '</strong></p>';
34
      $output .= '<p>' . t('The link provides a standard custom content field for links. Links can be easily added to any content types and profiles and include advanced validating and different ways of storing internal or external links and URLs. It also supports additional link text title, site wide tokens for titles and title attributes, target attributes, css class attribution, static repeating values, input conversion, and many more.') . '</p>';
35
      $output .= '<p><strong>' . t('Requirements / Dependencies') . '</strong></p>';
36 39a181a4 Assos Assos
      $output .= '<p>' . 'Fields API is provided already by core [no dependencies].' . '</p>';
37
      $output .= '<p><strong>Configuration</strong></p>';
38
      $output .= '<p>' . 'Configuration is only slightly more complicated than a text field. Link text titles for URLs can be made required, set as instead of URL, optional (default), or left out entirely. If no link text title is provided, the trimmed version of the complete URL will be displayed. The target attribute should be set to "_blank", "top", or left out completely (checkboxes provide info). The rel=nofollow attribute prevents the link from being followed by certain search engines.' . '</p>';
39
      return $output;
40
  }
41
}
42
43 85ad3d82 Assos Assos
/**
44
 * Implements hook_field_info().
45
 */
46
function link_field_info() {
47
  return array(
48
    'link_field' => array(
49
      'label' => t('Link'),
50
      'description' => t('Store a title, href, and attributes in the database to assemble a link.'),
51
      'settings' => array(
52
        'attributes' => _link_default_attributes(),
53
        'url' => 0,
54
        'title' => 'optional',
55
        'title_value' => '',
56
        'title_maxlength' => 128,
57
        'enable_tokens' => 1,
58
        'display' => array(
59
          'url_cutoff' => 80,
60
        ),
61
      ),
62
      'instance_settings' => array(
63
        'attributes' => _link_default_attributes(),
64
        'url' => 0,
65
        'title' => 'optional',
66
        'title_value' => '',
67 c8740e19 Assos Assos
        'title_label_use_field_label' => FALSE,
68 85ad3d82 Assos Assos
        'title_maxlength' => 128,
69
        'enable_tokens' => 1,
70
        'display' => array(
71
          'url_cutoff' => 80,
72
        ),
73
        'validate_url' => 1,
74 c8740e19 Assos Assos
        'absolute_url' => 1,
75 85ad3d82 Assos Assos
      ),
76
      'default_widget' => 'link_field',
77
      'default_formatter' => 'link_default',
78
      // Support hook_entity_property_info() from contrib "Entity API".
79
      'property_type' => 'field_item_link',
80
      'property_callbacks' => array('link_field_property_info_callback'),
81
    ),
82
  );
83
}
84
85
/**
86
 * Implements hook_field_instance_settings_form().
87
 */
88
function link_field_instance_settings_form($field, $instance) {
89
  $form = array(
90
    '#element_validate' => array('link_field_settings_form_validate'),
91
  );
92
93 c8740e19 Assos Assos
  $form['absolute_url'] = array(
94
    '#type' => 'checkbox',
95
    '#title' => t('Absolute URL'),
96
    '#default_value' => isset($instance['settings']['absolute_url']) && ($instance['settings']['absolute_url'] !== '') ? $instance['settings']['absolute_url'] : TRUE,
97
    '#description' => t('If checked, the URL will always render as an absolute URL.'),
98
  );
99
100 85ad3d82 Assos Assos
  $form['validate_url'] = array(
101
    '#type' => 'checkbox',
102
    '#title' => t('Validate URL'),
103
    '#default_value' => isset($instance['settings']['validate_url']) && ($instance['settings']['validate_url'] !== '') ? $instance['settings']['validate_url'] : TRUE,
104
    '#description' => t('If checked, the URL field will be verified as a valid URL during validation.'),
105
  );
106
107
  $form['url'] = array(
108
    '#type' => 'checkbox',
109
    '#title' => t('Optional URL'),
110
    '#default_value' => isset($instance['settings']['url']) ? $instance['settings']['url'] : '',
111
    '#return_value' => 'optional',
112
    '#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.'),
113
  );
114
115
  $title_options = array(
116
    'optional' => t('Optional Title'),
117
    'required' => t('Required Title'),
118
    'value' => t('Static Title'),
119 39a181a4 Assos Assos
    'select' => t('Selected Title'),
120 85ad3d82 Assos Assos
    'none' => t('No Title'),
121
  );
122
123
  $form['title'] = array(
124
    '#type' => 'radios',
125
    '#title' => t('Link Title'),
126
    '#default_value' => isset($instance['settings']['title']) ? $instance['settings']['title'] : 'optional',
127
    '#options' => $title_options,
128
    '#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.'),
129
  );
130
131
  $form['title_value'] = array(
132
    '#type' => 'textfield',
133 39a181a4 Assos Assos
    '#title' => t('Static or default title'),
134 85ad3d82 Assos Assos
    '#default_value' => isset($instance['settings']['title_value']) ? $instance['settings']['title_value'] : '',
135 39a181a4 Assos Assos
    '#description' => t('This title will 1) always be used if "Static Title" is selected above, or 2) used if "Optional title" is selected above and no title is entered when creating content.'),
136
    '#states' => array(
137
      'visible' => array(
138
        ':input[name="instance[settings][title]"]' => array('value' => 'value'),
139
      ),
140
    ),
141
  );
142
143
  $form['title_allowed_values'] = array(
144
    '#type' => 'textarea',
145
    '#title' => t('Title allowed values'),
146
    '#default_value' => isset($instance['settings']['title_allowed_values']) ? $instance['settings']['title_allowed_values'] : '',
147
    '#description' => t('When using "Selected Title", you can allow users to select the title from a limited set of values (eg. Home, Office, Other). Enter here all possible values that title can take, one value per line.'),
148
    '#states' => array(
149
      'visible' => array(
150
        ':input[name="instance[settings][title]"]' => array('value' => 'select'),
151
      ),
152
    ),
153 85ad3d82 Assos Assos
  );
154
155 c8740e19 Assos Assos
  $form['title_label_use_field_label'] = array(
156
    '#type' => 'checkbox',
157
    '#title' => t('Use field label as the label for the title field'),
158
    '#default_value' => isset($instance['settings']['title_label_use_field_label']) ? $instance['settings']['title_label_use_field_label'] : FALSE,
159
    '#description' => t('If this is checked the field label will be hidden.'),
160
  );
161
162 85ad3d82 Assos Assos
  $form['title_maxlength'] = array(
163
    '#type' => 'textfield',
164
    '#title' => t('Max length of title field'),
165
    '#default_value' => isset($instance['settings']['title_maxlength']) ? $instance['settings']['title_maxlength'] : '128',
166
    '#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.'),
167
    '#maxlength' => 3,
168
    '#size' => 3,
169
  );
170
171
  if (module_exists('token')) {
172 c8740e19 Assos Assos
    // Add token module replacements fields.
173 85ad3d82 Assos Assos
    $form['enable_tokens'] = array(
174
      '#type' => 'checkbox',
175
      '#title' => t('Allow user-entered tokens'),
176
      '#default_value' => isset($instance['settings']['enable_tokens']) ? $instance['settings']['enable_tokens'] : 1,
177
      '#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.'),
178
    );
179 c8740e19 Assos Assos
180 85ad3d82 Assos Assos
    $entity_info = entity_get_info($instance['entity_type']);
181
    $form['tokens_help'] = array(
182
      '#theme' => 'token_tree',
183
      '#token_types' => array($entity_info['token type']),
184
      '#global_types' => TRUE,
185
      '#click_insert' => TRUE,
186
      '#dialog' => TRUE,
187
    );
188
  }
189
190
  $form['display'] = array(
191
    '#tree' => TRUE,
192
  );
193
  $form['display']['url_cutoff'] = array(
194
    '#type' => 'textfield',
195
    '#title' => t('URL Display Cutoff'),
196
    '#default_value' => isset($instance['settings']['display']['url_cutoff']) ? $instance['settings']['display']['url_cutoff'] : '80',
197
    '#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 (&hellip;)? Leave blank for no limit.'),
198
    '#maxlength' => 3,
199
    '#size' => 3,
200
  );
201
202 8e7483ab Assos Assos
  // Target options. E.g. New window = target="_blank".
203 85ad3d82 Assos Assos
  $target_options = array(
204
    LINK_TARGET_DEFAULT => t('Default (no target attribute)'),
205
    LINK_TARGET_TOP => t('Open link in window root'),
206
    LINK_TARGET_NEW_WINDOW => t('Open link in new window'),
207
    LINK_TARGET_USER => t('Allow the user to choose'),
208
  );
209 8e7483ab Assos Assos
210 85ad3d82 Assos Assos
  $form['attributes'] = array(
211
    '#tree' => TRUE,
212
  );
213 8e7483ab Assos Assos
214 85ad3d82 Assos Assos
  $form['attributes']['target'] = array(
215
    '#type' => 'radios',
216
    '#title' => t('Link Target'),
217
    '#default_value' => empty($instance['settings']['attributes']['target']) ? LINK_TARGET_DEFAULT : $instance['settings']['attributes']['target'],
218
    '#options' => $target_options,
219
  );
220
  $form['attributes']['rel'] = array(
221
    '#type' => 'textfield',
222
    '#title' => t('Rel Attribute'),
223 39a181a4 Assos Assos
    '#description' => t('When output, this link will have this rel attribute. The most common usage is <a href="http://en.wikipedia.org/wiki/Nofollow" target="blank">rel=&quot;nofollow&quot;</a> which prevents some search engines from spidering entered links.'),
224 85ad3d82 Assos Assos
    '#default_value' => empty($instance['settings']['attributes']['rel']) ? '' : $instance['settings']['attributes']['rel'],
225
    '#field_prefix' => 'rel = "',
226
    '#field_suffix' => '"',
227
    '#size' => 20,
228
  );
229
  $rel_remove_options = array(
230
    'default' => t('Keep rel as set up above (untouched/default)'),
231
    'rel_remove_external' => t('Remove rel if given link is external'),
232
    'rel_remove_internal' => t('Remove rel if given link is internal'),
233
  );
234
  $form['rel_remove'] = array(
235
    '#type' => 'radios',
236
    '#title' => t('Remove rel attribute automatically'),
237
    '#default_value' => !isset($instance['settings']['rel_remove']) ? 'default' : $instance['settings']['rel_remove'],
238
    '#description' => t('Turn on/off if rel attribute should be removed automatically, if user given link is internal/external'),
239
    '#options' => $rel_remove_options,
240
  );
241 c8740e19 Assos Assos
  $form['attributes']['configurable_class'] = array(
242
    '#title' => t("Allow the user to enter a custom link class per link"),
243
    '#type' => 'checkbox',
244
    '#default_value' => empty($instance['settings']['attributes']['configurable_class']) ? '' : $instance['settings']['attributes']['configurable_class'],
245
  );
246 85ad3d82 Assos Assos
  $form['attributes']['class'] = array(
247
    '#type' => 'textfield',
248
    '#title' => t('Additional CSS Class'),
249 7fe061e8 Assos Assos
    '#description' => t('When output, this link will have this class attribute. Multiple classes should be separated by spaces. Only alphanumeric characters and hyphens are allowed'),
250 85ad3d82 Assos Assos
    '#default_value' => empty($instance['settings']['attributes']['class']) ? '' : $instance['settings']['attributes']['class'],
251
  );
252
  $form['attributes']['configurable_title'] = array(
253
    '#title' => t("Allow the user to enter a link 'title' attribute"),
254
    '#type' => 'checkbox',
255
    '#default_value' => empty($instance['settings']['attributes']['configurable_title']) ? '' : $instance['settings']['attributes']['configurable_title'],
256
  );
257
  $form['attributes']['title'] = array(
258
    '#title' => t("Default link 'title' Attribute"),
259
    '#type' => 'textfield',
260 39a181a4 Assos Assos
    '#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" target="blank">WCAG 1.0 Guidelines</a> for links comformances. Tokens values will be evaluated.'),
261 85ad3d82 Assos Assos
    '#default_value' => empty($instance['settings']['attributes']['title']) ? '' : $instance['settings']['attributes']['title'],
262
    '#field_prefix' => 'title = "',
263
    '#field_suffix' => '"',
264
    '#size' => 20,
265
  );
266
  return $form;
267
}
268
269
/**
270 39a181a4 Assos Assos
 * Form validate.
271
 *
272 85ad3d82 Assos Assos
 * #element_validate handler for link_field_instance_settings_form().
273
 */
274
function link_field_settings_form_validate($element, &$form_state, $complete_form) {
275
  if ($form_state['values']['instance']['settings']['title'] === 'value' && empty($form_state['values']['instance']['settings']['title_value'])) {
276 39a181a4 Assos Assos
    form_set_error('instance][settings][title_value', t('A default title must be provided if the title is a static value.'));
277
  }
278
  if ($form_state['values']['instance']['settings']['title'] === 'select'
279
    && empty($form_state['values']['instance']['settings']['title_allowed_values'])) {
280
    form_set_error('instance][settings][title_allowed_values', t('You must enter one or more allowed values for link Title, the title is a selected value.'));
281 85ad3d82 Assos Assos
  }
282
  if (!empty($form_state['values']['instance']['settings']['display']['url_cutoff']) && !is_numeric($form_state['values']['instance']['settings']['display']['url_cutoff'])) {
283
    form_set_error('display', t('URL Display Cutoff value must be numeric.'));
284
  }
285
  if (empty($form_state['values']['instance']['settings']['title_maxlength'])) {
286
    form_set_value($element['title_maxlength'], '128', $form_state);
287
  }
288
  elseif (!is_numeric($form_state['values']['instance']['settings']['title_maxlength'])) {
289
    form_set_error('title_maxlength', t('The max length of the link title must be numeric.'));
290
  }
291
  elseif ($form_state['values']['instance']['settings']['title_maxlength'] > 255) {
292
    form_set_error('title_maxlength', t('The max length of the link title cannot be greater than 255 characters.'));
293
  }
294
}
295
296
/**
297
 * Implements hook_field_is_empty().
298
 */
299
function link_field_is_empty($item, $field) {
300
  return empty($item['title']) && empty($item['url']);
301
}
302
303
/**
304
 * Implements hook_field_load().
305
 */
306
function link_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) {
307
  foreach ($entities as $id => $entity) {
308
    foreach ($items[$id] as $delta => $item) {
309
      $items[$id][$delta]['attributes'] = _link_load($field, $item, $instances[$id]);
310 8e7483ab Assos Assos
      $items[$id][$delta]['original_title'] = $item['title'];
311
      $items[$id][$delta]['original_url'] = $item['url'];
312 85ad3d82 Assos Assos
    }
313
  }
314
}
315
316
/**
317
 * Implements hook_field_validate().
318
 */
319
function link_field_validate($entity_type, $entity, $field, $instance, $langcode, $items, &$errors) {
320
  $optional_field_found = FALSE;
321
  if ($instance['settings']['validate_url'] !== 0 || is_null($instance['settings']['validate_url']) || !isset($instance['settings']['validate_url'])) {
322
    foreach ($items as $delta => $value) {
323
      _link_validate($items[$delta], $delta, $field, $entity, $instance, $langcode, $optional_field_found, $errors);
324
    }
325
  }
326
327 639c8fcb Assos Assos
  foreach ($items as $delta => $value) {
328
    if (isset($value['attributes']) && is_string($value['attributes'])) {
329
      $errors[$field['field_name']][$langcode][$delta][] = array(
330
        'error' => 'link_required',
331
        'message' => t('String values are not acceptable for attributes.'),
332
        'error_element' => array('url' => TRUE, 'title' => FALSE),
333
      );
334
    }
335
  }
336
337 85ad3d82 Assos Assos
  if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] && !$optional_field_found) {
338
    $errors[$field['field_name']][$langcode][0][] = array(
339
      'error' => 'link_required',
340
      'message' => t('At least one title or URL must be entered.'),
341
      'error_element' => array('url' => FALSE, 'title' => TRUE),
342
    );
343
  }
344
}
345
346
/**
347
 * Implements hook_field_insert().
348
 */
349
function link_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
350
  foreach ($items as $delta => $value) {
351 7fe061e8 Assos Assos
    _link_process($items[$delta], $delta, $field, $entity, $instance);
352 85ad3d82 Assos Assos
  }
353
}
354
355
/**
356
 * Implements hook_field_update().
357
 */
358
function link_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
359
  foreach ($items as $delta => $value) {
360 7fe061e8 Assos Assos
    _link_process($items[$delta], $delta, $field, $entity, $instance);
361 85ad3d82 Assos Assos
  }
362
}
363
364
/**
365
 * Implements hook_field_prepare_view().
366
 */
367
function link_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
368
  foreach ($items as $entity_id => $entity_items) {
369
    foreach ($entity_items as $delta => $value) {
370
      _link_sanitize($items[$entity_id][$delta], $delta, $field, $instances[$entity_id], $entities[$entity_id]);
371
    }
372
  }
373
}
374
375
/**
376
 * Implements hook_field_widget_info().
377
 */
378
function link_field_widget_info() {
379
  return array(
380
    'link_field' => array(
381
      'label' => 'Link',
382
      'field types' => array('link_field'),
383
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
384
    ),
385
  );
386
}
387
388
/**
389
 * Implements hook_field_widget_form().
390
 */
391
function link_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
392
  $element += array(
393
    '#type' => $instance['widget']['type'],
394
    '#default_value' => isset($items[$delta]) ? $items[$delta] : '',
395
  );
396
  return $element;
397
}
398
399
/**
400
 * Implements hook_field_widget_error().
401
 */
402
function link_field_widget_error($element, $error, $form, &$form_state) {
403 39a181a4 Assos Assos
  if (!empty($error['error_element']['title'])) {
404 85ad3d82 Assos Assos
    form_error($element['title'], $error['message']);
405
  }
406 39a181a4 Assos Assos
  elseif (!empty($error['error_element']['url'])) {
407 85ad3d82 Assos Assos
    form_error($element['url'], $error['message']);
408
  }
409
}
410
411
/**
412
 * Unpacks the item attributes for use.
413
 */
414
function _link_load($field, $item, $instance) {
415
  if (isset($item['attributes'])) {
416
    if (!is_array($item['attributes'])) {
417
      $item['attributes'] = unserialize($item['attributes']);
418
    }
419
    return $item['attributes'];
420
  }
421
  elseif (isset($instance['settings']['attributes'])) {
422
    return $instance['settings']['attributes'];
423
  }
424
  else {
425
    return $field['settings']['attributes'];
426
  }
427
}
428
429
/**
430
 * Prepares the item attributes and url for storage.
431 7fe061e8 Assos Assos
 *
432 39a181a4 Assos Assos
 * @param array $item
433
 *   Link field values.
434
 * @param array $delta
435
 *   The sequence number for current values.
436
 * @param array $field
437
 *   The field structure array.
438
 * @param object $entity
439
 *   Entity object.
440
 * @param array $instance
441
 *   The instance structure for $field on $entity's bundle.
442 7fe061e8 Assos Assos
 *
443 39a181a4 Assos Assos
 * @codingStandardsIgnoreStart
444 85ad3d82 Assos Assos
 */
445 7fe061e8 Assos Assos
function _link_process(&$item, $delta, $field, $entity, $instance) {
446 39a181a4 Assos Assos
  // @codingStandardsIgnoreEnd
447 85ad3d82 Assos Assos
  // Trim whitespace from URL.
448 c8740e19 Assos Assos
  if (!empty($item['url'])) {
449
    $item['url'] = trim($item['url']);
450
  }
451 85ad3d82 Assos Assos
452
  // If no attributes are set then make sure $item['attributes'] is an empty
453
  // array, so $field['attributes'] can override it.
454
  if (empty($item['attributes'])) {
455
    $item['attributes'] = array();
456
  }
457
458
  // Serialize the attributes array.
459
  if (!is_string($item['attributes'])) {
460
    $item['attributes'] = serialize($item['attributes']);
461
  }
462
463
  // Don't save an invalid default value (e.g. 'http://').
464
  if ((isset($field['widget']['default_value'][$delta]['url']) && $item['url'] == $field['widget']['default_value'][$delta]['url']) && is_object($entity)) {
465 7fe061e8 Assos Assos
    $langcode = !empty($entity) ? field_language($instance['entity_type'], $entity, $instance['field_name']) : LANGUAGE_NONE;
466
    if (!link_validate_url($item['url'], $langcode)) {
467 85ad3d82 Assos Assos
      unset($item['url']);
468
    }
469
  }
470
}
471
472
/**
473
 * Validates that the link field has been entered properly.
474
 */
475
function _link_validate(&$item, $delta, $field, $entity, $instance, $langcode, &$optional_field_found, &$errors) {
476
  if ($item['url'] && !(isset($instance['default_value'][$delta]['url']) && $item['url'] === $instance['default_value'][$delta]['url'] && !$instance['required'])) {
477
    // Validate the link.
478 7fe061e8 Assos Assos
    if (!link_validate_url(trim($item['url']), $langcode)) {
479 85ad3d82 Assos Assos
      $errors[$field['field_name']][$langcode][$delta][] = array(
480
        'error' => 'link_required',
481 c8740e19 Assos Assos
        'message' => t('The value %value provided for %field is not a valid URL.', array(
482
          '%value' => trim($item['url']),
483
          '%field' => $instance['label'],
484
        )),
485 85ad3d82 Assos Assos
        'error_element' => array('url' => TRUE, 'title' => FALSE),
486
      );
487
    }
488
    // Require a title for the link if necessary.
489
    if ($instance['settings']['title'] == 'required' && strlen(trim($item['title'])) == 0) {
490
      $errors[$field['field_name']][$langcode][$delta][] = array(
491
        'error' => 'link_required',
492
        'message' => t('Titles are required for all links.'),
493
        'error_element' => array('url' => FALSE, 'title' => TRUE),
494
      );
495
    }
496
  }
497
  // Require a link if we have a title.
498
  if ($instance['settings']['url'] !== 'optional' && strlen(isset($item['title']) ? $item['title'] : NULL) > 0 && strlen(trim($item['url'])) == 0) {
499
    $errors[$field['field_name']][$langcode][$delta][] = array(
500
      'error' => 'link_required',
501
      'message' => t('You cannot enter a title without a link url.'),
502
      'error_element' => array('url' => TRUE, 'title' => FALSE),
503
    );
504
  }
505 39a181a4 Assos Assos
  // In a totally bizzaro case, where URLs and titles are optional but the field
506
  // is required, ensure there is at least one link.
507 c8740e19 Assos Assos
  if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional'
508 39a181a4 Assos Assos
    && (strlen(trim($item['url'])) !== 0 || strlen(trim($item['title'])) !== 0)) {
509 85ad3d82 Assos Assos
    $optional_field_found = TRUE;
510
  }
511 c8740e19 Assos Assos
  // Require entire field.
512 85ad3d82 Assos Assos
  if ($instance['settings']['url'] === 'optional' && $instance['settings']['title'] === 'optional' && $instance['required'] == 1 && !$optional_field_found && isset($instance['id'])) {
513
    $errors[$field['field_name']][$langcode][$delta][] = array(
514
      'error' => 'link_required',
515
      'message' => t('At least one title or URL must be entered.'),
516
      'error_element' => array('url' => FALSE, 'title' => TRUE),
517
    );
518
  }
519
}
520
521
/**
522
 * Clean up user-entered values for a link field according to field settings.
523
 *
524 39a181a4 Assos Assos
 * @param array $item
525 85ad3d82 Assos Assos
 *   A single link item, usually containing url, title, and attributes.
526 39a181a4 Assos Assos
 * @param int $delta
527 85ad3d82 Assos Assos
 *   The delta value if this field is one of multiple fields.
528 39a181a4 Assos Assos
 * @param array $field
529 85ad3d82 Assos Assos
 *   The CCK field definition.
530 39a181a4 Assos Assos
 * @param object $entity
531 85ad3d82 Assos Assos
 *   The entity containing this link.
532 39a181a4 Assos Assos
 *
533
 * @codingStandardsIgnoreStart
534 85ad3d82 Assos Assos
 */
535
function _link_sanitize(&$item, $delta, &$field, $instance, &$entity) {
536 39a181a4 Assos Assos
  // @codingStandardsIgnoreEnd
537 8e7483ab Assos Assos
  // As this function can be called multiple times and the item is changed by
538
  // reference we need to ensure that there's always the original data to
539
  // process otherwise processed data are processed again which might leads to
540
  // unexpected results.
541
  if (isset($item['_link_sanitized'])) {
542
    return;
543
  }
544
545
  // Store a flag to check in case of a second call.
546
  $item['_link_sanitized'] = TRUE;
547
548 85ad3d82 Assos Assos
  // Don't try to process empty links.
549
  if (empty($item['url']) && empty($item['title'])) {
550
    return;
551
  }
552 c8740e19 Assos Assos
  if (empty($item['html'])) {
553
    $item['html'] = FALSE;
554
  }
555 85ad3d82 Assos Assos
556
  // Replace URL tokens.
557
  $entity_type = $instance['entity_type'];
558
  $entity_info = entity_get_info($entity_type);
559
  $property_id = $entity_info['entity keys']['id'];
560
  $entity_token_type = isset($entity_info['token type']) ? $entity_info['token type'] : (
561 39a181a4 Assos Assos
  $entity_type == 'taxonomy_term' || $entity_type == 'taxonomy_vocabulary' ? str_replace('taxonomy_', '', $entity_type) : $entity_type
562 85ad3d82 Assos Assos
  );
563
  if (isset($instance['settings']['enable_tokens']) && $instance['settings']['enable_tokens']) {
564 7fe061e8 Assos Assos
    $text_tokens = token_scan($item['url']);
565
    if (!empty($text_tokens)) {
566
      // Load the entity if necessary for entities in views.
567
      if (isset($entity->{$property_id})) {
568
        $entity_loaded = entity_load($entity_type, array($entity->{$property_id}));
569
        $entity_loaded = array_pop($entity_loaded);
570
      }
571
      else {
572
        $entity_loaded = $entity;
573
      }
574
      $item['url'] = token_replace($item['url'], array($entity_token_type => $entity_loaded));
575 85ad3d82 Assos Assos
    }
576
  }
577
578 7fe061e8 Assos Assos
  $type = link_url_type($item['url']);
579 85ad3d82 Assos Assos
  // If the type of the URL cannot be determined and URL validation is disabled,
580
  // then assume LINK_EXTERNAL for later processing.
581
  if ($type == FALSE && $instance['settings']['validate_url'] === 0) {
582
    $type = LINK_EXTERNAL;
583
  }
584 8e7483ab Assos Assos
  elseif ($type == LINK_FRAGMENT || $type == LINK_QUERY) {
585
    // If type is a fragment or query, then use the current URL.
586
    $item['url'] = $_GET['q'] . $item['url'];
587
  }
588 85ad3d82 Assos Assos
  $url = link_cleanup_url($item['url']);
589
  $url_parts = _link_parse_url($url);
590
591
  if (!empty($url_parts['url'])) {
592 8e7483ab Assos Assos
    $item = array(
593
      'url' => $url_parts['url'],
594
      'query' => isset($url_parts['query']) ? $url_parts['query'] : NULL,
595
      'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : NULL,
596
      'absolute' => !empty($instance['settings']['absolute_url']),
597
      'html' => TRUE,
598
    ) + $item;
599 85ad3d82 Assos Assos
  }
600
601
  // Create a shortened URL for display.
602
  if ($type == LINK_EMAIL) {
603
    $display_url = str_replace('mailto:', '', $url);
604
  }
605 8e7483ab Assos Assos
  elseif ($type === LINK_EXTERNAL) {
606
    $display_url = $item['url'];
607
  }
608
  elseif ($type == LINK_TEL) {
609
    $display_url = str_replace('tel:', '', $url);
610
  }
611 85ad3d82 Assos Assos
  else {
612
    $display_url = url($url_parts['url'],
613
      array(
614
        'query' => isset($url_parts['query']) ? $url_parts['query'] : NULL,
615
        'fragment' => isset($url_parts['fragment']) ? $url_parts['fragment'] : NULL,
616 c8740e19 Assos Assos
        'absolute' => !empty($instance['settings']['absolute_url']),
617 85ad3d82 Assos Assos
      )
618
    );
619
  }
620
  if ($instance['settings']['display']['url_cutoff'] && strlen($display_url) > $instance['settings']['display']['url_cutoff']) {
621 8e7483ab Assos Assos
    $display_url = substr($display_url, 0, $instance['settings']['display']['url_cutoff']) . "…";
622 85ad3d82 Assos Assos
  }
623
  $item['display_url'] = $display_url;
624
625
  // Use the title defined at the instance level.
626
  if ($instance['settings']['title'] == 'value' && strlen(trim($instance['settings']['title_value']))) {
627
    $title = $instance['settings']['title_value'];
628
    if (function_exists('i18n_string_translate')) {
629
      $i18n_string_name = "field:{$instance['field_name']}:{$instance['bundle']}:title_value";
630
      $title = i18n_string_translate($i18n_string_name, $title);
631
    }
632
  }
633
  // Use the title defined by the user at the widget level.
634 8e7483ab Assos Assos
  elseif (isset($item['title']) && drupal_strlen(trim($item['title']))) {
635 85ad3d82 Assos Assos
    $title = $item['title'];
636
  }
637 39a181a4 Assos Assos
  // Use the static title if a user-defined title is optional and a static title
638
  // has been defined.
639
  elseif ($instance['settings']['title'] == 'optional' && drupal_strlen(trim($instance['settings']['title_value']))) {
640
    $title = $instance['settings']['title_value'];
641
  }
642 85ad3d82 Assos Assos
  else {
643
    $title = '';
644
  }
645
646 c8740e19 Assos Assos
  // Replace title tokens.
647 39a181a4 Assos Assos
  if ($title && $instance['settings']['enable_tokens']) {
648 7fe061e8 Assos Assos
    $text_tokens = token_scan($title);
649
    if (!empty($text_tokens)) {
650
      // Load the entity if necessary for entities in views.
651
      if (isset($entity->{$property_id})) {
652
        $entity_loaded = entity_load($entity_type, array($entity->{$property_id}));
653
        $entity_loaded = array_pop($entity_loaded);
654
      }
655
      else {
656
        $entity_loaded = $entity;
657
      }
658
      $title = token_replace($title, array($entity_token_type => $entity_loaded));
659 85ad3d82 Assos Assos
    }
660 39a181a4 Assos Assos
  }
661
  if ($title && ($instance['settings']['title'] == 'value' || $instance['settings']['enable_tokens'])) {
662
    $title = filter_xss($title, array(
663
      'b',
664
      'br',
665
      'code',
666
      'em',
667
      'i',
668
      'img',
669
      'span',
670
      'strong',
671
      'sub',
672
      'sup',
673
      'tt',
674
      'u',
675
    ));
676 85ad3d82 Assos Assos
    $item['html'] = TRUE;
677
  }
678 39a181a4 Assos Assos
  $item['title'] = empty($title) && $title !== '0' ? $item['display_url'] : $title;
679 85ad3d82 Assos Assos
680
  if (!isset($item['attributes'])) {
681
    $item['attributes'] = array();
682
  }
683
684
  // Unserialize attributtes array if it has not been unserialized yet.
685
  if (!is_array($item['attributes'])) {
686 c8740e19 Assos Assos
    $item['attributes'] = (array) unserialize($item['attributes']);
687 85ad3d82 Assos Assos
  }
688
689
  // Add default attributes.
690
  if (!is_array($instance['settings']['attributes'])) {
691
    $instance['settings']['attributes'] = _link_default_attributes();
692
  }
693
  else {
694
    $instance['settings']['attributes'] += _link_default_attributes();
695
  }
696
697
  // Merge item attributes with attributes defined at the field level.
698
  $item['attributes'] += $instance['settings']['attributes'];
699
700
  // If user is not allowed to choose target attribute, use default defined at
701
  // field level.
702
  if ($instance['settings']['attributes']['target'] != LINK_TARGET_USER) {
703
    $item['attributes']['target'] = $instance['settings']['attributes']['target'];
704
  }
705
  elseif ($item['attributes']['target'] == LINK_TARGET_USER) {
706
    $item['attributes']['target'] = LINK_TARGET_DEFAULT;
707
  }
708
709
  // Remove the target attribute if the default (no target) is selected.
710
  if (empty($item['attributes']) || (isset($item['attributes']['target']) && $item['attributes']['target'] == LINK_TARGET_DEFAULT)) {
711
    unset($item['attributes']['target']);
712
  }
713
714
  // Remove rel attribute for internal or external links if selected.
715
  if (isset($item['attributes']['rel']) && isset($instance['settings']['rel_remove']) && $instance['settings']['rel_remove'] != 'default') {
716
    if (($instance['settings']['rel_remove'] != 'rel_remove_internal' && $type != LINK_INTERNAL) ||
717
      ($instance['settings']['rel_remove'] != 'rel_remove_external' && $type != LINK_EXTERNAL)) {
718
      unset($item['attributes']['rel']);
719
    }
720
  }
721
722
  // Handle "title" link attribute.
723
  if (!empty($item['attributes']['title']) && module_exists('token')) {
724 7fe061e8 Assos Assos
    $text_tokens = token_scan($item['attributes']['title']);
725 39a181a4 Assos Assos
    if (!empty($text_tokens)) {
726 7fe061e8 Assos Assos
      // Load the entity (necessary for entities in views).
727
      if (isset($entity->{$property_id})) {
728
        $entity_loaded = entity_load($entity_type, array($entity->{$property_id}));
729
        $entity_loaded = array_pop($entity_loaded);
730
      }
731
      else {
732
        $entity_loaded = $entity;
733
      }
734 8e7483ab Assos Assos
      $item['attributes']['title'] = token_replace($item['attributes']['title'], array($entity_token_type => $entity_loaded), array('clear' => TRUE));
735 85ad3d82 Assos Assos
    }
736 39a181a4 Assos Assos
    $item['attributes']['title'] = filter_xss($item['attributes']['title'], array(
737
      'b',
738
      'br',
739
      'code',
740
      'em',
741
      'i',
742
      'img',
743
      'span',
744
      'strong',
745
      'sub',
746
      'sup',
747
      'tt',
748
      'u',
749
    ));
750 85ad3d82 Assos Assos
  }
751 c8740e19 Assos Assos
  // Handle attribute classes.
752
  if (!empty($item['attributes']['class'])) {
753
    $classes = explode(' ', $item['attributes']['class']);
754
    foreach ($classes as &$class) {
755 7fe061e8 Assos Assos
      $class = drupal_clean_css_identifier($class);
756 c8740e19 Assos Assos
    }
757
    $item['attributes']['class'] = implode(' ', $classes);
758
  }
759
  unset($item['attributes']['configurable_class']);
760
761 85ad3d82 Assos Assos
  // Remove title attribute if it's equal to link text.
762
  if (isset($item['attributes']['title']) && $item['attributes']['title'] == $item['title']) {
763
    unset($item['attributes']['title']);
764
  }
765
  unset($item['attributes']['configurable_title']);
766
767
  // Remove empty attributes.
768
  $item['attributes'] = array_filter($item['attributes']);
769
}
770
771
/**
772
 * Because parse_url doesn't work with relative urls.
773
 *
774
 * @param string $url
775
 *   URL to parse.
776
 *
777 c8740e19 Assos Assos
 * @return array
778 85ad3d82 Assos Assos
 *   Array of url pieces - only 'url', 'query', and 'fragment'.
779
 */
780
function _link_parse_url($url) {
781
  $url_parts = array();
782
  // Separate out the anchor, if any.
783
  if (strpos($url, '#') !== FALSE) {
784
    $url_parts['fragment'] = substr($url, strpos($url, '#') + 1);
785
    $url = substr($url, 0, strpos($url, '#'));
786
  }
787
  // Separate out the query string, if any.
788
  if (strpos($url, '?') !== FALSE) {
789
    $query = substr($url, strpos($url, '?') + 1);
790
    $url_parts['query'] = _link_parse_str($query);
791
    $url = substr($url, 0, strpos($url, '?'));
792
  }
793
  $url_parts['url'] = $url;
794
  return $url_parts;
795
}
796
797
/**
798 c8740e19 Assos Assos
 * Replaces the PHP parse_str() function.
799
 *
800
 * Because parse_str replaces the following characters in query parameters name
801 39a181a4 Assos Assos
 * in order to maintain compatibility with deprecated register_globals
802
 * directive:
803 85ad3d82 Assos Assos
 *
804
 *   - chr(32) ( ) (space)
805
 *   - chr(46) (.) (dot)
806
 *   - chr(91) ([) (open square bracket)
807
 *   - chr(128) - chr(159) (various)
808
 *
809
 * @param string $query
810
 *   Query to parse.
811
 *
812 c8740e19 Assos Assos
 * @return array
813 85ad3d82 Assos Assos
 *   Array of query parameters.
814
 *
815
 * @see http://php.net/manual/en/language.variables.external.php#81080
816
 */
817
function _link_parse_str($query) {
818
  $query_array = array();
819
820
  $pairs = explode('&', $query);
821
  foreach ($pairs as $pair) {
822 c8740e19 Assos Assos
    $name_value = explode('=', $pair, 2);
823 85ad3d82 Assos Assos
    $name = urldecode($name_value[0]);
824
    $value = isset($name_value[1]) ? urldecode($name_value[1]) : NULL;
825
    $query_array[$name] = $value;
826
  }
827
828
  return $query_array;
829
}
830
831
/**
832
 * Implements hook_theme().
833
 */
834
function link_theme() {
835
  return array(
836
    'link_formatter_link_default' => array(
837
      'variables' => array('element' => NULL, 'field' => NULL),
838
    ),
839
    'link_formatter_link_plain' => array(
840
      'variables' => array('element' => NULL, 'field' => NULL),
841
    ),
842 7fe061e8 Assos Assos
    'link_formatter_link_host' => array(
843
      'variables' => array('element' => NULL),
844
    ),
845 85ad3d82 Assos Assos
    'link_formatter_link_absolute' => array(
846
      'variables' => array('element' => NULL, 'field' => NULL),
847
    ),
848
    'link_formatter_link_domain' => array(
849 39a181a4 Assos Assos
      'variables' => array(
850
        'element' => NULL,
851
        'display' => NULL,
852
        'field' => NULL,
853
      ),
854
    ),
855
    'link_formatter_link_no_protocol' => array(
856
      'variables' => array('element' => NULL, 'field' => NULL),
857 85ad3d82 Assos Assos
    ),
858
    'link_formatter_link_title_plain' => array(
859
      'variables' => array('element' => NULL, 'field' => NULL),
860
    ),
861
    'link_formatter_link_url' => array(
862
      'variables' => array('element' => NULL, 'field' => NULL),
863
    ),
864
    'link_formatter_link_short' => array(
865
      'variables' => array('element' => NULL, 'field' => NULL),
866
    ),
867
    'link_formatter_link_label' => array(
868
      'variables' => array('element' => NULL, 'field' => NULL),
869
    ),
870
    'link_formatter_link_separate' => array(
871
      'variables' => array('element' => NULL, 'field' => NULL),
872
    ),
873
    'link_field' => array(
874
      'render element' => 'element',
875
    ),
876
  );
877
}
878
879
/**
880
 * Formats a link field widget.
881
 */
882
function theme_link_field($vars) {
883 8e7483ab Assos Assos
  drupal_add_css(drupal_get_path('module', 'link') . '/css/link.css');
884 85ad3d82 Assos Assos
  $element = $vars['element'];
885
  // Prefix single value link fields with the name of the field.
886
  if (empty($element['#field']['multiple'])) {
887
    if (isset($element['url']) && !isset($element['title'])) {
888
      $element['url']['#title_display'] = 'invisible';
889
    }
890
  }
891
892
  $output = '';
893
  $output .= '<div class="link-field-subrow clearfix">';
894
  if (isset($element['title'])) {
895
    $output .= '<div class="link-field-title link-field-column">' . drupal_render($element['title']) . '</div>';
896
  }
897
  $output .= '<div class="link-field-url' . (isset($element['title']) ? ' link-field-column' : '') . '">' . drupal_render($element['url']) . '</div>';
898
  $output .= '</div>';
899
  if (!empty($element['attributes']['target'])) {
900
    $output .= '<div class="link-attributes">' . drupal_render($element['attributes']['target']) . '</div>';
901
  }
902
  if (!empty($element['attributes']['title'])) {
903
    $output .= '<div class="link-attributes">' . drupal_render($element['attributes']['title']) . '</div>';
904
  }
905 c8740e19 Assos Assos
  if (!empty($element['attributes']['class'])) {
906
    $output .= '<div class="link-attributes">' . drupal_render($element['attributes']['class']) . '</div>';
907
  }
908
  $output .= drupal_render_children($element);
909 85ad3d82 Assos Assos
  return $output;
910
}
911
912
/**
913
 * Implements hook_element_info().
914
 */
915
function link_element_info() {
916
  $elements = array();
917
  $elements['link_field'] = array(
918
    '#input' => TRUE,
919
    '#process' => array('link_field_process'),
920
    '#theme' => 'link_field',
921
    '#theme_wrappers' => array('form_element'),
922
  );
923
  return $elements;
924
}
925
926
/**
927
 * Returns the default attributes and their values.
928
 */
929
function _link_default_attributes() {
930
  return array(
931
    'target' => LINK_TARGET_DEFAULT,
932
    'class' => '',
933
    'rel' => '',
934
  );
935
}
936
937
/**
938
 * Processes the link type element before displaying the field.
939
 *
940
 * Build the form element. When creating a form using FAPI #process,
941
 * note that $element['#value'] is already set.
942
 *
943 39a181a4 Assos Assos
 * The $fields array is in
944
 * $complete_form['#field_info'][$element['#field_name']].
945 85ad3d82 Assos Assos
 */
946
function link_field_process($element, $form_state, $complete_form) {
947
  $instance = field_widget_instance($element, $form_state);
948 8e7483ab Assos Assos
  if (!$instance) {
949
    // The element comes from a custom form, we have to manually create the
950
    // $instance settings.
951
    $instance['settings'] = array(
952
      'title_maxlength' => isset($element['#title_maxlength']) ? $element['#title_maxlength'] : 128,
953
      'title' => isset($element['#title_mode']) ? $element['#title_mode'] : 'optional',
954
      'title_label_use_field_label' => isset($element['#title_label_use_field_label']) ? $element['#title_label_use_field_label'] : FALSE,
955
      'url' => isset($element['#url']) ? $element['#url'] : 'optional',
956
    );
957
    if (isset($element['#attributes'])) {
958
      $instance['settings']['attributes'] = $element['#attributes'];
959
    }
960
  }
961 85ad3d82 Assos Assos
  $settings = $instance['settings'];
962
  $element['url'] = array(
963
    '#type' => 'textfield',
964
    '#maxlength' => LINK_URL_MAX_LENGTH,
965
    '#title' => t('URL'),
966
    '#required' => ($element['#delta'] == 0 && $settings['url'] !== 'optional') ? $element['#required'] : FALSE,
967
    '#default_value' => isset($element['#value']['url']) ? $element['#value']['url'] : NULL,
968
  );
969 39a181a4 Assos Assos
  if (in_array($settings['title'], array('optional', 'required'))) {
970 c8740e19 Assos Assos
    // Figure out the label of the title field.
971
    if (!empty($settings['title_label_use_field_label'])) {
972
      // Use the element label as the title field label.
973
      $title_label = $element['#title'];
974
      // Hide the field label because there is no need for the duplicate labels.
975
      $element['#title_display'] = 'invisible';
976
    }
977
    else {
978
      $title_label = t('Title');
979
    }
980
981 39a181a4 Assos Assos
    // Default value.
982
    $title_maxlength = 128;
983
    if (!empty($settings['title_maxlength'])) {
984
      $title_maxlength = $settings['title_maxlength'];
985
    }
986
987 85ad3d82 Assos Assos
    $element['title'] = array(
988
      '#type' => 'textfield',
989 39a181a4 Assos Assos
      '#maxlength' => $title_maxlength,
990 c8740e19 Assos Assos
      '#title' => $title_label,
991 39a181a4 Assos Assos
      '#description' => t('The link title is limited to @maxlength characters maximum.', array('@maxlength' => $title_maxlength)),
992 85ad3d82 Assos Assos
      '#required' => ($settings['title'] == 'required' && (($element['#delta'] == 0 && $element['#required']) || !empty($element['#value']['url']))) ? TRUE : FALSE,
993
      '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL,
994
    );
995
  }
996 39a181a4 Assos Assos
  elseif ($settings['title'] == 'select') {
997
    $options = drupal_map_assoc(array_filter(explode("\n", str_replace("\r", "\n", trim($settings['title_allowed_values'])))));
998
    $element['title'] = array(
999
      '#type' => 'select',
1000
      '#title' => t('Title'),
1001
      '#description' => t('Select the a title for this link.'),
1002
      '#default_value' => isset($element['#value']['title']) ? $element['#value']['title'] : NULL,
1003
      '#options' => $options,
1004
    );
1005
  }
1006 85ad3d82 Assos Assos
1007
  // Initialize field attributes as an array if it is not an array yet.
1008
  if (!is_array($settings['attributes'])) {
1009
    $settings['attributes'] = array();
1010
  }
1011
  // Add default attributes.
1012
  $settings['attributes'] += _link_default_attributes();
1013
  $attributes = isset($element['#value']['attributes']) ? $element['#value']['attributes'] : $settings['attributes'];
1014
  if (!empty($settings['attributes']['target']) && $settings['attributes']['target'] == LINK_TARGET_USER) {
1015
    $element['attributes']['target'] = array(
1016
      '#type' => 'checkbox',
1017
      '#title' => t('Open URL in a New Window'),
1018
      '#return_value' => LINK_TARGET_NEW_WINDOW,
1019
      '#default_value' => isset($attributes['target']) ? $attributes['target'] : FALSE,
1020
    );
1021
  }
1022
  if (!empty($settings['attributes']['configurable_title']) && $settings['attributes']['configurable_title'] == 1) {
1023
    $element['attributes']['title'] = array(
1024
      '#type' => 'textfield',
1025
      '#title' => t('Link "title" attribute'),
1026
      '#default_value' => isset($attributes['title']) ? $attributes['title'] : '',
1027
      '#field_prefix' => 'title = "',
1028
      '#field_suffix' => '"',
1029
    );
1030
  }
1031 c8740e19 Assos Assos
  if (!empty($settings['attributes']['configurable_class']) && $settings['attributes']['configurable_class'] == 1) {
1032
    $element['attributes']['class'] = array(
1033
      '#type' => 'textfield',
1034
      '#title' => t('Custom link class'),
1035
      '#default_value' => isset($attributes['class']) ? $attributes['class'] : '',
1036
      '#field_prefix' => 'class = "',
1037
      '#field_suffix' => '"',
1038
    );
1039
  }
1040 85ad3d82 Assos Assos
1041 7fe061e8 Assos Assos
  // If the title field is available or there are field accepts multiple values
1042 39a181a4 Assos Assos
  // then allow the individual field items display the required asterisk if
1043
  // needed.
1044 85ad3d82 Assos Assos
  if (isset($element['title']) || isset($element['_weight'])) {
1045
    // To prevent an extra required indicator, disable the required flag on the
1046
    // base element since all the sub-fields are already required if desired.
1047
    $element['#required'] = FALSE;
1048
  }
1049
1050
  return $element;
1051
}
1052
1053
/**
1054
 * Implements hook_field_formatter_info().
1055
 */
1056
function link_field_formatter_info() {
1057
  return array(
1058
    'link_default' => array(
1059
      'label' => t('Title, as link (default)'),
1060
      'field types' => array('link_field'),
1061
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1062 8e7483ab Assos Assos
      'settings' => array(
1063
        'custom_title' => '',
1064
      ),
1065 85ad3d82 Assos Assos
    ),
1066
    'link_title_plain' => array(
1067
      'label' => t('Title, as plain text'),
1068
      'field types' => array('link_field'),
1069
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1070
    ),
1071 7fe061e8 Assos Assos
    'link_host' => array(
1072
      'label' => t('Host, as plain text'),
1073
      'field types' => array('link_field'),
1074
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1075
    ),
1076 85ad3d82 Assos Assos
    'link_url' => array(
1077
      'label' => t('URL, as link'),
1078
      'field types' => array('link_field'),
1079
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1080
    ),
1081
    'link_plain' => array(
1082
      'label' => t('URL, as plain text'),
1083
      'field types' => array('link_field'),
1084
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1085
    ),
1086
    'link_absolute' => array(
1087
      'label' => t('URL, absolute'),
1088
      'field types' => array('link_field'),
1089
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1090
    ),
1091
    'link_domain' => array(
1092
      'label' => t('Domain, as link'),
1093
      'field types' => array('link_field'),
1094
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1095
      'settings' => array(
1096
        'strip_www' => FALSE,
1097
      ),
1098
    ),
1099 39a181a4 Assos Assos
    'link_no_protocol' => array(
1100
      'label' => t('URL with the protocol removed'),
1101
      'field types' => array('link_field'),
1102
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1103
    ),
1104 85ad3d82 Assos Assos
    'link_short' => array(
1105
      'label' => t('Short, as link with title "Link"'),
1106
      'field types' => array('link_field'),
1107
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1108
    ),
1109
    'link_label' => array(
1110
      'label' => t('Label, as link with label as title'),
1111
      'field types' => array('link_field'),
1112
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1113
    ),
1114
    'link_separate' => array(
1115
      'label' => t('Separate title and URL'),
1116
      'field types' => array('link_field'),
1117
      'multiple values' => FIELD_BEHAVIOR_DEFAULT,
1118
    ),
1119
  );
1120
}
1121
1122
/**
1123
 * Implements hook_field_formatter_settings_form().
1124
 */
1125
function link_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
1126
  $display = $instance['display'][$view_mode];
1127
  $settings = $display['settings'];
1128
  $element = array();
1129
  if ($display['type'] == 'link_domain') {
1130
    $element['strip_www'] = array(
1131
      '#title' => t('Strip www. from domain'),
1132
      '#type' => 'checkbox',
1133
      '#default_value' => $settings['strip_www'],
1134
    );
1135
  }
1136 8e7483ab Assos Assos
  if ($display['type'] == 'link_default') {
1137
    $element['custom_title'] = array(
1138
      '#title' => t('Override title'),
1139
      '#description' => t('Optionally override the title for the link(s).'),
1140
      '#type' => 'textfield',
1141
      '#default_value' => $settings['custom_title'],
1142
    );
1143
  }
1144 85ad3d82 Assos Assos
  return $element;
1145
}
1146
1147
/**
1148
 * Implements hook_field_formatter_settings_summary().
1149
 */
1150
function link_field_formatter_settings_summary($field, $instance, $view_mode) {
1151 39a181a4 Assos Assos
1152 85ad3d82 Assos Assos
  $display = $instance['display'][$view_mode];
1153 39a181a4 Assos Assos
1154 85ad3d82 Assos Assos
  if ($display['type'] == 'link_domain') {
1155
    if ($display['settings']['strip_www']) {
1156
      return t('Strip www. from domain');
1157
    }
1158
    else {
1159
      return t('Leave www. in domain');
1160
    }
1161
  }
1162 8e7483ab Assos Assos
  if ($display['type'] == 'link_default') {
1163
    if ($display['settings']['custom_title']) {
1164
      return t('Title: %title', array('%title' => $display['settings']['custom_title']));
1165
    }
1166
  }
1167 85ad3d82 Assos Assos
  return '';
1168
}
1169
1170
/**
1171
 * Implements hook_field_formatter_view().
1172
 */
1173
function link_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
1174
  $elements = array();
1175
  foreach ($items as $delta => $item) {
1176 8e7483ab Assos Assos
    if (!empty($display['settings']['custom_title'])) {
1177
      $item['title'] = $display['settings']['custom_title'];
1178
    }
1179 85ad3d82 Assos Assos
    $elements[$delta] = array(
1180
      '#theme' => 'link_formatter_' . $display['type'],
1181
      '#element' => $item,
1182
      '#field' => $instance,
1183 8e7483ab Assos Assos
      '#display' => array(
1184
        'settings' => $display['settings'],
1185
      ),
1186 85ad3d82 Assos Assos
    );
1187
  }
1188
  return $elements;
1189
}
1190
1191
/**
1192
 * Formats a link.
1193
 */
1194
function theme_link_formatter_link_default($vars) {
1195
  $link_options = $vars['element'];
1196
  unset($link_options['title']);
1197
  unset($link_options['url']);
1198
1199
  if (isset($link_options['attributes']['class'])) {
1200
    $link_options['attributes']['class'] = array($link_options['attributes']['class']);
1201
  }
1202
  // Display a normal link if both title and URL are available.
1203
  if (!empty($vars['element']['title']) && !empty($vars['element']['url'])) {
1204 8e7483ab Assos Assos
    return l($vars['element']['title'], rawurldecode($vars['element']['url']), $link_options);
1205 85ad3d82 Assos Assos
  }
1206
  // If only a title, display the title.
1207
  elseif (!empty($vars['element']['title'])) {
1208 39a181a4 Assos Assos
    return !empty($link_options['html']) ? $vars['element']['title'] : check_plain($vars['element']['title']);
1209 85ad3d82 Assos Assos
  }
1210
  elseif (!empty($vars['element']['url'])) {
1211 8e7483ab Assos Assos
    return l($vars['element']['title'], rawurldecode($vars['element']['url']), $link_options);
1212 85ad3d82 Assos Assos
  }
1213
}
1214
1215
/**
1216
 * Formats a link (or its title) as plain text.
1217
 */
1218
function theme_link_formatter_link_plain($vars) {
1219
  $link_options = $vars['element'];
1220
  if (isset($link_options['title'])) {
1221
    unset($link_options['title']);
1222
  }
1223
  else {
1224
    $vars['element']['title'] = '';
1225
  }
1226
  unset($link_options['url']);
1227
  return empty($vars['element']['url']) ? check_plain($vars['element']['title']) : url($vars['element']['url'], $link_options);
1228
}
1229
1230 7fe061e8 Assos Assos
/**
1231
 * Theme function for 'host' text field formatter.
1232
 */
1233
function theme_link_formatter_link_host($vars) {
1234
  $host = @parse_url($vars['element']['url']);
1235
  return isset($host['host']) ? check_plain($host['host']) : '';
1236
}
1237
1238 85ad3d82 Assos Assos
/**
1239 c8740e19 Assos Assos
 * Formats a link as an absolute URL.
1240 85ad3d82 Assos Assos
 */
1241
function theme_link_formatter_link_absolute($vars) {
1242
  $absolute = array('absolute' => TRUE);
1243
  return empty($vars['element']['url']) ? '' : url($vars['element']['url'], $absolute + $vars['element']);
1244
}
1245
1246
/**
1247
 * Formats a link using the URL's domain for it's link text.
1248
 */
1249
function theme_link_formatter_link_domain($vars) {
1250
  $link_options = $vars['element'];
1251
  unset($link_options['title']);
1252
  unset($link_options['url']);
1253
  $domain = parse_url($vars['element']['display_url'], PHP_URL_HOST);
1254
  if (!empty($vars['display']['settings']['strip_www'])) {
1255
    $domain = str_replace('www.', '', $domain);
1256
  }
1257
  return $vars['element']['url'] ? l($domain, $vars['element']['url'], $link_options) : '';
1258
}
1259
1260 39a181a4 Assos Assos
/**
1261
 * Formats a link without the http:// or https://.
1262
 */
1263
function theme_link_formatter_link_no_protocol($vars) {
1264
  $link_options = $vars['element'];
1265
  unset($link_options['title']);
1266
  unset($link_options['url']);
1267
  // We drop any scheme of the url.
1268
  $scheme = parse_url($vars['element']['url']);
1269
  $search = '/' . preg_quote($scheme['scheme'] . '://', '/') . '/';
1270
  $replace = '';
1271
  $display_url = preg_replace($search, $replace, $vars['element']['url'], 1);
1272
1273
  return $vars['element']['url'] ? l($display_url, $vars['element']['url'], $link_options) : '';
1274
}
1275
1276 85ad3d82 Assos Assos
/**
1277
 * Formats a link's title as plain text.
1278
 */
1279
function theme_link_formatter_link_title_plain($vars) {
1280 39a181a4 Assos Assos
  return empty($vars['element']['title']) ? '' : check_plain(decode_entities($vars['element']['title']));
1281 85ad3d82 Assos Assos
}
1282
1283
/**
1284
 * Formats a link using an alternate display URL for its link text.
1285
 */
1286
function theme_link_formatter_link_url($vars) {
1287
  $link_options = $vars['element'];
1288 8e7483ab Assos Assos
  if (isset($link_options['attributes']['class'])) {
1289
    $link_options['attributes']['class'] = array($link_options['attributes']['class']);
1290
  }
1291 85ad3d82 Assos Assos
  unset($link_options['title']);
1292
  unset($link_options['url']);
1293
  return $vars['element']['url'] ? l($vars['element']['display_url'], $vars['element']['url'], $link_options) : '';
1294
}
1295
1296
/**
1297
 * Formats a link using "Link" as the link text.
1298
 */
1299
function theme_link_formatter_link_short($vars) {
1300
  $link_options = $vars['element'];
1301
  unset($link_options['title']);
1302
  unset($link_options['url']);
1303
  return $vars['element']['url'] ? l(t('Link'), $vars['element']['url'], $link_options) : '';
1304
}
1305
1306
/**
1307
 * Formats a link using the field's label as link text.
1308
 */
1309
function theme_link_formatter_link_label($vars) {
1310
  $link_options = $vars['element'];
1311
  unset($link_options['title']);
1312
  unset($link_options['url']);
1313 8e7483ab Assos Assos
  $label = $vars['field']['label'];
1314
  if (function_exists('i18n_string_translate')) {
1315
    $i18n_string_name = "field:{$vars['field']['field_name']}:{$vars['field']['bundle']}:label";
1316
    $label = i18n_string_translate($i18n_string_name, $label);
1317
  }
1318
  return $vars['element']['url'] ? l($label, $vars['element']['url'], $link_options) : '';
1319 85ad3d82 Assos Assos
}
1320
1321
/**
1322
 * Formats a link as separate title and URL elements.
1323
 */
1324
function theme_link_formatter_link_separate($vars) {
1325
  $class = empty($vars['element']['attributes']['class']) ? '' : ' ' . $vars['element']['attributes']['class'];
1326
  unset($vars['element']['attributes']['class']);
1327
  $link_options = $vars['element'];
1328
  unset($link_options['title']);
1329
  unset($link_options['url']);
1330
  $title = empty($vars['element']['title']) ? '' : check_plain($vars['element']['title']);
1331
1332 c8740e19 Assos Assos
  // @TODO static html markup looks not very elegant
1333
  // needs smarter output solution and an optional title/url seperator
1334 85ad3d82 Assos Assos
  $url_parts = _link_parse_url($vars['element']['url']);
1335
  $output = '';
1336
  $output .= '<div class="link-item ' . $class . '">';
1337
  if (!empty($title)) {
1338
    $output .= '<div class="link-title">' . $title . '</div>';
1339
  }
1340
  $output .= '<div class="link-url">' . l($url_parts['url'], $vars['element']['url'], $link_options) . '</div>';
1341
  $output .= '</div>';
1342
  return $output;
1343
}
1344
1345
/**
1346
 * Implements hook_token_list().
1347 c8740e19 Assos Assos
 *
1348 39a181a4 Assos Assos
 * @TODO: hook_token_list no longer exists - this should change to
1349
 *   hook_token_info().
1350 85ad3d82 Assos Assos
 */
1351
function link_token_list($type = 'all') {
1352
  if ($type === 'field' || $type === 'all') {
1353
    $tokens = array();
1354
    $tokens['link']['url'] = t("Link URL");
1355
    $tokens['link']['title'] = t("Link title");
1356
    $tokens['link']['view'] = t("Formatted html link");
1357
    return $tokens;
1358
  }
1359
}
1360
1361 c8740e19 Assos Assos
/**
1362
 * Implements hook_token_values().
1363
 *
1364 39a181a4 Assos Assos
 * @TODO: hook_token_values no longer exists - this should change to
1365
 *   hook_tokens().
1366 c8740e19 Assos Assos
 */
1367 85ad3d82 Assos Assos
function link_token_values($type, $object = NULL) {
1368
  if ($type === 'field') {
1369
    $item = $object[0];
1370
1371
    $tokens['url'] = $item['url'];
1372
    $tokens['title'] = $item['title'];
1373
    $tokens['view'] = isset($item['view']) ? $item['view'] : '';
1374
1375
    return $tokens;
1376
  }
1377
}
1378
1379
/**
1380
 * Implements hook_views_api().
1381
 */
1382
function link_views_api() {
1383
  return array(
1384
    'api' => 2,
1385
    'path' => drupal_get_path('module', 'link') . '/views',
1386
  );
1387
}
1388
1389
/**
1390
 * Forms a valid URL if possible from an entered address.
1391 7fe061e8 Assos Assos
 *
1392 85ad3d82 Assos Assos
 * Trims whitespace and automatically adds an http:// to addresses without a
1393 39a181a4 Assos Assos
 * protocol specified.
1394 85ad3d82 Assos Assos
 *
1395
 * @param string $url
1396 c8740e19 Assos Assos
 *   The url entered by the user.
1397 85ad3d82 Assos Assos
 * @param string $protocol
1398 39a181a4 Assos Assos
 *   The protocol to be prepended to the url if one is not specified.
1399 85ad3d82 Assos Assos
 */
1400
function link_cleanup_url($url, $protocol = 'http') {
1401
  $url = trim($url);
1402 7fe061e8 Assos Assos
  $type = link_url_type($url);
1403 85ad3d82 Assos Assos
1404
  if ($type === LINK_EXTERNAL) {
1405
    // Check if there is no protocol specified.
1406
    $protocol_match = preg_match("/^([a-z0-9][a-z0-9\.\-_]*:\/\/)/i", $url);
1407
    if (empty($protocol_match)) {
1408 39a181a4 Assos Assos
      // But should there be? Add an automatic http:// if it starts with a
1409
      // domain name.
1410
      $link_domains = _link_domains();
1411
      $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)(' . $link_domains . '|[a-z]{2}))/i', $url);
1412 85ad3d82 Assos Assos
      if (!empty($domain_match)) {
1413
        $url = $protocol . "://" . $url;
1414
      }
1415
    }
1416
  }
1417
1418
  return $url;
1419
}
1420
1421
/**
1422
 * Validates a URL.
1423 7fe061e8 Assos Assos
 *
1424 39a181a4 Assos Assos
 * @param string $text
1425 7fe061e8 Assos Assos
 *   Url to be validated.
1426 39a181a4 Assos Assos
 * @param string $langcode
1427 7fe061e8 Assos Assos
 *   An optional language code to look up the path in.
1428
 *
1429 39a181a4 Assos Assos
 * @return bool
1430 7fe061e8 Assos Assos
 *   True if a valid link, FALSE otherwise.
1431
 */
1432
function link_validate_url($text, $langcode = NULL) {
1433 39a181a4 Assos Assos
1434
  $text = _link_clean_relative($text);
1435 7fe061e8 Assos Assos
  $text = link_cleanup_url($text);
1436
  $type = link_url_type($text);
1437
1438
  if ($type && ($type == LINK_INTERNAL || $type == LINK_EXTERNAL)) {
1439 8e7483ab Assos Assos
    $flag = valid_url($text, $type == LINK_EXTERNAL);
1440 7fe061e8 Assos Assos
    if (!$flag) {
1441
      $normal_path = drupal_get_normal_path($text, $langcode);
1442
      $parsed_link = parse_url($normal_path, PHP_URL_PATH);
1443
      if ($normal_path != $parsed_link) {
1444
        $normal_path = $parsed_link;
1445
      }
1446
      $flag = drupal_valid_path($normal_path);
1447
    }
1448
    if (!$flag) {
1449
      $flag = file_exists($normal_path);
1450
    }
1451
    if (!$flag) {
1452
      $uri = file_build_uri($normal_path);
1453
      $flag = file_exists($uri);
1454
    }
1455
  }
1456
  else {
1457
    $flag = (bool) $type;
1458
  }
1459
1460
  return $flag;
1461
}
1462
1463 39a181a4 Assos Assos
/**
1464
 * Cleaner of relatives urls.
1465
 *
1466
 * @param string $url
1467
 *   The url to clean up the relative protocol.
1468
 */
1469
function _link_clean_relative($url) {
1470
  $check = substr($url, 0, 2);
1471
  if (isset($_SERVER['HTTPS']) &&
1472
    ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] == 1) ||
1473
    isset($_SERVER['HTTP_X_FORWARDED_PROTO']) &&
1474
    $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
1475
    $protocol = 'https://';
1476
  }
1477
  else {
1478
    $protocol = 'http://';
1479
  }
1480
1481
  if ($check == '//') {
1482
    $url = str_replace('//', $protocol, $url);
1483
  }
1484
1485
  return $url;
1486
}
1487
1488 7fe061e8 Assos Assos
/**
1489
 * Type check a URL.
1490
 *
1491 85ad3d82 Assos Assos
 * Accepts all URLs following RFC 1738 standard for URL formation and all e-mail
1492
 * addresses following the RFC 2368 standard for mailto address formation.
1493
 *
1494
 * @param string $text
1495 7fe061e8 Assos Assos
 *   Url to be checked.
1496 39a181a4 Assos Assos
 *
1497 85ad3d82 Assos Assos
 * @return mixed
1498
 *   Returns boolean FALSE if the URL is not valid. On success, returns one of
1499
 *   the LINK_(linktype) constants.
1500
 */
1501 7fe061e8 Assos Assos
function link_url_type($text) {
1502 85ad3d82 Assos Assos
  // @TODO Complete letters.
1503 39a181a4 Assos Assos
  // @codingStandardsIgnoreStart
1504
  $link_ichars_domain = (string) html_entity_decode(implode("", array(
1505
    "&#x00BF;", // ¿
1506 85ad3d82 Assos Assos
    "&#x00C0;", // À
1507
    "&#x00C1;", // Á
1508
    "&#x00C2;", // Â
1509 39a181a4 Assos Assos
    "&#x00C3;", // Ã
1510 85ad3d82 Assos Assos
    "&#x00C4;", // Ä
1511 39a181a4 Assos Assos
    "&#x00C5;", // Å
1512
    "&#x00C6;", // Æ
1513 85ad3d82 Assos Assos
    "&#x00C7;", // Ç
1514
    "&#x00C8;", // È
1515
    "&#x00C9;", // É
1516
    "&#x00CA;", // Ê
1517
    "&#x00CB;", // Ë
1518 39a181a4 Assos Assos
    "&#x00CC;", // Ì
1519
    "&#x00CD;", // Í
1520 85ad3d82 Assos Assos
    "&#x00CE;", // Î
1521
    "&#x00CF;", // Ï
1522 39a181a4 Assos Assos
    "&#x00D0;", // Ð
1523
    "&#x00D1;", // Ñ
1524
    "&#x00D2;", // Ò
1525
    "&#x00D3;", // Ó
1526 85ad3d82 Assos Assos
    "&#x00D4;", // Ô
1527
    "&#x00D5;", // Õ
1528 39a181a4 Assos Assos
    "&#x00D6;", // Ö
1529
    // ×
1530
    "&#x00D8;", // Ø
1531 85ad3d82 Assos Assos
    "&#x00D9;", // Ù
1532 39a181a4 Assos Assos
    "&#x00DA;", // Ú
1533 85ad3d82 Assos Assos
    "&#x00DB;", // Û
1534 39a181a4 Assos Assos
    "&#x00DC;", // Ü
1535
    "&#x00DD;", // Ý
1536 85ad3d82 Assos Assos
    "&#x00DE;", // Þ
1537 39a181a4 Assos Assos
    // ß (see LINK_ICHARS)
1538
    "&#x00E0;", // à
1539
    "&#x00E1;", // á
1540
    "&#x00E2;", // â
1541
    "&#x00E3;", // ã
1542
    "&#x00E4;", // ä
1543
    "&#x00E5;", // å
1544
    "&#x00E6;", // æ
1545
    "&#x00E7;", // ç
1546
    "&#x00E8;", // è
1547
    "&#x00E9;", // é
1548
    "&#x00EA;", // ê
1549
    "&#x00EB;", // ë
1550
    "&#x00EC;", // ì
1551
    "&#x00ED;", // í
1552
    "&#x00EE;", // î
1553
    "&#x00EF;", // ï
1554
    "&#x00F0;", // ð
1555
    "&#x00F1;", // ñ
1556
    "&#x00F2;", // ò
1557
    "&#x00F3;", // ó
1558
    "&#x00F4;", // ô
1559
    "&#x00F5;", // õ
1560
    "&#x00F6;", // ö
1561
    // ÷
1562
    "&#x00F8;", // ø
1563
    "&#x00F9;", // ù
1564
    "&#x00FA;", // ú
1565
    "&#x00FB;", // û
1566
    "&#x00FC;", // ü
1567 85ad3d82 Assos Assos
    "&#x00FD;", // ý
1568 39a181a4 Assos Assos
    "&#x00FE;", // þ
1569
    "&#x00FF;", // ÿ
1570
    "&#x0152;", // Œ
1571
    "&#x0153;", // œ
1572
    "&#x0178;", // Ÿ
1573 85ad3d82 Assos Assos
  )), ENT_QUOTES, 'UTF-8');
1574 39a181a4 Assos Assos
  // @codingStandardsIgnoreEnd
1575
1576
  $link_ichars = $link_ichars_domain . (string) html_entity_decode(implode("", array(
1577
      // ß.
1578 8e7483ab Assos Assos
    "&#x00DF;",
1579
  )), ENT_QUOTES, 'UTF-8');
1580 39a181a4 Assos Assos
  $allowed_protocols = variable_get('filter_allowed_protocols', array(
1581
    'http',
1582
    'https',
1583
    'ftp',
1584 8e7483ab Assos Assos
    'file',
1585 39a181a4 Assos Assos
    'news',
1586
    'nntp',
1587
    'telnet',
1588
    'mailto',
1589
    'irc',
1590
    'ssh',
1591
    'sftp',
1592
    'webcal',
1593 8e7483ab Assos Assos
    'tel',
1594 39a181a4 Assos Assos
  ));
1595
  $link_domains = _link_domains();
1596
1597
  // Starting a parenthesis group with (?: means that it is grouped, but is not
1598
  // captured.
1599 85ad3d82 Assos Assos
  $protocol = '((?:' . implode("|", $allowed_protocols) . '):\/\/)';
1600 39a181a4 Assos Assos
  $authentication = "(?:(?:(?:[\w\.\-\+!$&'\(\)*\+,;=" . $link_ichars . "]|%[0-9a-f]{2})+(?::(?:[\w" . $link_ichars . "\.\-\+%!$&'\(\)*\+,;=]|%[0-9a-f]{2})*)?)?@)";
1601
  $domain = '(?:(?:[a-z0-9' . $link_ichars_domain . ']([a-z0-9' . $link_ichars_domain . '\-_\[\]])*)(\.(([a-z0-9' . $link_ichars_domain . '\-_\[\]])+\.)*(' . $link_domains . '|[a-z]{2}))?)';
1602 85ad3d82 Assos Assos
  $ipv4 = '(?:[0-9]{1,3}(\.[0-9]{1,3}){3})';
1603
  $ipv6 = '(?:[0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7})';
1604
  $port = '(?::([0-9]{1,5}))';
1605
  // Pattern specific to external links.
1606
  $external_pattern = '/^' . $protocol . '?' . $authentication . '?(' . $domain . '|' . $ipv4 . '|' . $ipv6 . ' |localhost)' . $port . '?';
1607
1608
  // Pattern specific to internal links.
1609 39a181a4 Assos Assos
  $internal_pattern = "/^(?:[a-z0-9" . $link_ichars . "_\-+\[\] ]+)";
1610
  $internal_pattern_file = "/^(?:[a-z0-9" . $link_ichars . "_\-+\[\]\. \/\(\)][a-z0-9" . $link_ichars . "_\-+\[\]\. \(\)][a-z0-9" . $link_ichars . "_\-+\[\]\. \/\(\)]+)$/i";
1611 85ad3d82 Assos Assos
1612 39a181a4 Assos Assos
  $directories = "(?:\/[a-z0-9" . $link_ichars . "_\-\.~+%=&,$'#!():;*@\[\]]*)*";
1613 85ad3d82 Assos Assos
  // Yes, four backslashes == a single backslash.
1614 8e7483ab Assos Assos
  $query = "(?:\/?\?([?a-zA-Z0-9" . $link_ichars . "+_|\-\.~\/\\\\%=&,$'!():;*@\[\]{} ]*))";
1615
  $anchor = "(?:#[a-zA-Z0-9" . $link_ichars . "_\-\.~+%=&,$'():;*@\[\]\/\?!]*)";
1616 85ad3d82 Assos Assos
1617
  // The rest of the path for a standard URL.
1618 39a181a4 Assos Assos
  // @codingStandardsIgnoreLine
1619 85ad3d82 Assos Assos
  $end = $directories . '?' . $query . '?' . $anchor . '?' . '$/i';
1620
1621
  $message_id = '[^@].*@' . $domain;
1622
  $newsgroup_name = '(?:[0-9a-z+-]*\.)*[0-9a-z+-]*';
1623
  $news_pattern = '/^news:(' . $newsgroup_name . '|' . $message_id . ')$/i';
1624
1625 39a181a4 Assos Assos
  $user = '[a-zA-Z0-9' . $link_ichars . '_\-\.\+\^!#\$%&*+\/\=\?\`\|\{\}~\'\[\]]+';
1626 85ad3d82 Assos Assos
  $email_pattern = '/^mailto:' . $user . '@' . '(?:' . $domain . '|' . $ipv4 . '|' . $ipv6 . '|localhost)' . $query . '?$/';
1627 8e7483ab Assos Assos
  $tel_pattern = '/^tel:(?:\+[1-9]\d{1,14}|\d{2,15})$/';
1628
1629
  $file_pattern = "/^(?:file:\/\/)" . "(?:\/?[a-z0-9" . $link_ichars . "_\-\.\\\~+%=&,$'#!():;*@\[\]]*)*" . '$/i';
1630 85ad3d82 Assos Assos
1631
  if (strpos($text, '<front>') === 0) {
1632
    return LINK_FRONT;
1633
  }
1634
  if (in_array('mailto', $allowed_protocols) && preg_match($email_pattern, $text)) {
1635
    return LINK_EMAIL;
1636
  }
1637 8e7483ab Assos Assos
  if (strpos($text, '#') === 0) {
1638
    return LINK_FRAGMENT;
1639
  }
1640
  if (strpos($text, '?') === 0) {
1641
    return LINK_QUERY;
1642
  }
1643
  if (in_array('tel', $allowed_protocols) && strpos($text, 'tel:') === 0) {
1644
    if (preg_match($tel_pattern, $text)) {
1645
      // Based on our tel pattern this is a 'valid' phone number so return tel
1646
      // type.
1647
      return LINK_TEL;
1648
    }
1649
    else {
1650
      // Based on our tel pattern this is using the tel protocol, but is not a
1651
      // 'valid' phone number. If we don't return false here $text will match
1652
      // LINK_EXTERNAL which is incorrect.
1653
      return FALSE;
1654
    }
1655
  }
1656 85ad3d82 Assos Assos
  if (in_array('news', $allowed_protocols) && preg_match($news_pattern, $text)) {
1657
    return LINK_NEWS;
1658
  }
1659 8e7483ab Assos Assos
  if (in_array('file', $allowed_protocols) && preg_match($file_pattern, $text, $as)) {
1660
    return LINK_FILE;
1661
  }
1662 85ad3d82 Assos Assos
  if (preg_match($internal_pattern . $end, $text)) {
1663
    return LINK_INTERNAL;
1664
  }
1665 39a181a4 Assos Assos
  if (drupal_valid_path($text) && url_is_external($text) == FALSE) {
1666
    return LINK_INTERNAL;
1667
  }
1668 85ad3d82 Assos Assos
  if (preg_match($external_pattern . $end, $text)) {
1669
    return LINK_EXTERNAL;
1670
  }
1671
  if (preg_match($internal_pattern_file, $text)) {
1672
    return LINK_INTERNAL;
1673
  }
1674
1675
  return FALSE;
1676
}
1677
1678
/**
1679 39a181a4 Assos Assos
 * Returns the list of allowed domains.
1680
 *
1681
 * If the variable link_allowed_domains is set, restrict allowed domains to the
1682
 * strings in that array. If the variable link_allowed_domains is not set, allow
1683
 * all domains between 2 and 63 characters in length.
1684
 * See https://tools.ietf.org/html/rfc1034.
1685 85ad3d82 Assos Assos
 */
1686
function _link_domains() {
1687 39a181a4 Assos Assos
  $link_allowed_domains = variable_get('link_allowed_domains', array());
1688
  return empty($link_allowed_domains) ? '[a-z][a-z0-9-]{1,62}' : implode('|', $link_allowed_domains);
1689 85ad3d82 Assos Assos
}
1690
1691
/**
1692
 * Implements hook_migrate_field_alter().
1693
 */
1694
function link_content_migrate_field_alter(&$field_value, $instance_value) {
1695
  if ($field_value['type'] == 'link') {
1696
    // Adjust the field type.
1697
    $field_value['type'] = 'link_field';
1698
    // Remove settings that are now on the instance.
1699 39a181a4 Assos Assos
    foreach (array(
1700 8e7483ab Assos Assos
      'attributes',
1701
      'display',
1702
      'url',
1703
      'title',
1704
      'title_value',
1705
      'enable_tokens',
1706
      'validate_url',
1707
    ) as $setting) {
1708 85ad3d82 Assos Assos
      unset($field_value['settings'][$setting]);
1709
    }
1710
  }
1711
}
1712
1713
/**
1714
 * Implements hook_migrate_instance_alter().
1715
 *
1716
 * Widget type also changed to link_field.
1717
 */
1718
function link_content_migrate_instance_alter(&$instance_value, $field_value) {
1719
  if ($field_value['type'] == 'link') {
1720
    // Grab settings that were previously on the field.
1721 39a181a4 Assos Assos
    foreach (array(
1722 8e7483ab Assos Assos
      'attributes',
1723
      'display',
1724
      'url',
1725
      'title',
1726
      'title_value',
1727
      'enable_tokens',
1728
      'validate_url',
1729
    ) as $setting) {
1730 85ad3d82 Assos Assos
      if (isset($field_value['settings'][$setting])) {
1731
        $instance_value['settings'][$setting] = $field_value['settings'][$setting];
1732
      }
1733
    }
1734
    // Adjust widget type.
1735
    if ($instance_value['widget']['type'] == 'link') {
1736
      $instance_value['widget']['type'] = 'link_field';
1737
    }
1738
    // Adjust formatter types.
1739
    foreach ($instance_value['display'] as $context => $settings) {
1740 39a181a4 Assos Assos
      if (in_array($settings['type'], array(
1741
        'default',
1742
        'title_plain',
1743
        'url',
1744
        'plain',
1745
        'short',
1746
        'label',
1747
        'separate',
1748
      ))) {
1749 85ad3d82 Assos Assos
        $instance_value['display'][$context]['type'] = 'link_' . $settings['type'];
1750
      }
1751
    }
1752
  }
1753
}
1754
1755
/**
1756
 * Implements hook_field_settings_form().
1757
 */
1758
function link_field_settings_form() {
1759
  return array();
1760
}
1761
1762
/**
1763
 * Additional callback to adapt the property info of link fields.
1764 39a181a4 Assos Assos
 *
1765 c8740e19 Assos Assos
 * @see entity_metadata_field_entity_property_info()
1766 85ad3d82 Assos Assos
 */
1767
function link_field_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
1768
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$field['field_name']];
1769
  // Define a data structure so it's possible to deal with both the link title
1770
  // and URL.
1771
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
1772
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
1773
1774
  // Auto-create the field item as soon as a property is set.
1775
  $property['auto creation'] = 'link_field_item_create';
1776
1777
  $property['property info'] = link_field_item_property_info();
1778 8e7483ab Assos Assos
  $property['property info']['url']['required'] = $instance['required'] && !$instance['settings']['url'];
1779
  $property['property info']['title']['required'] = $instance['required'] && ($instance['settings']['title'] == 'required');
1780 85ad3d82 Assos Assos
  if ($instance['settings']['title'] == 'none') {
1781
    unset($property['property info']['title']);
1782
  }
1783
  unset($property['query callback']);
1784
}
1785
1786
/**
1787
 * Callback for creating a new, empty link field item.
1788
 *
1789
 * @see link_field_property_info_callback()
1790
 */
1791
function link_field_item_create() {
1792 39a181a4 Assos Assos
  return array('title' => NULL, 'url' => NULL, 'display_url' => NULL);
1793 85ad3d82 Assos Assos
}
1794
1795
/**
1796
 * Defines info for the properties of the link-field item data structure.
1797
 */
1798
function link_field_item_property_info() {
1799
  $properties['title'] = array(
1800
    'type' => 'text',
1801
    'label' => t('The title of the link.'),
1802
    'setter callback' => 'entity_property_verbatim_set',
1803
  );
1804
  $properties['url'] = array(
1805 39a181a4 Assos Assos
    'type' => 'text',
1806 85ad3d82 Assos Assos
    'label' => t('The URL of the link.'),
1807
    'setter callback' => 'entity_property_verbatim_set',
1808
  );
1809 c8740e19 Assos Assos
  $properties['attributes'] = array(
1810
    'type' => 'struct',
1811
    'label' => t('The attributes of the link.'),
1812
    'setter callback' => 'entity_property_verbatim_set',
1813
    'getter callback' => 'link_attribute_property_get',
1814
  );
1815 39a181a4 Assos Assos
  $properties['display_url'] = array(
1816
    'type' => 'uri',
1817
    'label' => t('The full URL of the link.'),
1818
    'setter callback' => 'entity_property_verbatim_set',
1819
  );
1820 85ad3d82 Assos Assos
  return $properties;
1821
}
1822
1823 c8740e19 Assos Assos
/**
1824
 * Entity property info getter callback for link attributes.
1825
 */
1826
function link_attribute_property_get($data, array $options, $name, $type, $info) {
1827
  return isset($data[$name]) ? array_filter($data[$name]) : array();
1828
}
1829
1830 85ad3d82 Assos Assos
/**
1831
 * Implements hook_field_update_instance().
1832
 */
1833
function link_field_update_instance($instance, $prior_instance) {
1834 39a181a4 Assos Assos
  if (function_exists('i18n_string_update') && isset($instance['widget']) && $instance['widget']['type'] == 'link_field' && $prior_instance['settings']['title_value'] != $instance['settings']['title_value']) {
1835 85ad3d82 Assos Assos
    $i18n_string_name = "field:{$instance['field_name']}:{$instance['bundle']}:title_value";
1836
    i18n_string_update($i18n_string_name, $instance['settings']['title_value']);
1837
  }
1838
}
1839
1840
/**
1841
 * Implements hook_i18n_string_list_TEXTGROUP_alter().
1842
 */
1843
function link_i18n_string_list_field_alter(&$strings, $type = NULL, $object = NULL) {
1844
  if ($type != 'field_instance' || !is_array($object) || !isset($object['widget']['type'])) {
1845
    return;
1846
  }
1847
  if ($object['widget']['type'] == 'link_field' && isset($object['settings']['title_value'])) {
1848
    $strings['field'][$object['field_name']][$object['bundle']]['title_value']['string'] = $object['settings']['title_value'];
1849
  }
1850
}