Projet

Général

Profil

Paste
Télécharger (29,8 ko) Statistiques
| Branche: | Révision:

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

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']);
185
  return $format;
186
}
187

    
188
function _addressfield_process_format_form(&$format, $address) {
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 (isset($address[$key])) {
215
        $child['#default_value'] = $address[$key];
216
      }
217
    }
218

    
219
    // Recurse through the element's children if it has any.
220
    _addressfield_process_format_form($child, $address);
221
  }
222
}
223

    
224
/**
225
 * Render an address in a given format.
226
 */
227
function addressfield_render_address($format) {
228
  _addressfield_render_address($format, $format['#address']);
229
  return $format;
230
}
231

    
232
function _addressfield_render_address(&$format, $address) {
233
  foreach (element_children($format) as $key) {
234
    $child = &$format[$key];
235

    
236
    // Automatically expand elements that match one of the fields of the address
237
    // structure.
238
    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)) {
239
      if (isset($child['#render_type'])) {
240
        $child['#type'] = $child['#render_type'];
241
      }
242
      else {
243
        $child['#type'] = 'addressfield_container';
244
        if (!isset($child['#tag'])) {
245
          $child['#tag'] = 'span';
246
        }
247
      }
248

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

    
262
      // Skip empty elements.
263
      if ((string) $child['#children'] === '') {
264
        $child['#access'] = FALSE;
265
      }
266

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

    
276
    // Recurse through the child.
277
    _addressfield_render_address($child, $address);
278
  }
279
}
280

    
281
/**
282
 * @} End of "ingroup addressfield_format"
283
 */
284

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

    
295
/**
296
 * Render a container for a set of address fields.
297
 */
298
function theme_addressfield_container($variables) {
299
  $element = $variables['element'];
300
  $element['#children'] = trim($element['#children']);
301
  // Remove the autocomplete attribute because the W3C validator complains.
302
  // It's only used on forms anyway.
303
  unset($element['#attributes']['autocomplete']);
304

    
305
  if (strlen($element['#children']) > 0) {
306
    $output = '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . '>';
307
    $output .= $element['#children'];
308
    $output .= '</' . $element['#tag'] . ">";
309
    return $output;
310
  }
311
  else {
312
    return '';
313
  }
314
}
315

    
316
/**
317
 * Implementation of hook_element_info().
318
 */
319
function addressfield_element_info() {
320
  $types['addressfield_container'] = array(
321
    '#theme_wrappers' => array('addressfield_container'),
322
    '#process' => array('addressfield_widget_process'),
323
    '#attributes' => array(),
324
    '#tag' => 'div',
325
  );
326
  return $types;
327
}
328

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

    
338
  return $element;
339
}
340

    
341
/**
342
 * Implements hook_field_info()
343
 */
344
function addressfield_field_info() {
345
  $fields = array();
346

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

    
358
  return $fields;
359
}
360

    
361
/**
362
 * Returns an array of default values for the addressfield form elements.
363
 *
364
 * @param $field
365
 *   The field array.
366
 * @param $instance
367
 *   The instance array.
368
 * @param $address
369
 *   The current address values, if known. Allows for per-country defaults.
370
 *
371
 * @return
372
 *   An array of default values.
373
 */
374
function addressfield_default_values($field, $instance, array $address = array()) {
375
  $available_countries = _addressfield_country_options_list($field, $instance);
376
  $default_country = $instance['widget']['settings']['default_country'];
377
  // Resolve the special site_default option.
378
  if ($default_country == 'site_default') {
379
    $default_country = variable_get('site_default_country', '');
380
  }
381
  // Fallback to the first country in the list if the default country is not
382
  // available, or is empty even though the field is required.
383
  $not_available = $default_country && !isset($available_countries[$default_country]);
384
  $empty_but_required = empty($default_country) && !empty($instance['required']);
385
  if ($not_available || $empty_but_required) {
386
    $default_country = key($available_countries);
387
  }
388

    
389
  $default_values = array(
390
    'country' => $default_country,
391
    'name_line' => '',
392
    'first_name' => '',
393
    'last_name' => '',
394
    'organisation_name' => '',
395
    'administrative_area' => '',
396
    'sub_administrative_area' => '',
397
    'locality' => '',
398
    'dependent_locality' => '',
399
    'postal_code' => '',
400
    'thoroughfare' => '',
401
    'premise' => '',
402
    'sub_premise' => '',
403
    'data' => '',
404
  );
405

    
406
  // Allow other modules to alter the default values.
407
  $context = array(
408
    'field' => $field,
409
    'instance' => $instance,
410
    'address' => $address,
411
  );
412
  drupal_alter('addressfield_default_values', $default_values, $context);
413

    
414
  return $default_values;
415
}
416

    
417
/**
418
 * Implements hook_field_is_empty().
419
 */
