Projet

Général

Profil

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

root / drupal7 / sites / all / modules / addressfield / addressfield.module @ 9d13637e

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 attributes because the W3C validator complains.
302
  // They are only used on forms anyway.
303
  unset($element['#attributes']['autocomplete']);
304
  unset($element['#attributes']['x-autocompletetype']);
305

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

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

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

    
339
  return $element;
340
}
341

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

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

    
359
  return $fields;
360
}
361

    
362
/**
363
 * Returns an array of default values for the addressfield form elements.
364
 *
365
 * @param $field
366
 *   The field array.
367
 * @param $instance
368
 *   The instance array.
369
 * @param $address
370
 *   The current address values, if known. Allows for per-country defaults.
371
 *
372
 * @return
373
 *   An array of default values.
374
 */
375
function addressfield_default_values($field, $instance, array $address = array()) {
376
  $available_countries = _addressfield_country_options_list($field, $instance);
377
  $default_country = $instance['widget']['settings']['default_country'];
378
  // If the default country is not in the list of available countries,
379
  // fallback to the first country in the list.
380
  if ($default_country && !isset($available_countries[$default_country])) {
381
    $default_country = key($available_countries);
382
  }
383

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

    
401
  // Allow other modules to alter the default values.
402
  $context = array(
403
    'field' => $field,
404
    'instance' => $instance,
405
    'address' => $address,
406
  );
407
  drupal_alter('addressfield_default_values', $default_values, $context);
408

    
409
  return $default_values;
410
}
411

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

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

    
436
      $item['first_name'] = array_shift($names);
437
      $item['last_name'] = implode(' ', $names);
438
    }
439

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

    
450
/**
451
 * Implements hook_field_widget_info()
452
 */
453
function addressfield_field_widget_info() {
454
  $widgets = array();
455

    
456
  $widgets['addressfield_standard'] = array(
457
    'label' => t('Dynamic address form'),
458
    'field types' => array('addressfield'),
459
    'settings' => array(
460
      'available_countries' => array(),
461
      'default_country' => '',
462
      'format_handlers' => array('address'),
463
    ),
464
  );
465

    
466
  return $widgets;
467
}
468

    
469
/**
470
 * Implements hook_field_widget_settings_form()
471
 */
472
function addressfield_field_widget_settings_form($field, $instance) {
473
  $widget = $instance['widget'];
474
  $defaults = field_info_widget_settings($widget['type']);
475
  $settings = array_merge($defaults, $widget['settings']);
476
  $form = array();
477

    
478
  if ($widget['type'] == 'addressfield_standard') {
479
    $form['available_countries'] = array(
480
      '#type' => 'select',
481
      '#multiple' => TRUE,
482
      '#title' => t('Available countries'),
483
      '#description' => t('If no countries are selected, all countries will be available.'),
484
      '#options' => _addressfield_country_options_list(),
485
      '#default_value' => $settings['available_countries'],
486
    );
487
    $form['default_country'] = array(
488
      '#type' => 'select',
489
      '#title' => t('Default country'),
490
      '#options' => _addressfield_country_options_list(),
491
      '#default_value' => $settings['default_country'],
492
      '#empty_value' => '',
493
    );
494
    $form['format_handlers'] = array(
495
      '#type' => 'checkboxes',
496
      '#title' => t('Format handlers'),
497
      '#options' => addressfield_format_plugins_options(),
498
      '#default_value' => $settings['format_handlers'],
499
    );
500
  }
501

    
502
  return $form;
503
}
504

    
505
/**
506
 * Implements hook_form_BASE_FORM_ID_alter().
507
 *
508
 * Removes the default values form from the field settings page.
509
 * Allows the module to implement its own, more predictable default value
510
 * handling, getting around #1253820 and other bugs.
511
 */
512
function addressfield_form_field_ui_field_edit_form_alter(&$form, $form_state) {
513
  if ($form['#field']['type'] == 'addressfield') {
514
    $form['instance']['default_value_widget']['#access'] = FALSE;
515
  }
516
}
517

    
518
/**
519
 * Implements hook_field_widget_form()
520
 */
