Projet

Général

Profil

Paste
Télécharger (28,6 ko) Statistiques
| Branche: | Révision:

root / drupal7 / sites / all / modules / addressfield / addressfield.module @ f066bdb5

1
<?php
2

    
3
/**
4
 * @file
5
 * Defines a field for attaching country-specific addresses to entities.
6
 */
7

    
8
/**
9
 * Implements hook_ctools_plugin_directory().
10
 */
11
function addressfield_ctools_plugin_directory($module, $plugin) {
12
  if ($module == 'addressfield') {
13
    return 'plugins/' . $plugin;
14
  }
15
}
16

    
17
/**
18
 * Implements hook_ctools_plugin_type().
19
 */
20
function addressfield_ctools_plugin_type() {
21
  $plugins['format'] = array(
22
    'load themes' => TRUE,
23
  );
24
  return $plugins;
25
}
26

    
27
/**
28
 * Implements hook_views_api().
29
 */
30
function addressfield_views_api() {
31
  return array(
32
    'api' => 3,
33
    'path' => drupal_get_path('module', 'addressfield') . '/views',
34
  );
35
}
36

    
37
/**
38
 * Implements hook_module_implements_alter().
39
 *
40
 * Moves the hook_token_info_alter() implementation to the bottom so it is
41
 * invoked after all modules implementing the same hook.
42
 */
43
function addressfield_module_implements_alter(&$implementations, $hook) {
44
  if ($hook == 'token_info_alter') {
45
    // Make sure that the $implementations list is populated before altering it,
46
    // to work around a crash experienced by some people (#2181001).
47
    if (isset($implementations['addressfield'])) {
48
      $group = $implementations['addressfield'];
49
      unset($implementations['addressfield']);
50
      $implementations['addressfield'] = $group;
51
    }
52
  }
53
}
54

    
55
/**
56
 * Returns TRUE if a field map array value represents an addressfield.
57
 *
58
 * Provided for use as a callback by array_filter().
59
 */
60
function addressfield_field_map_filter($field) {
61
  return !empty($field['type']) && $field['type'] == 'addressfield';
62
}
63

    
64
/**
65
 * Get the list of format plugins.
66
 */
67
function addressfield_format_plugins() {
68
  ctools_include('plugins');
69
  $plugins = ctools_get_plugins('addressfield', 'format');
70
  uasort($plugins, 'ctools_plugin_sort');
71

    
72
  return $plugins;
73
}
74

    
75
/**
76
 * Get the list of format plugins in a format suitable for #options.
77
 */
78
function addressfield_format_plugins_options() {
79
  $options = array();
80
  foreach (addressfield_format_plugins() as $widget => $info) {
81
    $options[$widget] = check_plain($info['title']);
82
  }
83
  return $options;
84
}
85

    
86
/**
87
 * @defgroup addressfield_format Address format API
88
 * @{
89
 * API for generating address forms and display formats.
90
 *
91
 * Addresses forms and display formats are collaboratively generated by one or
92
 * more format handler plugins. An address with a name and a company, for example,
93
 * will be generated by three handlers:
94
 *   - 'address' that will generate the country, locality, street blocks
95
 *   - 'organisation' that will add the organisation block to the address
96
 *   - 'name-full' that will add a first name and last name block to the address
97
 *
98
 * A format handler is a CTools plugin of type 'addressfield' / 'format'. Each
99
 * handler is passed the format in turn, and can add to or modify the format.
100
 *
101
 * The format itself is a renderable array stub. This stub will be transformed
102
 * into either a Form API array suitable for use as part of a form or into a
103
 * renderable array suitable for use with drupal_render(). The following
104
 * modifications are done:
105
 *   - when rendering as a form, every element which name (its key in the array)
106
 *     is a valid addressfield column (see addressfield_field_schema()), will
107
 *     be transformed into a form element, either using a type explicitly
108
 *     defined in '#widget_type' or using 'select' if '#options' is set or
109
 *     'textfield' if it is not. In addition, the '#default_value' of every
110
 *     field will be populated from the address being edited.
111
 *   - when rendering as a formatter, every element which name (its key in the array)
112
 *     is a valid addressfield column (see addressfield_field_schema()), will
113
 *     be transformed into a renderable element, either using a type explicitly
114
 *     defined in '#render_type' or else using 'addressfield_container'. When
115
 *     the type is 'addressfield_container' the element will be rendered as
116
 *     an HTML element set by '#tag' (default: span).
117
 */