420
function addressfield_field_is_empty($item, $field) {
421
  // Every address field must have at least a country value or it is considered
422
  // empty, even if it has name information.
423
  return empty($item['country']);
424
}
425

    
426
/**
427
 * Implements hook_field_presave().
428
 */
429
function addressfield_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
430
  foreach ($items as $delta => &$item) {
431
    // If the first name and last name are set but the name line isn't...
432
    if (isset($item['first_name']) && isset($item['last_name']) && !isset($item['name_line'])) {
433
      // Combine the first and last name to be the name line.
434
      $items[$delta]['name_line'] = $items[$delta]['first_name'] . ' ' . $items[$delta]['last_name'];
435
    }
436
    elseif (isset($item['name_line'])) {
437
      // Otherwise if the name line is set, separate it out into a best guess at
438
      // the first and last name.
439
      $names = explode(' ', $item['name_line']);
440

    
441
      $item['first_name'] = array_shift($names);
442
      $item['last_name'] = implode(' ', $names);
443
    }
444

    
445
    // Trim whitespace from all of the address components and convert any double
446
    // spaces to single spaces.
447
    foreach ($item as $key => &$value) {
448
      if (!in_array($key, array('data')) && is_string($value)) {
449
        $value = trim(str_replace('  ', ' ', $value));
450
      }
451
    }
452
  }
453
}
454

    
455
/**
456
 * Implements hook_field_widget_info()
457
 */
458
function addressfield_field_widget_info() {
459
  $widgets = array();
460

    
461
  $widgets['addressfield_standard'] = array(
462
    'label' => t('Dynamic address form'),
463
    'field types' => array('addressfield'),
464
    'settings' => array(
465
      'available_countries' => array(),
466
      // Can't use variable_get('site_default_country') here because it would
467
      // set the value in stone. Instead, the site_default option allows the
468
      // default country to always reflect the current site setting.
469
      'default_country' => 'site_default',
470
      'format_handlers' => array('address'),
471
    ),
472
  );
473

    
474
  return $widgets;
475
}
476

    
477
/**
478
 * Implements hook_field_widget_settings_form()
479
 */
480
function addressfield_field_widget_settings_form($field, $instance) {
481
  $widget = $instance['widget'];
482
  $defaults = field_info_widget_settings($widget['type']);
483
  $settings = array_merge($defaults, $widget['settings']);
484
  $form = array();
485

    
486
  if ($widget['type'] == 'addressfield_standard') {
487
    $form['available_countries'] = array(
488
      '#type' => 'select',
489
      '#multiple' => TRUE,
490
      '#title' => t('Available countries'),
491
      '#description' => t('If no countries are selected, all countries will be available.'),
492
      '#options' => _addressfield_country_options_list(),
493
      '#default_value' => $settings['available_countries'],
494
    );
495
    $form['default_country'] = array(
496
      '#type' => 'select',
497
      '#title' => t('Default country'),
498
      '#options' => array('site_default' => t('- Site default -')) + _addressfield_country_options_list(),
499
      '#default_value' => $settings['default_country'],
500
      '#empty_value' => '',
501
    );
502
    $form['format_handlers'] = array(
503
      '#type' => 'checkboxes',
504
      '#title' => t('Format handlers'),
505
      '#options' => addressfield_format_plugins_options(),
506
      '#default_value' => $settings['format_handlers'],
507
    );
508
  }
509

    
510
  return $form;
511
}
512

    
513
/**
514
 * Implements hook_form_BASE_FORM_ID_alter().
515
 *
516
 * Removes the default values form from the field settings page.
517
 * Allows the module to implement its own, more predictable default value
518
 * handling, getting around #1253820 and other bugs.
519
 */
520
function addressfield_form_field_ui_field_edit_form_alter(&$form, $form_state) {
521
  if ($form['#field']['type'] == 'addressfield') {
522
    $form['instance']['default_value_widget']['#access'] = FALSE;
523
  }
524
}
525

    
526
/**
527
 * Implements hook_field_widget_form()
528
 */
