Projet

Général

Profil

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

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

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
    $group = $implementations['addressfield'];
46
    unset($implementations['addressfield']);
47
    $implementations['addressfield'] = $group;
48
  }
49
}
50

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

    
60
/**
61
 * Get the list of format plugins.
62
 */
63
function addressfield_format_plugins() {
64
  ctools_include('plugins');
65
  $plugins = ctools_get_plugins('addressfield', 'format');
66
  uasort($plugins, 'ctools_plugin_sort');
67

    
68
  return $plugins;
69
}
70

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

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

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

    
143
  ctools_include('plugins');
144
  $format = array();
145
  $format['#handlers'] = $handlers;
146
  foreach ($format['#handlers'] as $handler) {
147
    if ($callback = ctools_plugin_load_function('addressfield', 'format', $handler, 'format callback')) {
148
      $callback($format, $address, $context);
149
    }
150
  }
151

    
152
  // Store the address in the format, for processing.
153
  $format['#address'] = $address;
154

    
155
  // Post-process the format stub, depending on the rendering mode.
156
  if ($context['mode'] == 'form') {
157
    $format['#addressfield'] = TRUE;
158
    $format['#process'][] = 'addressfield_process_format_form';
159
    $format['#required'] = FALSE;
160
  }
161
  elseif ($context['mode'] == 'render') {
162
    $format['#pre_render'][] = 'addressfield_render_address';
163
  }
164

    
165
  return $format;
166
}
167

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

    
178
  _addressfield_process_format_form($format, $format['#address'], $format['#required']);
179
  return $format;
180
}
181

    
182
function _addressfield_process_format_form(&$format, $address, $required) {
183
  foreach (element_children($format) as $key) {
184
    $child = &$format[$key];
185

    
186
    // Automatically convert any element in the format array to an appropriate
187
    // form element that matches one of the address component names.
188
    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'))) {
189
      // Set the form element type for the address component to whatever the
190
      // address format specified in its #widget_type property.
191
      if (isset($child['#widget_type'])) {
192
        $child['#type'] = $child['#widget_type'];
193
      }
194
      else {
195
        // If the element didn't specify a #widget_type and has options, turn it
196
        // into a select list and unset its #size value, which is typically used
197
        // to provide the width of a textfield.
198
        if (isset($child['#options'])) {
199
          $child['#type'] = 'select';
200
          unset($child['#size']);
201
        }
202
        else {
203
          // Otherwise go ahead and make it a textfield.
204
          $child['#type'] = 'textfield';
205
        }
206
      }
207

    
208
      if (!$required) {
209
        unset($child['#required']);
210
      }
211

    
212
      if (isset($address[$key])) {
213
        $child['#default_value'] = $address[$key];
214
      }
215
    }
216

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

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

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

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

    
247
      // If the element instructs us to render the option value instead of the
248
      // raw address element value and its #options array has a matching key,
249
      // swap it out for the option value now.
250
      $child['#children'] = '';
251

    
252
      if (!empty($child['#render_option_value']) && 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

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

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

    
273
    // Recurse through the child.
274
    _addressfield_render_address($child, $address);
275
  }
276
}
277

    
278
/**
279
 * @} End of "ingroup addressfield_format"
280
 */
281

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

    
292
/**
293
 * Render a container for a set of address fields.
294
 */
295
function theme_addressfield_container($variables) {
296
  $element = $variables['element'];
297

    
298
  $element['#children'] = trim($element['#children']);
299
  if (strlen($element['#children']) > 0) {
300
    $output = '<' . $element['#tag'] . drupal_attributes($element['#attributes']) . '>';
301
    $output .= $element['#children'];
302
    $output .= '</' . $element['#tag'] . ">";
303
    return $output;
304
  }
305
  else {
306
    return '';
307
  }
308
}
309

    
310
/**
311
 * Implementation of hook_element_info().
312
 */
313
function addressfield_element_info() {
314
  $types['addressfield_container'] = array(
315
    '#theme_wrappers' => array('addressfield_container'),
316
    '#process' => array('addressfield_widget_process'),
317
    '#attributes' => array(),
318
    '#tag' => 'div',
319
  );
320
  return $types;
321
}
322

    
323
/**
324
 * Form API process function: set the #parents of the children of this element so they appear at the same level as the parent.
325
 */