118

    
119
/**
120
 * Generate a format for a given address.
121
 *
122
 * @param $address
123
 *   The address format being generated.
124
 * @param $handlers
125
 *   The format handlers to use to generate the format.
126
 * @param $context
127
 *   An associative array of context information pertaining to how the address
128
 *   format should be generated. If no mode is given, it will initialize to the
129
 *   default value. The remaining context keys should only be present when the
130
 *   address format is being generated for a field:
131
 *   - mode: either 'form' or 'render'; defaults to 'render'.
132
 *   - field: the field info array.
133
 *   - instance: the field instance array.
134
 *   - langcode: the langcode of the language the field is being rendered in.
135
 *   - delta: the delta value of the given address.
136
 *
137
 * @return
138
 *   A renderable array suitable for use as part of a form (if 'mode' is 'form')
139
 *   or for formatted address output when passed to drupal_render().
140
 */
141
function addressfield_generate($address, array $handlers, array $context = array()) {
142
  // If no mode is given in the context array, default it to 'render'.
143
  if (empty($context['mode'])) {
144
    $context['mode'] = 'render';
145
  }
146

    
147
  ctools_include('plugins');
148
  $format = array();
149
  // Add the handlers, ordered by weight.
150
  $plugins = addressfield_format_plugins();
151
  $format['#handlers'] = array_intersect(array_keys($plugins), $handlers);
152

    
153
  foreach ($format['#handlers'] as $handler) {
154
    if ($callback = ctools_plugin_load_function('addressfield', 'format', $handler, 'format callback')) {
155
      $callback($format, $address, $context);
156
    }
157
  }
158

    
159
  // Store the address in the format, for processing.
160
  $format['#address'] = $address;
161

    
162
  // Post-process the format stub, depending on the rendering mode.
163
  if ($context['mode'] == 'form') {
164
    $format['#addressfield'] = TRUE;
165
    $format['#process'][] = 'addressfield_process_format_form';
166
  }
167
  elseif ($context['mode'] == 'render') {
168
    $format['#pre_render'][] = 'addressfield_render_address';
169
  }
170

    
171
  return $format;
172
}
173

    
174
/**
175
 * Generate a full-fledged form from a format snippet, as returned by addressfield_formats().
176
 */
177
function addressfield_process_format_form($format, &$form_state, $complete_form) {
178
  // Make sure to load all the plugins that participated in this format.
179
  ctools_include('plugins');
180
  foreach ($format['#handlers'] as $handler) {
181
    ctools_plugin_load_function('addressfield', 'format', $handler, 'format callback');
182
  }
183

    
184
  _addressfield_process_format_form($format, $format['#address'], $format['#required']);
185
  return $format;
186
}
187

    
188
function _addressfield_process_format_form(&$format, $address, $required) {
189
  foreach (element_children($format) as $key) {
190
    $child = &$format[$key];
191

    
192
    // Automatically convert any element in the format array to an appropriate
193
    // form element that matches one of the address component names.
194
    if (in_array($key, array('name_line', 'first_name', 'last_name', 'organisation_name', 'country', 'administrative_area', 'sub_administrative_area', 'locality', 'dependent_locality', 'postal_code', 'thoroughfare', 'premise', 'sub_premise'))) {
195
      // Set the form element type for the address component to whatever the
196
      // address format specified in its #widget_type property.
197
      if (isset($child['#widget_type'])) {
198
        $child['#type'] = $child['#widget_type'];
199
      }
200
      else {
201
        // If the element didn't specify a #widget_type and has options, turn it
202
        // into a select list and unset its #size value, which is typically used
203
        // to provide the width of a textfield.
204
        if (isset($child['#options'])) {
205
          $child['#type'] = 'select';
206
          unset($child['#size']);
207
        }
208
        else {
209
          // Otherwise go ahead and make it a textfield.
210
          $child['#type'] = 'textfield';
211
        }
212
      }
213

    
214
      if (!$required) {
215
        unset($child['#required']);
216
      }
217

    
218
      if (isset($address[$key])) {
219
        $child['#default_value'] = $address[$key];
220
      }
221
    }
222

    
223
    // Recurse through the element's children if it has any.
224
    _addressfield_process_format_form($child, $address, $required);
225
  }
226
}
227

    
228
/**
229
 * Render an address in a given format.
230
 */