529
function addressfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
530
  $settings = $instance['widget']['settings'];
531

    
532
  $address = array();
533
  // If the form has been rebuilt via AJAX, use the form state values.
534
  // $form_state['values'] is empty because of #limit_validation_errors, so
535
  // $form_state['input'] needs to be used instead.
536
  $parents = array_merge($element['#field_parents'], array($element['#field_name'], $langcode, $delta));
537
  if (!empty($form_state['input'])) {
538
    $input_address = drupal_array_get_nested_value($form_state['input'], $parents);
539
  }
540
  if (!empty($input_address)) {
541
    $address = $input_address;
542
  }
543
  elseif (!empty($items[$delta]['country'])) {
544
    // Else use the saved value for the field.
545
    $address = $items[$delta];
546
  }
547

    
548
  // Determine the list of available countries, and if the currently selected
549
  // country is not in it, unset it so it can be reset to the default country.
550
  $countries = _addressfield_country_options_list($field, $instance);
551
  if (!empty($address['country']) && !isset($countries[$address['country']])) {
552
    unset($address['country']);
553
  }
554

    
555
  // Merge in default values.
556
  $address += addressfield_default_values($field, $instance, $address);
557

    
558
  // Add the form elements for the standard widget, which includes a country
559
  // select list at the top that reloads the available address elements when the
560
  // country is changed.
561
  if ($instance['widget']['type'] == 'addressfield_standard') {
562
    // Wrap everything in a fieldset. This is not the best looking element,
563
    // but it's the only wrapper available in Drupal we can properly use
564
    // in that context, and it is overridable if necessary.
565
    $element['#type'] = 'fieldset';
566

    
567
    if (!empty($instance['description'])) {
568
      // Checkout panes convert the fieldset into a container, causing
569
      // #description to not be rendered. Hence, a real element is added and
570
      // the old #description is removed.
571
      $element['#description'] = '';
572
      $element['element_description'] = array(
573
        '#markup' =>  $instance['description'],
574
        '#prefix' => '<div class="fieldset-description">',
575
        '#suffix' => '</div>',
576
        '#weight' => -999,
577
      );
578
    }
579

    
580
    // Generate the address form.
581
    $context = array(
582
      'mode' => 'form',
583
      'field' => $field,
584
      'instance' => $instance,
585
      'langcode' => $langcode,
586
      'delta' => $delta,
587
    );
588
    $element += addressfield_generate($address, $settings['format_handlers'], $context);
589

    
590
    // Remove any already saved default value.
591
    // See addressfield_form_field_ui_field_edit_form_alter() for the reasoning.
592
    if ($form_state['build_info']['form_id'] == 'field_ui_field_edit_form') {
593
      $element['#address'] = array('country' => '');
594
    }
595
  }
596

    
597
  return $element;
598
}
599

    
600
/**
601
 * Element validate callback: rebuilds the form on country change.
602
 */
603
function addressfield_standard_country_validate($element, &$form_state) {
604
  if ($element['#default_value'] != $element['#value']) {
605
    $parents = $element['#parents'];
606
    array_pop($parents);
607
    $address = drupal_array_get_nested_value($form_state['values'], $parents);
608

    
609
    // Clear the country-specific field values.
610
    $country_specific_data = array(
611
      'dependent_locality' => '',
612
      'locality' => '',
613
      'administrative_area' => '',
614
      'postal_code' => '',
615
    );
616
    $address = array_diff_key($address, $country_specific_data);
617

    
618
    drupal_array_set_nested_value($form_state['values'], $parents, $address);
619
    drupal_array_set_nested_value($form_state['input'], $parents, $address);
620

    
621
    $form_state['rebuild'] = TRUE;
622
  }
623
}
624

    
625
/**
626
 * Ajax callback in response to a change of country in an address field.
627
 *
628
 * The only thing we have to do is to find the proper element to render.
629
 */