326
function addressfield_widget_process($element) {
327
  foreach (element_children($element) as $key) {
328
    $element[$key]['#parents'] = $element['#parents'];
329
    $element[$key]['#parents'][count($element[$key]['#parents']) - 1] = $key;
330
  }
331

    
332
  return $element;
333
}
334

    
335
/**
336
 * Implements hook_field_info()
337
 */
338
function addressfield_field_info() {
339
  $fields = array();
340

    
341
  $fields['addressfield'] = array(
342
    'label' => t('Postal address'),
343
    'description' => t('A field type used for storing postal addresses according the xNAL standard.'),
344
    'settings' => array(),
345
    'instance_settings' => array(),
346
    'default_widget' => 'addressfield_standard',
347
    'default_formatter' => 'addressfield_default',
348
    'property_type' => 'addressfield',
349
    'property_callbacks' => array('addressfield_property_info_callback'),
350
  );
351

    
352
  return $fields;
353
}
354

    
355
/**
356
 * Returns an array of default values for the addressfield form elements.
357
 */
358
function addressfield_default_values($available_countries = NULL) {
359
  if (!isset($available_countries)) {
360
    $available_countries = _addressfield_country_options_list();
361
  }
362

    
363
  // Use the default country of the site if possible.
364
  $default_country = variable_get('site_default_country', NULL);
365

    
366
  // If the default country is undefined or not in the list of available countries,
367
  // just fallback to the first country in the list.
368
  if (!$default_country || !isset($available_countries[$default_country])) {
369
    $default_country = key($available_countries);
370
  }
371

    
372
  return array(
373
    'country' => $default_country,
374
    'name_line' => '',
375
    'first_name' => '',
376
    'last_name' => '',
377
    'organisation_name' => '',
378
    'administrative_area' => '',
379
    'sub_administrative_area' => '',
380
    'locality' => '',
381
    'dependent_locality' => '',
382
    'postal_code' => '',
383
    'thoroughfare' => '',
384
    'premise' => '',
385
    'sub_premise' => '',
386
    'data' => '',
387
  );
388
}
389

    
390
/**
391
 * Implements hook_field_is_empty().
392
 */
393
function addressfield_field_is_empty($item, $field) {
394
  // Every address field must have at least a country value or it is considered
395
  // empty, even if it has name information.
396
  return empty($item['country']);
397
}
398

    
399
/**
400
 * Implements hook_field_presave().
401
 */
402
function addressfield_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) {
403
  foreach ($items as $delta => &$item) {
404
    // If the first name and last name are set but the name line isn't...
405
    if (isset($item['first_name']) && isset($item['last_name']) && !isset($item['name_line'])) {
406
      // Combine the first and last name to be the name line.
407
      $items[$delta]['name_line'] = $items[$delta]['first_name'] . ' ' . $items[$delta]['last_name'];
408
    }
409
    elseif (isset($item['name_line'])) {
410
      // Otherwise if the name line is set, separate it out into a best guess at
411
      // the first and last name.
412
      $names = explode(' ', $item['name_line']);
413

    
414
      $item['first_name'] = array_shift($names);
415
      $item['last_name'] = implode(' ', $names);
416
    }
417

    
418
    // Trim whitespace from all of the address components and convert any double
419
    // spaces to single spaces.
420
    foreach ($item as $key => &$value) {
421
      if (!in_array($key, array('element_key', 'data')) && is_string($value)) {
422
        $value = trim(str_replace('  ', ' ', $value));
423
      }
424
    }
425
  }
426
}
427

    
428
/**
429
 * Implements hook_field_widget_info()
430
 */
431
function addressfield_field_widget_info() {
432
  $widgets = array();
433

    
434
  $widgets['addressfield_standard'] = array(
435
    'label' => t('Dynamic address form'),
436
    'field types' => array('addressfield'),
437
    'settings' => array(
438
      'available_countries' => array(),
439
      'format_handlers' => array('address'),
440
    ),
441
  );
442

    
443
  return $widgets;
444
}
445

    
446
/**
447
 * Implements hook_field_widget_settings_form()
448
 */