231
function addressfield_render_address($format) {
232
  _addressfield_render_address($format, $format['#address']);
233
  return $format;
234
}
235

    
236
function _addressfield_render_address(&$format, $address) {
237
  foreach (element_children($format) as $key) {
238
    $child = &$format[$key];
239

    
240
    // Automatically expand elements that match one of the fields of the address
241
    // structure.
242
    if (in_array($key, array('name_line', 'first_name', 'last_name', 'organisation_name', 'country', 'administrative_area', 'sub_administrative_area', 'locality', 'dependent_locality', 'postal_code', 'thoroughfare', 'premise', 'sub_premise'), TRUE)) {
243
      if (isset($child['#render_type'])) {
244
        $child['#type'] = $child['#render_type'];
245
      }
246
      else {
247
        $child['#type'] = 'addressfield_container';
248
        if (!isset($child['#tag'])) {
249
          $child['#tag'] = 'span';
250
        }
251
      }
252

    
253
      // If the element instructs us to render the option value instead of the
254
      // raw address element value and its #options array has a matching key,
255
      // swap it out for the option value now.
256
      if (!empty($child['#render_option_value']) && isset($address[$key]) && isset($child['#options'][$address[$key]])) {
257
        $child['#children'] = check_plain($child['#options'][$address[$key]]);
258
      }
259
      elseif (isset($address[$key])) {
260
        $child['#children'] = check_plain($address[$key]);
261
      }
262
      else {
263
        $child['#children'] = '';
264
      }
265

    
266
      // Skip empty elements.
267
      if ((string) $child['#children'] === '') {
268
        $child['#access'] = FALSE;
269
      }
270

    
271
      // Add #field_prefix and #field_suffix to the prefixes and suffixes.
272
      if (isset($child['#field_prefix'])) {
273
        $child['#prefix'] = (isset($child['#prefix']) ? $child['#prefix'] : '') . $child['#field_prefix'];
274
      }
275
      if (isset($child['#field_suffix'])) {
276
        $child['#suffix'] = (isset($child['#suffix']) ? $child['#suffix'] : '') . $child['#field_suffix'];
277
      }
278
    }
279

    
280
    // Recurse through the child.
281
    _addressfield_render_address($child, $address);
282
  }
283
}
284

    
285
/**
286
 * @} End of "ingroup addressfield_format"
287
 */
288

    
289
/**
290
 * Implementation of hook_theme().
291
 */
292
function addressfield_theme() {
293
  $hooks['addressfield_container'] = array(
294
    'render element' => 'element',
295
  );
296
  return $hooks;
297
}
298

    
299
/**
300
 * Render a container for a set of address fields.
301
 */