630
function addressfield_standard_widget_refresh($form, $form_state) {
631
  // The target element is one element below the triggering country selector.
632
  $array_parents = $form_state['triggering_element']['#array_parents'];
633
  array_pop($array_parents);
634

    
635
  // Iterate over the form parents to find the element.
636
  $element = $form;
637
  foreach ($array_parents as $name) {
638
    $element = &$element[$name];
639
    if (!empty($element['#addressfield'])) {
640
      break;
641
    }
642
  }
643

    
644
  // Return the address block, but remove the '_weight' element inserted
645
  // by the field API.
646
  unset($element['_weight']);
647

    
648
  // Replace the address field widget with the updated widget and focus on the
649
  // new country select list.
650
  $commands[] = ajax_command_replace(NULL, render($element));
651
  $commands[] = ajax_command_invoke('#' . $element['country']['#id'], 'focus');
652
  // Add the status messages inside the new addressfield's wrapper element,
653
  // just like core does.
654
  $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
655

    
656
  // Allow other modules to add arbitrary AJAX commands on the refresh.
657
  drupal_alter('addressfield_standard_widget_refresh', $commands, $form, $form_state);
658

    
659
  return array('#type' => 'ajax', '#commands' => $commands);
660
}
661

    
662
/**
663
 * Implements hook_field_formatter_info().
664
 */
665
function addressfield_field_formatter_info() {
666
  return array(
667
    'addressfield_default' => array(
668
      'label' => t('Default'),
669
      'field types' => array('addressfield'),
670
      'settings' => array(
671
        'use_widget_handlers' => 1,
672
        'format_handlers' => array('address'),
673
      ),
674
    ),
675
  );
676
}
677

    
678
/**
679
 * Implements hook_field_formatter_settings_form().
680
 */
681
function addressfield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
682
  $display = $instance['display'][$view_mode];
683
  $settings = $display['settings'];
684

    
685
  $element['use_widget_handlers'] = array(
686
    '#type' => 'checkbox',
687
    '#title' => t('Use the same configuration as the widget.'),
688
    '#default_value' => !empty($settings['use_widget_handlers']),
689
  );
690

    
691
  $element['format_handlers'] = array(
692
    '#type' => 'checkboxes',
693
    '#title' => t('Format handlers'),
694
    '#options' => addressfield_format_plugins_options(),
695
    '#default_value' => $settings['format_handlers'],
696
    '#process' => array('form_process_checkboxes', '_addressfield_field_formatter_settings_form_process_add_state'),
697
    '#element_validate' => array('_addressfield_field_formatter_settings_form_validate')
698
  );
699

    
700
  return $element;
701
}
702

    
703
/**
704
 * Helper function: set the proper #states to the use widget handlers checkbox.
705
 */
706
function _addressfield_field_formatter_settings_form_process_add_state($element, $form_state) {
707
  // Build a #parents based on the current checkbox.
708
  $target_parents = array_slice($element['#parents'], 0, -1);
709
  $target_parents[] = 'use_widget_handlers';
710
  $target_parents = array_shift($target_parents) . ($target_parents ? '[' . implode('][', $target_parents) . ']' : '');
711

    
712
  $element['#states']['visible'] = array(
713
    ':input[name="' . $target_parents . '"]' => array('checked' => FALSE),
714
  );
715

    
716
  return $element;
717
}
718

    
719
/**
720
 * Helper function: filter the results of the checkboxes form element.
721
 */
722
function _addressfield_field_formatter_settings_form_validate($element, &$element_state) {
723
  form_set_value($element, array_filter($element['#value']), $element_state);
724
}
725

    
726
/**
727
 * Implements hook_field_formatter_settings_summary().
728
 */
729
function addressfield_field_formatter_settings_summary($field, $instance, $view_mode) {
730
  $display = $instance['display'][$view_mode];
731
  $settings = $display['settings'];
732

    
733
  if ($settings['use_widget_handlers']) {
734
    return t('Use widget configuration');
735
  }
736
  else {
737
    $summary = array();
738
    $plugins = addressfield_format_plugins();
739
    foreach ($settings['format_handlers'] as $handler) {
740
      $summary[] = $plugins[$handler]['title'];
741
    }
742
    return $summary ? implode(', ', $summary) : t('No handler');
743
  }
744
}
745

    
746
/**
747
 * Implements hook_field_formatter_view().
748
 */
749
function addressfield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
750
  $settings = $display['settings'];
751
  $element = array();
752

    
753
  switch ($display['type']) {
754
    case 'addressfield_default':
755
      if (!empty($settings['use_widget_handlers'])) {
756
        $handlers = $instance['widget']['settings']['format_handlers'];
757
      }
758
      else {
759
        $handlers = $settings['format_handlers'];
760
      }
761
      foreach ($items as $delta => $address) {
762
        // Generate the address format.
763
        $context = array(
764
          'mode' => 'render',
765
          'field' => $field,
766
          'instance' => $instance,
767
          'langcode' => $langcode,
768
          'delta' => $delta,
769
        );
770
        $element[$delta] = addressfield_generate($address, $handlers, $context);
771
      }
772
      break;
773
  }