521
function addressfield_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
522
  $settings = $instance['widget']['settings'];
523

    
524
  $address = array();
525
  // If the form has been rebuilt via AJAX, use the form state values.
526
  // $form_state['values'] is empty because of #limit_validation_errors, so
527
  // $form_state['input'] needs to be used instead.
528
  $parents = array_merge($element['#field_parents'], array($element['#field_name'], $langcode, $delta));
529
  $input_address = drupal_array_get_nested_value($form_state['input'], $parents);
530
  if (!empty($input_address)) {
531
    $address = $input_address;
532
  }
533
  elseif (!empty($items[$delta]['country'])) {
534
    // Else use the saved value for the field.
535
    $address = $items[$delta];
536
  }
537

    
538
  // Determine the list of available countries, and if the currently selected
539
  // country is not in it, unset it so it can be reset to the default country.
540
  $countries = _addressfield_country_options_list($field, $instance);
541
  if (!empty($address['country']) && !isset($countries[$address['country']])) {
542
    unset($address['country']);
543
  }
544

    
545
  // Merge in default values.
546
  $address += addressfield_default_values($field, $instance, $address);
547

    
548
  // Add the form elements for the standard widget, which includes a country
549
  // select list at the top that reloads the available address elements when the
550
  // country is changed.
551
  if ($instance['widget']['type'] == 'addressfield_standard') {
552
    // Wrap everything in a fieldset. This is not the best looking element,
553
    // but it's the only wrapper available in Drupal we can properly use
554
    // in that context, and it is overridable if necessary.
555
    $element['#type'] = 'fieldset';
556

    
557
    if (!empty($instance['description'])) {
558
      // Checkout panes convert the fieldset into a container, causing
559
      // #description to not be rendered. Hence, a real element is added and
560
      // the old #description is removed.
561
      $element['#description'] = '';
562
      $element['element_description'] = array(
563
        '#markup' =>  $instance['description'],
564
        '#prefix' => '<div class="fieldset-description">',
565
        '#suffix' => '</div>',
566
        '#weight' => -999,
567
      );
568
    }
569

    
570
    // Generate the address form.
571
    $context = array(
572
      'mode' => 'form',
573
      'field' => $field,
574
      'instance' => $instance,
575
      'langcode' => $langcode,
576
      'delta' => $delta,
577
    );
578
    $element += addressfield_generate($address, $settings['format_handlers'], $context);
579

    
580
    // Remove any already saved default value.
581
    // See addressfield_form_field_ui_field_edit_form_alter() for the reasoning.
582
    if ($form_state['build_info']['form_id'] == 'field_ui_field_edit_form') {
583
      $element['#address'] = array('country' => '');
584
    }
585
  }
586

    
587
  return $element;
588
}
589

    
590
/**
591
 * Element validate callback: rebuilds the form on country change.
592
 */
593
function addressfield_standard_country_validate($element, &$form_state) {
594
  if ($element['#default_value'] != $element['#value']) {
595
    $parents = $element['#parents'];
596
    array_pop($parents);
597
    $address = drupal_array_get_nested_value($form_state['values'], $parents);
598

    
599
    // Clear the country-specific field values.
600
    $country_specific_data = array(
601
      'dependent_locality' => '',
602
      'locality' => '',
603
      'administrative_area' => '',
604
      'postal_code' => '',
605
    );
606
    $address = array_diff_key($address, $country_specific_data);
607

    
608
    drupal_array_set_nested_value($form_state['values'], $parents, $address);
609
    drupal_array_set_nested_value($form_state['input'], $parents, $address);
610

    
611
    $form_state['rebuild'] = TRUE;
612
  }
613
}
614

    
615
/**
616
 * Ajax callback in response to a change of country in an address field.
617
 *
618
 * The only thing we have to do is to find the proper element to render.
619
 */