302
function theme_addressfield_container($variables) {
303
  $element = $variables['element'];
304
  $element['#children'] = trim($element['#children']);
305
  // Remove the autocomplete attributes because the W3C validator complains.
306
  // They are only used on forms anyway.
307
  unset($element['#attributes']['autocomplete']);
308
  unset($element['#attributes']['x-autocompletetype']);
309

    
310
  if (strlen($element['#children']) > 0) {
311
    $output = '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . '>';
312
    $output .= $element['#children'];
313
    $output .= '</' . $element['#tag'] . ">";
314
    return $output;
315
  }
316
  else {
317
    return '';
318
  }
319
}
320

    
321
/**
322
 * Implementation of hook_element_info().
323
 */
324
function addressfield_element_info() {
325
  $types['addressfield_container'] = array(
326
    '#theme_wrappers' => array('addressfield_container'),
327
    '#process' => array('addressfield_widget_process'),
328
    '#attributes' => array(),
329
    '#tag' => 'div',
330
  );
331
  return $types;
332
}
333

    
334
/**
335
 * Form API process function: set the #parents of the children of this element so they appear at the same level as the parent.
336
 */
337
function addressfield_widget_process($element) {
338
  foreach (element_children($element) as $key) {
339
    $element[$key]['#parents'] = $element['#parents'];
340
    $element[$key]['#parents'][count($element[$key]['#parents']) - 1] = $key;
341
  }
342

    
343
  return $element;
344
}
345

    
346
/**
347
 * Implements hook_field_info()
348
 */
349
function addressfield_field_info() {
350
  $fields = array();
351

    
352
  $fields['addressfield'] = array(
353
    'label' => t('Postal address'),
354
    'description' => t('A field type used for storing postal addresses according the xNAL standard.'),
355
    'settings' => array(),
356
    'instance_settings' => array(),
357
    'default_widget' => 'addressfield_standard',
358
    'default_formatter' => 'addressfield_default',
359
    'property_type' => 'addressfield',
360
    'property_callbacks' => array('addressfield_property_info_callback'),
361
  );
362

    
363
  return $fields;
364
}
365

    
366
/**
367
 * Returns an array of default values for the addressfield form elements.
368
 */
369
function addressfield_default_values($available_countries = NULL) {
370
  if (!isset($available_countries)) {
371
    $available_countries = _addressfield_country_options_list();
372
  }
373

    
374
  // Use the default country of the site if possible.
375
  $default_country = variable_get('site_default_country', NULL);
376

    
377
  // If the default country is undefined or not in the list of available countries,
378
  // just fallback to the first country in the list.
379
  if (!$default_country || !isset($available_countries[$default_country])) {
380
    $default_country = key($available_countries);
381
  }
382

    
383
  return array(
384
    'country' => $default_country,
385
    'name_line' => '',
386
    'first_name' => '',
387
    'last_name' => '',
388
    'organisation_name' => '',
389
    'administrative_area' => '',
390
    'sub_administrative_area' => '',
391
    'locality' => '',
392
    'dependent_locality' => '',
393
    'postal_code' => '',
394
    'thoroughfare' => '',
395
    'premise' => '',
396
    'sub_premise' => '',
397
    'data' => '',
398
  );
399
}
400

    
401
/**
402
 * Implements hook_field_is_empty().
403
 */
404
function addressfield_field_is_empty($item, $field) {
405
  // Every address field must have at least a country value or it is considered
406
  // empty, even if it has name information.
407
  return empty($item['country']);
408
}
409

    
410
/**
411
 * Implements hook_field_presave().
412
 */
413
function addressfield_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
414
  foreach ($items as $delta => &$item) {
415
    // If the first name and last name are set but the name line isn't...
416
    if (isset($item['first_name']) && isset($item['last_name']) && !isset($item['name_line'])) {
417
      // Combine the first and last name to be the name line.
418
      $items[$delta]['name_line'] = $items[$delta]['first_name'] . ' ' . $items[$delta]['last_name'];
419
    }
420
    elseif (isset($item['name_line'])) {
421
      // Otherwise if the name line is set, separate it out into a best guess at
422
      // the first and last name.
423
      $names = explode(' ', $item['name_line']);
424

    
425
      $item['first_name'] = array_shift($names);
426
      $item['last_name'] = implode(' ', $names);
427
    }
428

    
429
    // Trim whitespace from all of the address components and convert any double
430
    // spaces to single spaces.
431
    foreach ($item as $key => &$value) {
432
      if (!in_array($key, array('data')) && is_string($value)) {
433
        $value = trim(str_replace('  ', ' ', $value));
434
      }
435
    }
436
  }
