Projet

Général

Profil

Paste
Télécharger (70,5 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / link / link.module @ bad4e148

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