449
function addressfield_field_widget_settings_form($field, $instance) {
450
  $widget = $instance['widget'];
451
  $defaults = field_info_widget_settings($widget['type']);
452
  $settings = array_merge($defaults, $widget['settings']);
453
  $form = array();
454

    
455
  if ($widget['type'] == 'addressfield_standard') {
456
    $form['available_countries'] = array(
457
      '#type' => 'select',
458
      '#multiple' => TRUE,
459
      '#title' => t('Available countries'),
460
      '#description' => t('If no countries are selected, all countries will be available.'),
461
      '#options' => _addressfield_country_options_list(),
462
      '#default_value' => $settings['available_countries'],
463
    );
464

    
465
    $form['format_handlers'] = array(
466
      '#type' => 'checkboxes',
467
      '#title' => t('Format handlers'),
468
      '#options' => addressfield_format_plugins_options(),
469
      '#default_value' => $settings['format_handlers'],
470
    );
471
  }
472

    
473
  return $form;
474
}
475

    
476
/**
477
 * Implements hook_field_widget_form()
478
 */
479
function addressfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
480
  $settings = $instance['widget']['settings'];
481

    
482
  // Generate a specific key used to identify this element to restore a default
483
  // value upon AJAX submission regardless of where this element is in the
484
  // $form array.
485
  $element_key = implode('|', array($element['#entity_type'], $element['#bundle'], $element['#field_name'], $element['#language'], $element['#delta']));
486

    
487
  // Store the key in the element array as a value so it can be easily retrieved
488
  // in context in the $form_state['values'] array in the element validator.
489
  $element['element_key'] = array(
490
    '#type' => 'value',
491
    '#value' => $element_key,
492
  );
493

    
494
  // Get the default address used to build the widget form elements, looking
495
  // first in the form state, then in the stored value for the field, and then
496
  // in the default values of the instance.
497
  $address = array();
498

    
499
  if (!empty($form_state['addressfield'][$element_key])) {
500
    // Use the value from the form_state if available.
501
    $address = $form_state['addressfield'][$element_key];
502
  }
503
  elseif (!empty($items[$delta]['country'])) {
504
    // Else use the saved value for the field.
505
    $address = $items[$delta];
506
  }
507
  else {
508
    // Otherwise use the instance default.
509
    $address = (array) $instance['default_value'][0];
510
  }
511

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

    
519
  // Merge in default values to provide a value for every expected array key.
520
  $address += addressfield_default_values($countries);
521

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

    
531
    // Generate the address form.
532
    $context = array(
533
      'mode' => 'form',
534
      'field' => $field,
535
      'instance' => $instance,
536
      'langcode' => $langcode,
537
      'delta' => $delta,
538
    );
539
    $element += addressfield_generate($address, $settings['format_handlers'], $context);
540

    
541
    // Mark the form element as required if necessary.
542
    $element['#required'] = $delta == 0 && $instance['required'];
543
  }
544

    
545
  return $element;
546
}
547

    
548
/**
549
 * Element validate callback: rebuilds the form on country change and stores the
550
 * current address value in the $form_state for retrieval on rebuild.
551
 */
552
function addressfield_standard_country_validate($element, &$form_state) {
553
  // If the country was changed, rebuild the form.
554
  if ($element['#default_value'] != $element['#value']) {
555
    $form_state['rebuild'] = TRUE;
556
  }
557

    
558
  $parents = $element['#parents'];
559
  array_pop($parents);
560

    
561
  // Search through the form values to find the current address.
562
  $address = drupal_array_get_nested_value($form_state['values'], $parents);
563

    
564
  // Store the present address values in the form state for retrieval by the
565
  // widget form regardless of where the widget sits in the $form array.
566
  $form_state['addressfield'][$address['element_key']] = array_diff_key($address, array('element_key' => ''));
567
}
568

    
569
/**
570
 * Ajax callback in response to a change of country in an address field.
571
 *
572
 * The only thing we have to do is to find the proper element to render.
573
 */