437
}
438

    
439
/**
440
 * Implements hook_field_widget_info()
441
 */
442
function addressfield_field_widget_info() {
443
  $widgets = array();
444

    
445
  $widgets['addressfield_standard'] = array(
446
    'label' => t('Dynamic address form'),
447
    'field types' => array('addressfield'),
448
    'settings' => array(
449
      'available_countries' => array(),
450
      'format_handlers' => array('address'),
451
    ),
452
  );
453

    
454
  return $widgets;
455
}
456

    
457
/**
458
 * Implements hook_field_widget_settings_form()
459
 */
460
function addressfield_field_widget_settings_form($field, $instance) {
461
  $widget = $instance['widget'];
462
  $defaults = field_info_widget_settings($widget['type']);
463
  $settings = array_merge($defaults, $widget['settings']);
464
  $form = array();
465

    
466
  if ($widget['type'] == 'addressfield_standard') {
467
    $form['available_countries'] = array(
468
      '#type' => 'select',
469
      '#multiple' => TRUE,
470
      '#title' => t('Available countries'),
471
      '#description' => t('If no countries are selected, all countries will be available.'),
472
      '#options' => _addressfield_country_options_list(),
473
      '#default_value' => $settings['available_countries'],
474
    );
475

    
476
    $form['format_handlers'] = array(
477
      '#type' => 'checkboxes',
478
      '#title' => t('Format handlers'),
479
      '#options' => addressfield_format_plugins_options(),
480
      '#default_value' => $settings['format_handlers'],
481
    );
482
  }
483

    
484
  return $form;
485
}
486

    
487
/**
488
 * Implements hook_field_widget_form()
489
 */
490
function addressfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
491
  $settings = $instance['widget']['settings'];
492

    
493
  $address = array();
494
  // If the form has been rebuilt via AJAX, use the form state values.
495
  // $form_state['values'] is empty because of #limit_validation_errors, so
496
  // $form_state['input'] needs to be used instead.
497
  $parents = array_merge($element['#field_parents'], array($element['#field_name'], $langcode, $delta));
498
  $input_address = drupal_array_get_nested_value($form_state['input'], $parents);
499
  if (!empty($input_address)) {
500
    $address = $input_address;
501
  }
502
  elseif (!empty($items[$delta]['country'])) {
503
    // Else use the saved value for the field.
504
    $address = $items[$delta];
505
  }
506

    
507
  // Determine the list of available countries, and if the currently selected
508
  // country is not in it, unset it so it can be reset to the default country.
509
  $countries = _addressfield_country_options_list($field, $instance);
510
  if (!empty($address['country']) && !isset($countries[$address['country']])) {
511
    unset($address['country']);
512
  }
513

    
514
  // Merge in default values.
515
  // Skip the country default if the field is optional, in which case it might
516
  // not have a country selected, causing the whole field to be treated as empty.
517
  // The same logic applies if the field allows multiple values, in which case
518
  // only the first delta is required.
519
  $default_values = addressfield_default_values($countries);
520
  if (empty($instance['required']) || $delta > 0) {
521
    $default_values['country'] = '';
522
  }
523
  $address += $default_values;
524

    
525
  // Add the form elements for the standard widget, which includes a country
526
  // select list at the top that reloads the available address elements when the
527
  // country is changed.