774

    
775
  return $element;
776
}
777

    
778
/**
779
 * Callback to alter the property info of address fields.
780
 *
781
 * @see addressfield_field_info().
782
 */
783
function addressfield_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
784
  $name = $field['field_name'];
785
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
786

    
787
  $property['type'] = ($field['cardinality'] != 1) ? 'list<addressfield>' : 'addressfield';
788
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
789
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
790
  $property['auto creation'] = 'addressfield_auto_creation';
791
  $property['property info'] = addressfield_data_property_info();
792

    
793
  unset($property['query callback']);
794
}
795

    
796
/**
797
 * Auto creation callback for an addressfield value array.
798
 *
799
 * @see addressfield_property_info_callback()
800
 */
801
function addressfield_auto_creation($property_name, $context) {
802
  return addressfield_default_values($context['field'], $context['instance']);
803
}
804

    
805
/**
806
 * Defines info for the properties of the address field data structure.
807
 */
808
function addressfield_data_property_info($name = NULL) {
809
  // Build an array of basic property information for the address field.
810
  $properties = array(
811
    'country' => array(
812
      'label' => t('Country'),
813
      'options list' => '_addressfield_country_options_list',
814
    ),
815
    'name_line' => array(
816
      'label' => t('Full name'),
817
    ),
818
    'first_name' => array(
819
      'label' => t('First name'),
820
    ),
821
    'last_name' => array(
822
      'label' => t('Last name'),
823
    ),
824
    'organisation_name' => array(
825
      'label' => t('Company'),
826
    ),
827
    'administrative_area' => array(
828
      'label' => t('Administrative area (i.e. State / Province)'),
829
    ),
830
    'sub_administrative_area' => array(
831
      'label' => t('Sub administrative area'),
832
    ),
833
    'locality' => array(
834
      'label' => t('Locality (i.e. City)'),
835
    ),
836
    'dependent_locality' => array(
837
      'label' => t('Dependent locality'),
838
    ),
839
    'postal_code' => array(
840
      'label' => t('Postal code'),
841
    ),
842
    'thoroughfare' => array(
843
      'label' => t('Thoroughfare (i.e. Street address)'),
844
    ),
845
    'premise' => array(
846
      'label' => t('Premise (i.e. Apartment / Suite number)'),
847
    ),
848
    'sub_premise' => array(
849
      'label' => t('Sub Premise (i.e. Suite, Apartment, Floor, Unknown.'),
850
    ),
851
  );
852

    
853
  // Add the default values for each of the address field properties.
854
  foreach ($properties as $key => &$value) {
855
    $value += array(
856
      'description' => !empty($name) ? t('!label of field %name', array('!label' => $value['label'], '%name' => $name)) : '',
857
      'type' => 'text',
858
      'getter callback' => 'entity_property_verbatim_get',
859
      'setter callback' => 'entity_property_verbatim_set',
860
    );
861
  }
862

    
863
  return $properties;
864
}
865

    
866
/**
867
 * Returns the country list in a format suitable for use as an options list.
868
 */
869
function _addressfield_country_options_list($field = NULL, $instance = NULL) {
870
  if (module_exists('countries')) {
871
    $countries = countries_get_countries('name', array('enabled' => COUNTRIES_ENABLED));
872
  }
873
  else {
874
    require_once DRUPAL_ROOT . '/includes/locale.inc';
875
    $countries = country_get_list();
876
  }
877

    
878
  if (isset($field)) {
879
    // If the instance is not specified, loop against all the instances of the field.
880
    if (!isset($instance)) {
881
      $instances = array();
882
      foreach ($field['bundles'] as $entity_type => $bundles) {
883
        foreach ($bundles as $bundle_name) {
884
          $instances[] = field_info_instance($entity_type, $field['field_name'], $bundle_name);
885
        }
886
      }
887
    }
888
    else {
889
      $instances = array($instance);
890
    }
891

    
892
    foreach ($instances as $instance) {
893
      if (!empty($instance['widget']['settings']['available_countries'])) {
894
        $countries = array_intersect_key($countries, $instance['widget']['settings']['available_countries']);
895
        break;
896
      }
897
    }
898
  }
899

    
900
  return $countries;
901
}