620
function addressfield_standard_widget_refresh($form, $form_state) {
621
  // The target element is one element below the triggering country selector.
622
  $array_parents = $form_state['triggering_element']['#array_parents'];
623
  array_pop($array_parents);
624

    
625
  // Iterate over the form parents to find the element.
626
  $element = $form;
627
  foreach ($array_parents as $name) {
628
    $element = &$element[$name];
629
    if (!empty($element['#addressfield'])) {
630
      break;
631
    }
632
  }
633

    
634
  // Return the address block, but remove the '_weight' element inserted
635
  // by the field API.
636
  unset($element['_weight']);
637

    
638
  // Replace the address field widget with the updated widget and focus on the
639
  // new country select list.
640
  $commands[] = ajax_command_replace(NULL, render($element));
641
  $commands[] = ajax_command_invoke('#' . $element['country']['#id'], 'focus');
642
  // Add the status messages inside the new addressfield's wrapper element,
643
  // just like core does.
644
  $commands[] = ajax_command_prepend(NULL, theme('status_messages'));
645

    
646
  // Allow other modules to add arbitrary AJAX commands on the refresh.
647
  drupal_alter('addressfield_standard_widget_refresh', $commands, $form, $form_state);
648

    
649
  return array('#type' => 'ajax', '#commands' => $commands);
650
}
651

    
652
/**
653
 * Implements hook_field_formatter_info().
654
 */
655
function addressfield_field_formatter_info() {
656
  return array(
657
    'addressfield_default' => array(
658
      'label' => t('Default'),
659
      'field types' => array('addressfield'),
660
      'settings' => array(
661
        'use_widget_handlers' => 1,
662
        'format_handlers' => array('address'),
663
      ),
664
    ),
665
  );
666
}
667

    
668
/**
669
 * Implements hook_field_formatter_settings_form().
670
 */
671
function addressfield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
672
  $display = $instance['display'][$view_mode];
673
  $settings = $display['settings'];
674

    
675
  $element['use_widget_handlers'] = array(
676
    '#type' => 'checkbox',
677
    '#title' => t('Use the same configuration as the widget.'),
678
    '#default_value' => !empty($settings['use_widget_handlers']),
679
  );
680

    
681
  $element['format_handlers'] = array(
682
    '#type' => 'checkboxes',
683
    '#title' => t('Format handlers'),
684
    '#options' => addressfield_format_plugins_options(),
685
    '#default_value' => $settings['format_handlers'],
686
    '#process' => array('form_process_checkboxes', '_addressfield_field_formatter_settings_form_process_add_state'),
687
    '#element_validate' => array('_addressfield_field_formatter_settings_form_validate')
688
  );
689

    
690
  return $element;
691
}
692

    
693
/**
694
 * Helper function: set the proper #states to the use widget handlers checkbox.
695
 */
696
function _addressfield_field_formatter_settings_form_process_add_state($element, $form_state) {
697
  // Build a #parents based on the current checkbox.
698
  $target_parents = array_slice($element['#parents'], 0, -1);
699
  $target_parents[] = 'use_widget_handlers';
700
  $target_parents = array_shift($target_parents) . ($target_parents ? '[' . implode('][', $target_parents) . ']' : '');
701

    
702
  $element['#states']['visible'] = array(
703
    ':input[name="' . $target_parents . '"]' => array('checked' => FALSE),
704
  );
705

    
706
  return $element;
707
}
708

    
709
/**
710
 * Helper function: filter the results of the checkboxes form element.
711
 */
712
function _addressfield_field_formatter_settings_form_validate($element, &$element_state) {
713
  form_set_value($element, array_filter($element['#value']), $element_state);
714
}
715

    
716
/**
717
 * Implements hook_field_formatter_settings_summary().
718
 */