528
  if ($instance['widget']['type'] == 'addressfield_standard') {
529
    // Wrap everything in a fieldset. This is not the best looking element,
530
    // but it's the only wrapper available in Drupal we can properly use
531
    // in that context, and it is overridable if necessary.
532
    $element['#type'] = 'fieldset';
533

    
534
    if (!empty($instance['description'])) {
535
      // Checkout panes convert the fieldset into a container, causing
536
      // #description to not be rendered. Hence, a real element is added.
537
      $element['element_description'] = array(
538
        '#markup' =>  $instance['description'],
539
        '#prefix' => '<div class="fieldset-description">',
540
        '#suffix' => '</div>',
541
        '#weight' => -999,
542
      );
543
    }
544

    
545
    // Generate the address form.
546
    $context = array(
547
      'mode' => 'form',
548
      'field' => $field,
549
      'instance' => $instance,
550
      'langcode' => $langcode,
551
      'delta' => $delta,
552
    );
553
    $element += addressfield_generate($address, $settings['format_handlers'], $context);
554

    
555
    // If #required is set to FALSE, _addressfield_process_format_form() strips
556
    // it from all child elements. This is only needed on the field edit form.
557
    $element['#required'] = ($form_state['build_info']['form_id'] != 'field_ui_field_edit_form');
558
  }
559

    
560
  return $element;
561
}
562

    
563
/**
564
 * Element validate callback: rebuilds the form on country change.
565
 */
566
function addressfield_standard_country_validate($element, &$form_state) {
567
  if ($element['#default_value'] != $element['#value']) {
568
    $parents = $element['#parents'];
569
    array_pop($parents);
570
    $address = drupal_array_get_nested_value($form_state['values'], $parents);
571

    
572
    // Clear the country-specific field values.
573
    $country_specific_data = array(
574
      'dependent_locality' => '',
575
      'locality' => '',
576
      'administrative_area' => '',
577
      'postal_code' => '',
578
    );
579
    $address = array_diff_key($address, $country_specific_data);
580

    
581
    drupal_array_set_nested_value($form_state['values'], $parents, $address);
582
    drupal_array_set_nested_value($form_state['input'], $parents, $address);
583

    
584
    $form_state['rebuild'] = TRUE;
585
  }
586
}
587

    
588
/**
589
 * Ajax callback in response to a change of country in an address field.
590
 *
591
 * The only thing we have to do is to find the proper element to render.
592
 */
593
function addressfield_standard_widget_refresh($form, $form_state) {
594
  // The target element is one element below the triggering country selector.
595
  $array_parents = $form_state['triggering_element']['#array_parents'];
596
  array_pop($array_parents);
597

    
598
  // Iterate over the form parents to find the element.
599
  $element = $form;
600
  foreach ($array_parents as $name) {
601
    $element = &$element[$name];
602
    if (!empty($element['#addressfield'])) {
603
      break;
604
    }
605
  }
606

    
607
  // Return the address block, but remove the '_weight' element inserted
608
  // by the field API.
609
  unset($element['_weight']);
610

    
611
  // Replace the address field widget with the updated widget and focus on the
612
  // new country select list.
613
  $commands[] = ajax_command_replace(NULL, render($element));
614
  $commands[] = ajax_command_invoke('#' . $element['country']['#id'], 'focus');
615
  // Add the status messages inside the new addressfield's wrapper element,
616
  // just like core does.
617
  $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
618

    
619
  // Allow other modules to add arbitrary AJAX commands on the refresh.
620
  drupal_alter('addressfield_standard_widget_refresh', $commands, $form, $form_state);
621

    
622
  return array('#type' => 'ajax', '#commands' => $commands);
623
}
624

    
625
/**
626
 * Implements hook_field_formatter_info().
627
 */
628
function addressfield_field_formatter_info() {
629
  return array(
630
    'addressfield_default' => array(
631
      'label' => t('Default'),
632
      'field types' => array('addressfield'),
633
      'settings' => array(
634
        'use_widget_handlers' => 1,
635
        'format_handlers' => array('address'),
636
      ),
637
    ),
638
  );
639
}
640

    
641
/**
642
 * Implements hook_field_formatter_settings_form().
643
 */
