diff --git a/modules/cart/commerce_cart.module b/modules/cart/commerce_cart.module index 1f2dbff..1729fa8 100644 --- a/modules/cart/commerce_cart.module +++ b/modules/cart/commerce_cart.module @@ -1599,6 +1599,92 @@ function commerce_cart_field_instance_is_attribute($instance) { } /** + * Returns an array of attribute fields instances with their settings and options. + * + * @param $product_type + * The info array of the field instance whose attribute settings should be + * retrieved. + * @param $default_product + * A product entity object for getting the attribute fields options. + * + * @return + * An array of attribute fields with their settings including: + * - field: field definition data; + * - instance: field instance definition data; + * - commerce_cart_settings: field instance (attribute) cart settings; + * - options: field instance options; + * - weight: field instance weight; + * - required: field instance required setting. + */ +function commerce_cart_product_type_attributes($product_type, $default_product = NULL) { + $attributes = array(); + // Use initialized product object if it is not defined. + if (!$default_product) { + $default_product = commerce_product_new(); + } + // Loop through all the field instances on that product type. + foreach (field_info_instances('commerce_product', $product_type) as $field_name => $instance) { + // A field qualifies if it is single value, required and uses a widget + // with a definite set of options. For the sake of simplicity, this is + // currently restricted to fields defined by the options module. + $field = field_info_field($field_name); + + // If the instance is of a field type that is eligible to function as + // a product attribute field and if its attribute field settings + // specify that this functionality is enabled... + if (commerce_cart_field_attribute_eligible($field) && commerce_cart_field_instance_is_attribute($instance)) { + // Get the options properties from the options module for the + // attribute widget type selected for the field, defaulting to the + // select list options properties. + $commerce_cart_settings = commerce_cart_field_instance_attribute_settings($instance); + + switch ($commerce_cart_settings['attribute_widget']) { + case 'checkbox': + $widget_type = 'onoff'; + break; + case 'radios': + $widget_type = 'buttons'; + break; + default: + $widget_type = 'select'; + } + + $properties = _options_properties($widget_type, FALSE, TRUE, TRUE); + + // Try to fetch localized names. + $allowed_values = NULL; + + // Prepare translated options if using the i18n_field module. + if (module_exists('i18n_field')) { + if (($translate = i18n_field_type_info($field['type'], 'translate_options'))) { + $allowed_values = $translate($field); + _options_prepare_options($allowed_values, $properties); + } + } + + // Otherwise just use the base language values. + if (empty($allowed_values) && $default_product) { + $allowed_values = _options_get_options($field, $instance, $properties, 'commerce_product', $default_product); + } + + // Only consider this field a qualifying attribute field if we could + // derive a set of options for it. + if (!empty($allowed_values)) { + $attributes[$field_name] = array( + 'field' => $field, + 'instance' => $instance, + 'commerce_cart_settings' => $commerce_cart_settings, + 'options' => $allowed_values, + 'weight' => $instance['widget']['weight'], + 'required' => $instance['required'], + ); + } + } + } + return $attributes; +} + +/** * Returns an array of cart form field access settings for a field instance. * * Fields attached to line item types can be included on the Add to Cart form so @@ -1939,65 +2025,7 @@ function commerce_cart_add_to_cart_form($form, &$form_state, $line_item, $show_q // If all the products are of the same type... if ($same_type) { - // Loop through all the field instances on that product type. - foreach (field_info_instances('commerce_product', $type) as $field_name => $instance) { - // A field qualifies if it is single value, required and uses a widget - // with a definite set of options. For the sake of simplicity, this is - // currently restricted to fields defined by the options module. - $field = field_info_field($field_name); - - // If the instance is of a field type that is eligible to function as - // a product attribute field and if its attribute field settings - // specify that this functionality is enabled... - if (commerce_cart_field_attribute_eligible($field) && commerce_cart_field_instance_is_attribute($instance)) { - // Get the options properties from the options module for the - // attribute widget type selected for the field, defaulting to the - // select list options properties. - $commerce_cart_settings = commerce_cart_field_instance_attribute_settings($instance); - - switch ($commerce_cart_settings['attribute_widget']) { - case 'checkbox': - $widget_type = 'onoff'; - break; - case 'radios': - $widget_type = 'buttons'; - break; - default: - $widget_type = 'select'; - } - - $properties = _options_properties($widget_type, FALSE, TRUE, TRUE); - - // Try to fetch localized names. - $allowed_values = NULL; - - // Prepare translated options if using the i18n_field module. - if (module_exists('i18n_field')) { - if (($translate = i18n_field_type_info($field['type'], 'translate_options'))) { - $allowed_values = $translate($field); - _options_prepare_options($allowed_values, $properties); - } - } - - // Otherwise just use the base language values. - if (empty($allowed_values)) { - $allowed_values = _options_get_options($field, $instance, $properties, 'commerce_product', $default_product); - } - - // Only consider this field a qualifying attribute field if we could - // derive a set of options for it. - if (!empty($allowed_values)) { - $qualifying_fields[$field_name] = array( - 'field' => $field, - 'instance' => $instance, - 'commerce_cart_settings' => $commerce_cart_settings, - 'options' => $allowed_values, - 'weight' => $instance['widget']['weight'], - 'required' => $instance['required'], - ); - } - } - } + $qualifying_fields = commerce_cart_product_type_attributes($type, $default_product); } // Otherwise for products of varying types, display a simple select list @@ -2034,19 +2062,35 @@ function commerce_cart_add_to_cart_form($form, &$form_state, $line_item, $show_q if ($product_wrapper->{$field_name}->raw() != NULL) { $field_has_options[$field_name] = TRUE; } - $used_options[$field_name][] = $product_wrapper->{$field_name}->raw(); + + // Build the options. + $field_value = $product_wrapper->{$field_name}->raw(); + // Use tokens if token formatter specified and there is only attribute defined. + if ((empty($qualifying_fields) || count($qualifying_fields) == 1) && $line_item->data['context']['product_select_options_use_tokens'] && !empty($line_item->data['context']['product_select_options_tokens_formatter'])) { + if (module_exists('token')) { + $token_type = token_get_entity_mapping('entity', 'commerce_product'); + $options[$field_name][$field_value] = token_replace($line_item->data['context']['product_select_options_tokens_formatter'], array($token_type => $product, 'entity' => $product, 'entity_type' => 'commerce_product'), array('clear' => TRUE)); + } + else { + $options[$field_name][$field_value] = token_replace($line_item->data['context']['product_select_options_tokens_formatter'], array('commerce-product' => $product), array('clear' => TRUE)); + } + } + // Use the default field options. + else { + $options[$field_name][$field_value] = $data['options'][$field_value]; + } } // If for some reason no options for this field are used, remove it // from the qualifying fields array. - if (empty($field_has_options[$field_name]) || empty($used_options[$field_name])) { + if (empty($field_has_options[$field_name]) || empty($options[$field_name])) { unset($qualifying_fields[$field_name]); } else { $form['attributes'][$field_name] = array( '#type' => $data['commerce_cart_settings']['attribute_widget'], '#title' => commerce_cart_attribute_widget_title($data['instance']), - '#options' => array_intersect_key($data['options'], drupal_map_assoc($used_options[$field_name])), + '#options' => $options[$field_name], '#default_value' => $default_product_wrapper->{$field_name}->raw(), '#weight' => $data['instance']['widget']['weight'], '#ajax' => array( @@ -2056,7 +2100,7 @@ function commerce_cart_add_to_cart_form($form, &$form_state, $line_item, $show_q // Add the empty value if the field is not required and products on // the form include the empty value. - if (!$data['required'] && in_array('', $used_options[$field_name])) { + if (!$data['required'] && !empty($used_options) && in_array('', $used_options[$field_name])) { $form['attributes'][$field_name]['#empty_value'] = ''; } @@ -2104,7 +2148,20 @@ function commerce_cart_add_to_cart_form($form, &$form_state, $line_item, $show_q $options = array(); foreach ($matching_products as $product_id => $product) { - $options[$product_id] = $product->title; + // Use tokens if token formatter specified and there is only attribute defined. + if ($line_item->data['context']['product_select_options_use_tokens'] && !empty($line_item->data['context']['product_select_options_tokens_formatter'])) { + if (module_exists('token')) { + $token_type = token_get_entity_mapping('entity', 'commerce_product'); + $options[$product_id] = token_replace($line_item->data['context']['product_select_options_tokens_formatter'], array($token_type => $product, 'entity' => $product, 'entity_type' => 'commerce_product'), array('clear' => TRUE)); + } + else { + $options[$product_id] = token_replace($line_item->data['context']['product_select_options_tokens_formatter'], array('commerce-product' => $product), array('clear' => TRUE)); + } + } + // Use the default field options. + else { + $options[$product_id] = $product->title; + } } // Note that this element by default is a select list, so its @@ -2136,7 +2193,8 @@ function commerce_cart_add_to_cart_form($form, &$form_state, $line_item, $show_q // any qualifying attribute fields... if (!$same_type || empty($qualifying_fields)) { // For a single product form, just add the hidden product_id field. - if (count($products) == 1) { + // and the "show_single_product_attributes" is not checked. + if (count($products) == 1 && empty($line_item->data['context']['show_single_product_attributes'])) { $form['product_id'] = array( '#type' => 'hidden', '#value' => $default_product->product_id, @@ -2147,7 +2205,20 @@ function commerce_cart_add_to_cart_form($form, &$form_state, $line_item, $show_q $options = array(); foreach ($products as $product_id => $product) { - $options[$product_id] = $product->title; + // Use tokens if token formatter specified and there is only attribute defined. + if ($line_item->data['context']['product_select_options_use_tokens'] && !empty($line_item->data['context']['product_select_options_tokens_formatter'])) { + if (module_exists('token')) { + $token_type = token_get_entity_mapping('entity', 'commerce_product'); + $options[$product_id] = token_replace($line_item->data['context']['product_select_options_tokens_formatter'], array($token_type => $product, 'entity' => $product, 'entity_type' => 'commerce_product'), array('clear' => TRUE)); + } + else { + $options[$product_id] = token_replace($line_item->data['context']['product_select_options_tokens_formatter'], array('commerce-product' => $product), array('clear' => TRUE)); + } + } + // Use the default field options. + else { + $options[$product_id] = $product->title; + } } // Note that this element by default is a select list, so its #options @@ -2447,6 +2518,8 @@ function commerce_cart_field_formatter_info() { 'combine' => TRUE, 'show_single_product_attributes' => FALSE, 'line_item_type' => 'product', + 'product_select_options_use_tokens' => FALSE, + 'product_select_options_tokens_formatter' => FALSE, ), ), ); @@ -2485,8 +2558,8 @@ function commerce_cart_field_formatter_settings_form($field, $instance, $view_mo $element['show_single_product_attributes'] = array( '#type' => 'checkbox', - '#title' => t('Show attribute widgets even if the Add to Cart form only represents one product.'), - '#description' => t('If enabled, attribute widgets will be shown on the form with the only available options selected.'), + '#title' => t('Show attribute widgets or product selection even if the Add to Cart form only represents one product.'), + '#description' => t('If enabled, attribute widgets or product selection will be shown on the form with the only available options selected.'), '#default_value' => $settings['show_single_product_attributes'], ); @@ -2509,6 +2582,90 @@ function commerce_cart_field_formatter_settings_form($field, $instance, $view_mo } } + // Token formatter, display alternative for the product select options. + // Only for multiple product reference, except for single referenced product type + // with multiple attributes defined. + if ($field['cardinality'] != 1) { + $referenceable_product_types = $instance['settings']['referenceable_types']; + $product_types = array_filter($referenceable_product_types); + // If there is no product type selected, use all product types. + if (empty($product_types)) { + $product_types = array_keys($referenceable_product_types); + } + + $token_formatter = TRUE; + // Check for single referenced product type with multiple attributes defined. + if (count($product_types) == 1) { + $product_type = reset($product_types); + // Get product type attributes. + $attributes = commerce_cart_product_type_attributes($product_type); + if (count($attributes) > 1) { + $token_formatter = FALSE; + } + } + + if ($token_formatter) { + $use_tokens_descripton = t('If enabled, togheter with the replacement text, it will change the display values for the product select options.'); + $use_tokens_descripton .= '
'; + $use_tokens_descripton .= t('Exception: it will not work for single referenced product type with multiple attributes defined.'); + $element['product_select_options_use_tokens'] = array( + '#type' => 'checkbox', + '#title' => t('Use tokens for product select options display values.'), + '#description' => $use_tokens_descripton, + '#default_value' => isset($settings['product_select_options_use_tokens']) ? $settings['product_select_options_use_tokens'] : FALSE, + ); + + $tokens_formatter_description = t('Use tokens available for customize the display values of the product select options.'); + $tokens_formatter_description .= '
'; + $tokens_formatter_description .= t('For multiple referenced product types, be sure you are using the right tokens. A good practice in this case is usage of tokens of the mutual fields.'); + $element['product_select_options_tokens_formatter'] = array( + '#type' => 'textarea', + '#title' => t('Replacement text with token support'), + '#description' => $tokens_formatter_description, + '#default_value' => $settings['product_select_options_tokens_formatter'], + '#states' => array( + 'visible' => array( + ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][product_select_options_use_tokens]"]' => array('checked' => TRUE), + ), + ), + ); + $element['product_select_options_tokens_help'] = array( + '#type' => 'container', + '#states' => array( + 'visible' => array( + ':input[name="fields[' . $field['field_name'] . '][settings_edit_form][settings][product_select_options_use_tokens]"]' => array('checked' => TRUE), + ), + ), + ); + if (module_exists('token')) { + $token_type = token_get_entity_mapping('entity', 'commerce_product'); + $element['product_select_options_tokens_help']['token_tree'] = array( + '#theme' => 'token_tree', + '#token_types' => array($token_type, 'entity'), + ); + $element['product_select_options_tokens_formatter']['#element_validate'] = array('token_element_validate'); + } + else { + $token_info = array( + 'site' => array( + 'type' => 'site', + 'label' => t('Site information'), + 'description' => t('Site-wide settings and other global information.'), + ), + 'commerce_product' => array( + 'label' => t('Product'), + 'type' => 'commerce_product', + ), + ); + + $element['product_select_options_tokens_help']['token_tree'] = RulesTokenEvaluator::help($token_info); + $element['product_select_options_tokens_help']['token_extra'] = array( + '#markup' => '

' . t("Optional: Install Token module to improve tokens.", array('@token-url' => 'http://drupal.org/project/token')) . '

', + ); + } + } + } + return $element; } @@ -2541,6 +2698,27 @@ function commerce_cart_field_formatter_settings_summary($field, $instance, $view if (count(commerce_product_line_item_types()) > 1) { $summary[] = t('Add to Cart line item type: @type', array('@type' => commerce_line_item_type_get_name($settings['line_item_type']))); } + + // Token formatter setting if available. + $referenceable_product_types = $instance['settings']['referenceable_types']; + // If there is only one product type available, use it. + if (count($referenceable_product_types) == 1) { + $product_types = array_keys($referenceable_product_types); + } + // For more then one product type available, check the ones selected. + else { + $product_types = array_filter($referenceable_product_types); + } + // Only for single product type available for the product reference field instance. + if (count($product_types) == 1) { + foreach ($product_types as $product_type) { + // Get product type attributes. + $attributes = commerce_cart_product_type_attributes($product_type); + if (count($attributes) == 1) { + $summary[] = t('Tokens replacement: !token_formatter', array('!token_formatter' => !empty($settings['product_select_options_tokens_formatter']) ? $settings['product_select_options_tokens_formatter'] : t('Empty (not set).'))); + } + } + } } return implode('
', $summary); @@ -2576,6 +2754,8 @@ function commerce_cart_field_formatter_view($entity_type, $entity, $field, $inst $line_item->data['context']['product_ids'] = array_keys($products); $line_item->data['context']['add_to_cart_combine'] = !empty($settings['combine']); $line_item->data['context']['show_single_product_attributes'] = !empty($settings['show_single_product_attributes']); + $line_item->data['context']['product_select_options_use_tokens'] = isset($settings['product_select_options_use_tokens']) ? $settings['product_select_options_use_tokens'] : FALSE; + $line_item->data['context']['product_select_options_tokens_formatter'] = isset($settings['product_select_options_tokens_formatter']) ? $settings['product_select_options_tokens_formatter'] : FALSE; $result[] = array( '#arguments' => array( @@ -2608,6 +2788,10 @@ function commerce_cart_field_attach_view_alter(&$output, $context) { if (!empty($element['#formatter']) && $element['#formatter'] == 'commerce_cart_add_to_cart_form') { // Prepare the context information needed by the cart form. $cart_context = $context; + // Add the entity bundle to form context. + $cart_context['bundle'] = $element['#bundle']; + // Add the product reference field name to form context. + $cart_context['field'] = $field_name; // Remove the full entity from the context array and put the ID in instead. list($entity_id, $vid, $bundle) = entity_extract_ids($context['entity_type'], $context['entity']);