574
function addressfield_standard_widget_refresh($form, $form_state) {
575
  // The target element is one element below the triggering country selector.
576
  $array_parents = $form_state['triggering_element']['#array_parents'];
577
  array_pop($array_parents);
578

    
579
  // Iterate over the form parents to find the element.
580
  $element = $form;
581
  foreach ($array_parents as $name) {
582
    $element = &$element[$name];
583
    if (!empty($element['#addressfield'])) {
584
      break;
585
    }
586
  }
587

    
588
  // Return the address block, but remove the '_weight' element inserted
589
  // by the field API.
590
  unset($element['_weight']);
591

    
592
  // Replace the address field widget with the updated widget and focus on the
593
  // new country select list.
594
  $commands[] = ajax_command_replace(NULL, render($element));
595
  $commands[] = ajax_command_invoke('#' . $element['country']['#id'], 'focus');
596

    
597
  // Allow other modules to add arbitrary AJAX commands on the refresh.
598
  drupal_alter('addressfield_standard_widget_refresh', $commands, $form, $form_state);
599

    
600
  return array('#type' => 'ajax', '#commands' => $commands);
601
}
602

    
603
/**
604
 * Implements hook_field_formatter_info().
605
 */
606
function addressfield_field_formatter_info() {
607
  return array(
608
    'addressfield_default' => array(
609
      'label' => t('Default'),
610
      'field types' => array('addressfield'),
611
      'settings' => array(
612
        'use_widget_handlers' => 1,
613
        'format_handlers' => array('address'),
614
      ),
615
    ),
616
  );
617
}
618

    
619
/**
620
 * Implements hook_field_formatter_settings_form().
621
 */
622
function addressfield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
623
  $display = $instance['display'][$view_mode];
624
  $settings = $display['settings'];
625

    
626
  $element['use_widget_handlers'] = array(
627
    '#type' => 'checkbox',
628
    '#title' => t('Use the same configuration as the widget.'),
629
    '#default_value' => !empty($settings['use_widget_handlers']),
630
  );
631

    
632
  $element['format_handlers'] = array(
633
    '#type' => 'checkboxes',
634
    '#title' => t('Format handlers'),
635
    '#options' => addressfield_format_plugins_options(),
636
    '#default_value' => $settings['format_handlers'],
637
    '#process' => array('form_process_checkboxes', '_addressfield_field_formatter_settings_form_process_add_state'),
638
    '#element_validate' => array('_addressfield_field_formatter_settings_form_validate')
639
  );
640

    
641
  return $element;
642
}
643

    
644
/**
645
 * Helper function: set the proper #states to the use widget handlers checkbox.
646
 */
647
function _addressfield_field_formatter_settings_form_process_add_state($element, $form_state) {
648
  // Build a #parents based on the current checkbox.
649
  $target_parents = array_slice($element['#parents'], 0, -1);
650
  $target_parents[] = 'use_widget_handlers';
651
  $target_parents = array_shift($target_parents) . ($target_parents ? '[' . implode('][', $target_parents) . ']' : '');
652

    
653
  $element['#states']['visible'] = array(
654
    ':input[name="' . $target_parents . '"]' => array('checked' => FALSE),
655
  );
656

    
657
  return $element;
658
}
659

    
660
/**
661
 * Helper function: filter the results of the checkboxes form element.
662
 */
663
function _addressfield_field_formatter_settings_form_validate($element, &$element_state) {
664
  form_set_value($element, array_filter($element['#value']), $element_state);
665
}
666

    
667
/**
668
 * Implements hook_field_formatter_settings_summary().
669
 */
670
function addressfield_field_formatter_settings_summary($field, $instance, $view_mode) {
671
  $display = $instance['display'][$view_mode];
672
  $settings = $display['settings'];
673

    
674
  $summary = '';
675

    
676
  if ($settings['use_widget_handlers']) {
677
    return t('Use widget configuration');
678
  }
679
  else {
680
    $summary = array();
681
    $plugins = addressfield_format_plugins();
682
    foreach ($settings['format_handlers'] as $handler) {
683
      $summary[] = $plugins[$handler]['title'];
684
    }
685
    return $summary ? implode(', ', $summary) : t('No handler');
686
  }
687
}
688

    
689
/**
690
 * Implements hook_field_formatter_view().
691
 */
692
function addressfield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
693
  $settings = $display['settings'];
694
  $element = array();
695

    
696
  switch ($display['type']) {
697
    case 'addressfield_default':
698
      if (!empty($settings['use_widget_handlers'])) {
699
        $handlers = $instance['widget']['settings']['format_handlers'];
700
      }
701
      else {
702
        $handlers = $settings['format_handlers'];
703
      }
704
      foreach ($items as $delta => $address) {
705
        // Generate the address format.
706
        $context = array(
707
          'mode' => 'render',
708
          'field' => $field,
709
          'instance' => $instance,
710
          'langcode' => $langcode,
711
          'delta' => $delta,
712
        );
713
        $element[$delta] = addressfield_generate($address, $handlers, $context);
714
      }
715
      break;
716
  }