644
function addressfield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
645
  $display = $instance['display'][$view_mode];
646
  $settings = $display['settings'];
647

    
648
  $element['use_widget_handlers'] = array(
649
    '#type' => 'checkbox',
650
    '#title' => t('Use the same configuration as the widget.'),
651
    '#default_value' => !empty($settings['use_widget_handlers']),
652
  );
653

    
654
  $element['format_handlers'] = array(
655
    '#type' => 'checkboxes',
656
    '#title' => t('Format handlers'),
657
    '#options' => addressfield_format_plugins_options(),
658
    '#default_value' => $settings['format_handlers'],
659
    '#process' => array('form_process_checkboxes', '_addressfield_field_formatter_settings_form_process_add_state'),
660
    '#element_validate' => array('_addressfield_field_formatter_settings_form_validate')
661
  );
662

    
663
  return $element;
664
}
665

    
666
/**
667
 * Helper function: set the proper #states to the use widget handlers checkbox.
668
 */
669
function _addressfield_field_formatter_settings_form_process_add_state($element, $form_state) {
670
  // Build a #parents based on the current checkbox.
671
  $target_parents = array_slice($element['#parents'], 0, -1);
672
  $target_parents[] = 'use_widget_handlers';
673
  $target_parents = array_shift($target_parents) . ($target_parents ? '[' . implode('][', $target_parents) . ']' : '');
674

    
675
  $element['#states']['visible'] = array(
676
    ':input[name="' . $target_parents . '"]' => array('checked' => FALSE),
677
  );
678

    
679
  return $element;
680
}
681

    
682
/**
683
 * Helper function: filter the results of the checkboxes form element.
684
 */
685
function _addressfield_field_formatter_settings_form_validate($element, &$element_state) {
686
  form_set_value($element, array_filter($element['#value']), $element_state);
687
}
688

    
689
/**
690
 * Implements hook_field_formatter_settings_summary().
691
 */
692
function addressfield_field_formatter_settings_summary($field, $instance, $view_mode) {
693
  $display = $instance['display'][$view_mode];
694
  $settings = $display['settings'];
695

    
696
  $summary = '';
697

    
698
  if ($settings['use_widget_handlers']) {
699
    return t('Use widget configuration');
700
  }
701
  else {
702
    $summary = array();
703
    $plugins = addressfield_format_plugins();
704
    foreach ($settings['format_handlers'] as $handler) {
705
      $summary[] = $plugins[$handler]['title'];
706
    }
707
    return $summary ? implode(', ', $summary) : t('No handler');
708
  }
709
}
710

    
711
/**
712
 * Implements hook_field_formatter_view().
713
 */
714
function addressfield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
715
  $settings = $display['settings'];
716
  $element = array();
717

    
718
  switch ($display['type']) {
719
    case 'addressfield_default':
720
      if (!empty($settings['use_widget_handlers'])) {
721
        $handlers = $instance['widget']['settings']['format_handlers'];
722
      }
723
      else {
724
        $handlers = $settings['format_handlers'];
725
      }
726
      foreach ($items as $delta => $address) {
727
        // Generate the address format.
728
        $context = array(
729
          'mode' => 'render',
730
          'field' => $field,
731
          'instance' => $instance,
732
          'langcode' => $langcode,
733
          'delta' => $delta,
734
        );
735
        $element[$delta] = addressfield_generate($address, $handlers, $context);
736
      }
737
      break;
738
  }
739

    
740
  return $element;
741
}
742

    
743
/**
744
 * Callback to alter the property info of address fields.
745
 *
746
 * @see addressfield_field_info().
747
 */
748
function addressfield_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
749
  $name = $field['field_name'];
750
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
751

    
752
  $property['type'] = ($field['cardinality'] != 1) ? 'list<addressfield>' : 'addressfield';