719
function addressfield_field_formatter_settings_summary($field, $instance, $view_mode) {
720
  $display = $instance['display'][$view_mode];
721
  $settings = $display['settings'];
722

    
723
  $summary = '';
724

    
725
  if ($settings['use_widget_handlers']) {
726
    return t('Use widget configuration');
727
  }
728
  else {
729
    $summary = array();
730
    $plugins = addressfield_format_plugins();
731
    foreach ($settings['format_handlers'] as $handler) {
732
      $summary[] = $plugins[$handler]['title'];
733
    }
734
    return $summary ? implode(', ', $summary) : t('No handler');
735
  }
736
}
737

    
738
/**
739
 * Implements hook_field_formatter_view().
740
 */
741
function addressfield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
742
  $settings = $display['settings'];
743
  $element = array();
744

    
745
  switch ($display['type']) {
746
    case 'addressfield_default':
747
      if (!empty($settings['use_widget_handlers'])) {
748
        $handlers = $instance['widget']['settings']['format_handlers'];
749
      }
750
      else {
751
        $handlers = $settings['format_handlers'];
752
      }
753
      foreach ($items as $delta => $address) {
754
        // Generate the address format.
755
        $context = array(
756
          'mode' => 'render',
757
          'field' => $field,
758
          'instance' => $instance,
759
          'langcode' => $langcode,
760
          'delta' => $delta,
761
        );
762
        $element[$delta] = addressfield_generate($address, $handlers, $context);
763
      }
764
      break;
765
  }
766

    
767
  return $element;
768
}
769

    
770
/**
771
 * Callback to alter the property info of address fields.
772
 *
773
 * @see addressfield_field_info().
774
 */
775
function addressfield_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) {
776
  $name = $field['field_name'];
777
  $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name];
778

    
779
  $property['type'] = ($field['cardinality'] != 1) ? 'list<addressfield>' : 'addressfield';
780
  $property['getter callback'] = 'entity_metadata_field_verbatim_get';
781
  $property['setter callback'] = 'entity_metadata_field_verbatim_set';
782
  $property['auto creation'] = 'addressfield_auto_creation';
783
  $property['property info'] = addressfield_data_property_info();
784

    
785
  unset($property['query callback']);
786
}
787

    
788
/**
789
 * Auto creation callback for an addressfield value array.
790
 *
791
 * @see addressfield_property_info_callback()
792
 */
793
function addressfield_auto_creation($property_name, $context) {
794
  return addressfield_default_values($context['field'], $context['instance']);
795
}
796

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

    
845
  // Add the default values for each of the address field properties.
846
  foreach ($properties as $key => &$value) {
847
    $value += array(
848
      'description' => !empty($name) ? t('!label of field %name', array('!label' => $value['label'], '%name' => $name)) : '',
849
      'type' => 'text',
850
      'getter callback' => 'entity_property_verbatim_get',
851
      'setter callback' => 'entity_property_verbatim_set',
852
    );
853
  }
854

    
855
  return $properties;
856
}
857

    
858
/**
859
 * Returns the country list in a format suitable for use as an options list.
860
 */
861
function _addressfield_country_options_list($field = NULL, $instance = NULL) {
862
  if (module_exists('countries')) {
863
    $countries = countries_get_countries('name', array('enabled' => COUNTRIES_ENABLED));
864
  }
865
  else {
866
    require_once DRUPAL_ROOT . '/includes/locale.inc';
867
    $countries = country_get_list();
868
  }
869

    
870
  if (isset($field)) {
871
    // If the instance is not specified, loop against all the instances of the field.
872
    if (!isset($instance)) {
873
      $instances = array();
874
      foreach ($field['bundles'] as $entity_type => $bundles) {
875
        foreach ($bundles as $bundle_name) {
876
          $instances[] = field_info_instance($entity_type, $field['field_name'], $bundle_name);
877
        }
878
      }
879
    }
880
    else {
881
      $instances = array($instance);
882
    }
883

    
884
    foreach ($instances as $instance) {
885
      if (!empty($instance['widget']['settings']['available_countries'])) {
886
        $countries = array_intersect_key($countries, $instance['widget']['settings']['available_countries']);
887
        break;
888
      }
889
    }
890
  }
891

    
892
  return $countries;
893
}