717

    
718
  return $element;
719
}
720

    
721
/**
722
 * Callback to alter the property info of address fields.
723
 *
724
 * @see addressfield_field_info().
725
 */
726
function addressfield_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
727
  $name = $field['field_name'];
728
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
729

    
730
  $property['type'] = ($field['cardinality'] != 1) ? 'list<addressfield>' : 'addressfield';
731
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
732
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
733
  $property['auto creation'] = 'addressfield_auto_creation';
734
  $property['property info'] = addressfield_data_property_info();
735

    
736
  unset($property['query callback']);
737
}
738

    
739
/**
740
 * Auto creation callback for an addressfield value array.
741
 *
742
 * @see addressfield_property_info_callback()
743
 */
744
function addressfield_auto_creation() {
745
  // We can't call addressfield_default_values() directly, because it has an
746
  // optional array argument that will receive an invalid value when Entity API
747
  // tries to call it directly with its usual $property_name and $context
748
  // arguments.
749
  return addressfield_default_values();
750
}
751

    
752
/**
753
 * Defines info for the properties of the address field data structure.
754
 */
755
function addressfield_data_property_info($name = NULL) {
756
  // Build an array of basic property information for the address field.
757
  $properties = array(
758
    'country' => array(
759
      'label' => t('Country'),
760
      'options list' => '_addressfield_country_options_list',
761
    ),
762
    'name_line' => array(
763
      'label' => t('Full name'),
764
    ),
765
    'first_name' => array(
766
      'label' => t('First name'),
767
    ),
768
    'last_name' => array(
769
      'label' => t('Last name'),
770
    ),
771
    'organisation_name' => array(
772
      'label' => t('Company'),
773
    ),
774
    'administrative_area' => array(
775
      'label' => t('Administrative area (i.e. State / Province)'),
776
    ),
777
    'sub_administrative_area' => array(
778
      'label' => t('Sub administrative area'),
779
    ),
780
    'locality' => array(
781
      'label' => t('Locality (i.e. City)'),
782
    ),
783
    'dependent_locality' => array(
784
      'label' => t('Dependent locality'),
785
    ),
786
    'postal_code' => array(
787
      'label' => t('Postal code'),
788
    ),
789
    'thoroughfare' => array(
790
      'label' => t('Thoroughfare (i.e. Street address)'),
791
    ),
792
    'premise' => array(
793
      'label' => t('Premise (i.e. Apartment / Suite number)'),
794
    ),
795
  );
796

    
797
  // Add the default values for each of the address field properties.
798
  foreach ($properties as $key => &$value) {
799
    $value += array(
800
      'description' => !empty($name) ? t('!label of field %name', array('!label' => $value['label'], '%name' => $name)) : '',
801
      'type' => 'text',
802
      'getter callback' => 'entity_property_verbatim_get',
803
      'setter callback' => 'entity_property_verbatim_set',
804
    );
805
  }
806

    
807
  return $properties;
808
}
809

    
810
/**
811
 * Wraps country_get_list() for use as an Entity API options list.
812
 */
813
function _addressfield_country_options_list($field = NULL, $instance = NULL) {
814
  // Necessary for country_get_list().
815
  require_once DRUPAL_ROOT . '/includes/locale.inc';
816

    
817
  $countries = country_get_list();
818

    
819
  if (isset($field)) {
820
    // If the instance is not specified, loop against all the instances of the field.
821
    if (!isset($instance)) {
822
      $instances = array();
823
      foreach ($field['bundles'] as $entity_type => $bundles) {
824
        foreach ($bundles as $bundle_name) {
825
          $instances[] = field_info_instance($entity_type, $field['field_name'], $bundle_name);
826
        }
827
      }
828
    }
829
    else {
830
      $instances = array($instance);
831
    }
832

    
833
    foreach ($instances as $instance) {
834
      if (!empty($instance['widget']['settings']['available_countries'])) {
835
        $countries = array_intersect_key($countries, $instance['widget']['settings']['available_countries']);
836
      }
837
      else {
838
        // This instance allow all the countries.
839
        $countries = country_get_list();
840
        break;
841
      }
842
    }
843
  }
844

    
845
  return $countries;
846
}