diff --git a/addressfield.module b/addressfield.module index 68b45d9..acc89fd 100644 --- a/addressfield.module +++ b/addressfield.module @@ -121,9 +121,9 @@ function addressfield_format_plugins_options() { * * @param $address * The address format being generated. - * @param $handlers + * @param array $handlers * The format handlers to use to generate the format. - * @param $context + * @param array $context * An associative array of context information pertaining to how the address * format should be generated. If no mode is given, it will initialize to the * default value. The remaining context keys should only be present when the @@ -133,12 +133,14 @@ function addressfield_format_plugins_options() { * - instance: the field instance array. * - langcode: the langcode of the language the field is being rendered in. * - delta: the delta value of the given address. + * @param array $available_countries + * A list of countries to restrict the format to. * - * @return - * A renderable array suitable for use as part of a form (if 'mode' is 'form') - * or for formatted address output when passed to drupal_render(). + * @return array A renderable array suitable for use as part of a form (if 'mode' is 'form') + * A renderable array suitable for use as part of a form (if 'mode' is 'form') + * or for formatted address output when passed to drupal_render(). */ -function addressfield_generate($address, array $handlers, array $context = array()) { +function addressfield_generate($address, array $handlers, array $context = array(), array $available_countries = array()) { // If no mode is given in the context array, default it to 'render'. if (empty($context['mode'])) { $context['mode'] = 'render'; @@ -149,6 +151,7 @@ function addressfield_generate($address, array $handlers, array $context = array // Add the handlers, ordered by weight. $plugins = addressfield_format_plugins(); $format['#handlers'] = array_intersect(array_keys($plugins), $handlers); + $format['#available_countries'] = $available_countries; foreach ($format['#handlers'] as $handler) { if ($callback = ctools_plugin_load_function('addressfield', 'format', $handler, 'format callback')) { @@ -162,7 +165,6 @@ function addressfield_generate($address, array $handlers, array $context = array // Post-process the format stub, depending on the rendering mode. if ($context['mode'] == 'form') { $format['#addressfield'] = TRUE; - $format['#process'][] = 'addressfield_process_format_form'; } elseif ($context['mode'] == 'render') { $format['#pre_render'][] = 'addressfield_render_address'; @@ -330,10 +332,86 @@ function addressfield_element_info() { '#attributes' => array(), '#tag' => 'div', ); + + $types['addressfield'] = array( + '#process' => array('addressfield_element_process'), + '#context' => array('mode' => 'form'), + '#handlers' => array('address' => 'address'), + '#available_countries' => array_keys(_addressfield_country_options_list()), + '#inline' => FALSE, + ); + return $types; } /** + * Addressfield process function. + */ +function addressfield_element_process($element, &$form_state, $form) { + $address = array(); + // If the form has been rebuilt via AJAX, use the form state values. + // $form_state['values'] is empty because of #limit_validation_errors, so + // $form_state['input'] needs to be used instead. + $parents = $element['#parents']; + $input_address = drupal_array_get_nested_value($form_state['input'], $parents); + if (!empty($input_address)) { + $address = $input_address; + } + elseif (!empty($element['#default_value'])) { + // Else use the saved value for the field. + $address = $element['#default_value']; + } + + // Determine the list of available countries, and if the currently selected + // country is not in it, unset it so it can be reset to the default country. + if (!empty($address['country']) && !in_array($address['country'], $element['#available_countries'])) { + unset($address['country']); + } + + // Merge in default values. These only take effect if not already present from + // the settings in $element['#default_value']. + $default_country = isset($address['country']) ? $address['country'] : 'site_default'; + $element['address'] = array(); + $address += addressfield_default_values($element['#available_countries'], $default_country, $element['#context']); + + // Generate the address form. + $element['address'] = addressfield_generate($address, $element['#handlers'], $element['#context'], $element['#available_countries']); + $element['address'] = addressfield_process_format_form($element['address'], $form_state, $form); + + $element['address']['#type'] = (isset($element['#inline']) && $element['#inline'] == TRUE ? 'container' : 'fieldset'); + if (isset($element['#title'])) { + $element['address']['#title'] = $element['#title']; + } + + // Set the element to required based on the passed in required flag. + if (isset($element['#context']['instance']['required'])) { + $element['address']['country']['#required'] = (bool) $element['#context']['instance']['required']; + } + else if (isset($element['#required'])) { + $element['address']['country']['#required'] = isset($element['#required']) ? (bool) $element['#required'] : FALSE; + } + + /* + * Set the #parents of the children of this element so they appear at the same + * level as the parent. This also sets the #required to FALSE if the country + * field is not required. + */ + foreach (element_children($element['address']) as $key) { + $element['address'][$key]['#parents'] = $element['#parents']; + $element['address'][$key]['#parents'][] = $key; + + if ($element['address']['country']['#required'] === FALSE) { + $element['address'][$key]['#required'] = FALSE; + foreach (element_children($element['address'][$key]) as $inner_key) { + $element['address'][$key][$inner_key]['#required'] = FALSE; + } + } + } + + return $element; +} + +/** * Form API process function: set the #parents of the children of this element so they appear at the same level as the parent. */ function addressfield_widget_process($element) { @@ -368,29 +446,25 @@ function addressfield_field_info() { /** * Returns an array of default values for the addressfield form elements. * - * @param $field - * The field array. - * @param $instance - * The instance array. - * @param $address - * The current address values, if known. Allows for per-country defaults. + * @param $available_countries + * An array of country code-indexed country names. + * @param $default_country + * The default country to use. + * @param $context * - * @return - * An array of default values. + * @return array An array of default values. */ -function addressfield_default_values($field, $instance, array $address = array()) { - $available_countries = _addressfield_country_options_list($field, $instance); - $default_country = $instance['widget']['settings']['default_country']; +function addressfield_default_values($available_countries, $default_country, $context) { // Resolve the special site_default option. if ($default_country == 'site_default') { $default_country = variable_get('site_default_country', ''); } // Fallback to the first country in the list if the default country is not // available, or is empty even though the field is required. - $not_available = $default_country && !isset($available_countries[$default_country]); + $not_available = $default_country && !in_array($default_country, $available_countries); $empty_but_required = empty($default_country) && !empty($instance['required']); if ($not_available || $empty_but_required) { - $default_country = key($available_countries); + $default_country = reset($available_countries); } $default_values = array( @@ -411,14 +485,34 @@ function addressfield_default_values($field, $instance, array $address = array() ); // Allow other modules to alter the default values. + drupal_alter('addressfield_default_values', $default_values, $context); + return $default_values; +} + +/** + * Returns an array of default values for the addressfield form elements. + * + * @param $field + * The field array. + * @param $instance + * The instance array. + * @param $address + * The current address values, if known. Allows for per-country defaults. + * + * @return + * An array of default values. + */ +function addressfield_default_values_for_field($field, $instance, array $address = array()) { + $available_countries = _addressfield_field_country_list($field, $instance); + $default_country = $instance['widget']['settings']['default_country']; + $context = array( 'field' => $field, 'instance' => $instance, 'address' => $address, ); - drupal_alter('addressfield_default_values', $default_values, $context); - return $default_values; + return addressfield_default_values($available_countries, $default_country, $context); } /** @@ -554,14 +648,15 @@ function addressfield_field_widget_form(&$form, &$form_state, $field, $instance, // Determine the list of available countries, and if the currently selected // country is not in it, unset it so it can be reset to the default country. - $countries = _addressfield_country_options_list($field, $instance); + $countries = _addressfield_country_options_list( + !empty($instance['widget']['settings']['available_countries']) + ? $instance['widget']['settings']['available_countries'] + : array() + ); if (!empty($address['country']) && !isset($countries[$address['country']])) { unset($address['country']); } - // Merge in default values. - $address += addressfield_default_values($field, $instance, $address); - // Add the form elements for the standard widget, which includes a country // select list at the top that reloads the available address elements when the // country is changed. @@ -571,28 +666,23 @@ function addressfield_field_widget_form(&$form, &$form_state, $field, $instance, // in that context, and it is overridable if necessary. $element['#type'] = 'fieldset'; - if (!empty($instance['description'])) { - // Checkout panes convert the fieldset into a container, causing - // #description to not be rendered. Hence, a real element is added and - // the old #description is removed. - $element['#description'] = ''; - $element['element_description'] = array( - '#markup' => $instance['description'], - '#prefix' => '