753
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
754
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
755
  $property['auto creation'] = 'addressfield_auto_creation';
756
  $property['property info'] = addressfield_data_property_info();
757

    
758
  unset($property['query callback']);
759
}
760

    
761
/**
762
 * Auto creation callback for an addressfield value array.
763
 *
764
 * @see addressfield_property_info_callback()
765
 */
766
function addressfield_auto_creation() {
767
  // We can't call addressfield_default_values() directly, because it has an
768
  // optional array argument that will receive an invalid value when Entity API
769
  // tries to call it directly with its usual $property_name and $context
770
  // arguments.
771
  return addressfield_default_values();
772
}
773

    
774
/**
775
 * Defines info for the properties of the address field data structure.
776
 */
777
function addressfield_data_property_info($name = NULL) {
778
  // Build an array of basic property information for the address field.
779
  $properties = array(
780
    'country' => array(
781
      'label' => t('Country'),
782
      'options list' => '_addressfield_country_options_list',
783
    ),
784
    'name_line' => array(
785
      'label' => t('Full name'),
786
    ),
787
    'first_name' => array(
788
      'label' => t('First name'),
789
    ),
790
    'last_name' => array(
791
      'label' => t('Last name'),
792
    ),
793
    'organisation_name' => array(
794
      'label' => t('Company'),
795
    ),
796
    'administrative_area' => array(
797
      'label' => t('Administrative area (i.e. State / Province)'),
798
    ),
799
    'sub_administrative_area' => array(
800
      'label' => t('Sub administrative area'),
801
    ),
802
    'locality' => array(
803
      'label' => t('Locality (i.e. City)'),
804
    ),
805
    'dependent_locality' => array(
806
      'label' => t('Dependent locality'),
807
    ),
808
    'postal_code' => array(
809
      'label' => t('Postal code'),
810
    ),
811
    'thoroughfare' => array(
812
      'label' => t('Thoroughfare (i.e. Street address)'),
813
    ),
814
    'premise' => array(
815
      'label' => t('Premise (i.e. Apartment / Suite number)'),
816
    ),
817
    'sub_premise' => array(
818
      'label' => t('Sub Premise (i.e. Suite, Apartment, Floor, Unknown.'),
819
    ),
820
  );
821

    
822
  // Add the default values for each of the address field properties.
823
  foreach ($properties as $key => &$value) {
824
    $value += array(
825
      'description' => !empty($name) ? t('!label of field %name', array('!label' => $value['label'], '%name' => $name)) : '',
826
      'type' => 'text',
827
      'getter callback' => 'entity_property_verbatim_get',
828
      'setter callback' => 'entity_property_verbatim_set',
829
    );
830
  }
831

    
832
  return $properties;
833
}
834

    
835
/**
836
 * Wraps country_get_list() for use as an Entity API options list.
837
 */
838
function _addressfield_country_options_list($field = NULL, $instance = NULL) {
839
  // Necessary for country_get_list().
840
  require_once DRUPAL_ROOT . '/includes/locale.inc';
841

    
842
  $countries = country_get_list();
843

    
844
  if (isset($field)) {
845
    // If the instance is not specified, loop against all the instances of the field.
846
    if (!isset($instance)) {
847
      $instances = array();
848
      foreach ($field['bundles'] as $entity_type => $bundles) {
849
        foreach ($bundles as $bundle_name) {
850
          $instances[] = field_info_instance($entity_type, $field['field_name'], $bundle_name);
851
        }
852
      }
853
    }
854
    else {
855
      $instances = array($instance);
856
    }
857

    
858
    foreach ($instances as $instance) {
859
      if (!empty($instance['widget']['settings']['available_countries'])) {
860
        $countries = array_intersect_key($countries, $instance['widget']['settings']['available_countries']);
861
      }
862
      else {
863
        // This instance allow all the countries.
864
        $countries = country_get_list();
865
        break;
866
      }
867
    }
868
  }
869

    
870
  return $countries;
871
}