fields('s', array('weight')) ->condition('name', 'commerce_product_bundle', '=') ->execute() ->fetchField(); if (isset($weight)) { db_update('system') ->fields(array('weight' => $weight +1)) ->condition('name', 'commerce_pricing_attributes', '=') ->execute(); } } /** * Implements hook_theme(). */ function commerce_pricing_attributes_theme($existing, $type, $theme, $path) { return array( 'commerce_pricing_attributes_details' => array( 'render element' => 'element', ), ); } /** * Implements hook_field_info(); */ function commerce_pricing_attributes_field_info() { return array( 'commerce_pricing_attributes' => array( 'label' => t('Pricing Attributes'), 'description' => t('This field stores the ID of the option and the price calculation rules.'), 'settings' => array(), 'instance_settings' => array(), 'default_widget' => 'commerce_pricing_attributes_custom_widget', 'default_formatter' => 'commerce_pricing_attributes_form', 'property_type' => 'commerce_pricing_attributes', 'property_callbacks' => array('commerce_pricing_attributes_property_info_callback'), ), ); } /** * Callback to alter the property info of commerce_pricing_attributes fields. * * @see commerce_pricing_attributes_field_info(). */ function commerce_pricing_attributes_property_info_callback(&$info, $entity_type, $field, $instance, $field_type) { $name = $field['field_name']; $property = &$info[$entity_type]['bundles'][$instance['bundle']]['properties'][$name]; $property['type'] = ($field['cardinality'] != 1) ? 'list' : 'commerce_pricing_attributes'; $property['getter callback'] = 'entity_metadata_field_verbatim_get'; $property['setter callback'] = 'entity_metadata_field_verbatim_set'; $property['auto creation'] = 'commerce_pricing_attributes_field_data_auto_creation'; $property['property info'] = commerce_pricing_attributes_field_data_property_info(); unset($property['query callback']); } /** * Returns the default array structure for a commerce_pricing_attributes * field for use when creating new data arrays through an entity * metadata wrapper. */ function commerce_pricing_attributes_field_data_auto_creation() { return array('set_id' => '', 'set_details' => array()); } /** * Defines info for the properties of the commerce_pricing_attributes * field data structure. */ function commerce_pricing_attributes_field_data_property_info($name = NULL) { return array( 'set_id' => array( 'label' => t('Option Set'), 'description' => !empty($name) ? t('Option Set ID of field %name', array('%name' => $name)) : '', 'type' => 'text', 'getter callback' => 'entity_property_verbatim_get', 'setter callback' => 'entity_property_verbatim_set', 'options list' => 'commerce_pricing_attributes_option_set_list', ), ); } /** * Returns an array list of option sets. */ function commerce_pricing_attributes_option_set_list(){ // Retrieve the option sets. $list = commerce_option_get_sets(); // Iterate through the option sets. $options = array(); foreach($list as $item){ $options[$item->set_id] = $item->name; } return $options; } /** * Returns an array of commerce_pricing_attributes field names from a * specific entity. */ function _commerce_pricing_attributes_get_fields($entity_type, $entity) { $commerce_pricing_attibutes_fields = array(); // Determine the list of instances to iterate on. list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); $instances = field_info_instances($entity_type, $bundle); $fields = field_info_fields(); // Iterate through the instances and collect results. foreach ($instances as $instance) { $field_name = $instance['field_name']; // If the instance is a commerce_pricing_attributes field with data. if ($fields[$field_name]['type'] == 'commerce_pricing_attributes' && isset($entity->{$field_name})) { $commerce_pricing_attibutes_fields[] = $field_name; } } return $commerce_pricing_attibutes_fields; } /** * Converts commerce_pricing_attributes field data to a serialized array. */ function _commerce_pricing_attributes_serialize_set_details($entity_type, $entity) { // Loop over all the commerce_pricing_attributes fields attached to this entity. foreach (_commerce_pricing_attributes_get_fields($entity_type, $entity) as $field_name) { // Iterate over the items arrays for each language. foreach (array_keys($entity->{$field_name}) as $langcode) { $items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array(); // Serialize data arrays before saving. foreach ($items as $delta => $item) { // Serialize an existing data array. if (!empty($item['set_details']) && is_array($item['set_details'])) { $entity->{$field_name}[$langcode][$delta]['set_details'] = serialize($item['set_details']); } } } } } /** * Converts saved commerce_pricin_attributes field data columns back to arrays * for use in the rest of the current page request execution. */ function _commerce_pricing_attributes_unserialize_set_details($entity_type, $entity) { // Loop over all the commerce_pricing_attributes fields attached to this entity. foreach (_commerce_pricing_attributes_get_fields($entity_type, $entity) as $field_name) { // Iterate over the items arrays for each language. foreach (array_keys($entity->{$field_name}) as $langcode) { $items = isset($entity->{$field_name}[$langcode]) ? $entity->{$field_name}[$langcode] : array(); // For each item in the array, unserialize or initialize its data array. foreach ($items as $delta => $item) { // If we have a non-array $item['data'], unserialize it. if (!empty($item['set_details']) && !is_array($item['set_details'])) { $entity->{$field_name}[$langcode][$delta]['set_details'] = unserialize($item['set_details']); } // If we have no data element (or an existing empty), create an empty // array. elseif (empty($item['set_details'])) { $entity->{$field_name}[$langcode][$delta]['set_details'] = array(); } } } } } /** * Implements hook_field_presave(). */ function commerce_pricing_attributes_field_presave($entity_type, $entity, $field, $instance, $langcode, &$items) { // If the instance is a commerce_pricing_attributes field. if ($field['type'] == 'commerce_pricing_attributes') { foreach($items as $delta => &$value){ // Iterate through the set_details if isset. if(!empty($value['set_details'])){ foreach($value['set_details'] as $field_name => &$field){ // If the field is not enabled remove from list. if(!$field['enabled']){ unset($value['set_details'][$field_name]); } } } } } } /** * Implements hook_field_storage_pre_insert(). */ function commerce_pricing_attributes_field_storage_pre_insert($entity_type, $entity) { _commerce_pricing_attributes_serialize_set_details($entity_type, $entity); } /** * Implements hook_field_storage_pre_update(). */ function commerce_pricing_attributes_field_storage_pre_update($entity_type, $entity) { _commerce_pricing_attributes_serialize_set_details($entity_type, $entity); } /** * Implements hook_field_attach_insert(). */ function commerce_pricing_attributes_field_attach_insert($entity_type, $entity) { _commerce_pricing_attributes_unserialize_set_details($entity_type, $entity); } /** * Implements hook_field_attach_update(). */ function commerce_pricing_attributes_field_attach_update($entity_type, $entity) { _commerce_pricing_attributes_unserialize_set_details($entity_type, $entity); } /** * Implements hook_field_load(). */ function commerce_pricing_attributes_field_load($entity_type, $entities, $field, $instances, $langcode, &$items, $age) { // Convert amounts to their floating point values and deserialize data arrays. foreach ($entities as $id => $entity) { foreach ($items[$id] as $delta => $item) { // Unserialize the data array if necessary. if (!empty($items[$id][$delta]['set_details']) && !is_array($items[$id][$delta]['set_details'])) { $items[$id][$delta]['set_details'] = unserialize($items[$id][$delta]['set_details']); } else { $items[$id][$delta]['set_details'] = array(); } } } } /** * Implements hook_field_is_empty(). */ function commerce_pricing_attributes_field_is_empty($item, $field) { // set_id = 0 is empty too, which is exactly what we want. return empty($item['set_id']) or empty($item['set_details']); } /* * Implements hook_form_FORM_ID_alter(). * * This is used to remove cardinality from commererce_pricing_attributes fields. */ function commerce_pricing_attributes_form_field_ui_field_edit_form_alter(&$form, &$form_state) { if ($form['#field']['type'] == 'commerce_pricing_attributes') { $form['field']['cardinality']['#default_value'] = 1; $form['field']['cardinality']['#access'] = FALSE; } if($form['#instance']['entity_type'] == 'commerce_option' and $form['#field']['module'] == 'list'){ array_unshift($form['#submit'], 'commerce_pricing_attributes_field_ui_field_edit_form_submit'); } } /** * Form submission handler for field_ui_field_edit_form(). */ function commerce_pricing_attributes_field_ui_field_edit_form_submit($form, &$form_state){ $instance = $form_state['build_info']['args'][0]; $_GET['destination'] = 'admin/commerce/products/option-sets/manage/'.$instance['bundle'].'/fields/'.$instance['field_name'].'/pricing_attribute_settings'; if(!isset($instance['settings']['commerce_pricing_attributes'])){ $instance['settings']['commerce_pricing_attributes'] = array(); $form_state['values']['instance']['settings']['commerce_pricing_attributes'] = array(); $form_state['values']['instance']['settings']['commerce_pricing_attributes']['_none']['weight'] = -100; } $i = 0; foreach ($form_state['values']['field']['settings']['allowed_values'] as $option_value => $option_label) { if(array_key_exists($option_value, $instance['settings']['commerce_pricing_attributes'])){ $form_state['values']['instance']['settings']['commerce_pricing_attributes'][$option_value] = $instance['settings']['commerce_pricing_attributes'][$option_value]; } else{ $form_state['values']['instance']['settings']['commerce_pricing_attributes'][$option_value] = array( 'enabled' => TRUE, 'price_op' => 'plus', 'price' => 0, 'calculate' => 'per_item', 'weight' => 100, ); $i++; } } } /** * Implements hook_field_widget_info(). */ function commerce_pricing_attributes_field_widget_info() { return array( 'commerce_pricing_attributes_custom_widget' => array( 'label' => t('Pricing Attributes Widget'), 'description' => t('Display the list of option sets in a way that you can customize the settings and define pricing rules.'), 'field types' => array('commerce_pricing_attributes'), ), ); } /** * Implements hook_field_widget_form(). */ function commerce_pricing_attributes_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) { if($instance['widget']['type'] == 'commerce_pricing_attributes_custom_widget') { // Get the option set list. $option_sets = commerce_option_get_sets(); $options = array('' => t('none')); foreach($option_sets as $set) { $options[$set->set_id] = $set->name; } $element_replacement = drupal_html_class(implode('-', array('commerce-pricing-attributes-set-details', $element['#field_name'], $delta))); // Element to select option set. $element['set_id'] = $element + array( '#type' => 'select', '#default_value' => isset($items[$delta]['set_id']) ? $items[$delta]['set_id'] : NULL, '#attributes' => array('class' => array('commerce-pricing-attributes')), '#options' => $options, '#ajax' => array( 'callback' => 'commerce_pricing_attributes_widget_set_form_callback', 'wrapper' => $element_replacement, 'method' => 'html' ), ); // The container of the set details. $element['set_details'] = array( '#type' => 'container', '#attributes' => array( 'id' => $element_replacement, 'class' => array('commerce-pricing-attributes-set-details'), ), '#weight' => 999, ); if(isset($form_state['triggering_element']) and isset($form_state['triggering_element']['#field_name'])and $form_state['triggering_element']['#field_name'] == $element['#field_name']){ // Get submitted values from the ajax request. $element_state = drupal_array_get_nested_value($form_state['values'], $element['#field_parents']); // Get the selected set_id. $items[$delta] = array(); if(!empty($element_state[$element['#field_name']][$langcode][$delta]['set_id'])){ $items[$delta]['set_id'] = $element_state[$element['#field_name']][$langcode][$delta]['set_id']; $items[$delta]['set_details'] = array(); } // Get the selected set_details. if(!empty($element_state[$element['#field_name']][$langcode][$delta]['set_details'])){ $items[$delta]['set_details'] = $element_state[$element['#field_name']][$langcode][$delta]['set_details']; } } // If set_id is selected. if(isset($items[$delta]) and !empty($items[$delta]['set_id'])){ // Retrieve the fields of the selected option set. $option_fields = field_info_instances('commerce_option', $items[$delta]['set_id']); // Iterate through the fields of the selected option set. foreach($option_fields as $option_field_name => $option_field_instance){ // Get field settings. $option_field = field_info_field($option_field_name); $element['set_details'][$option_field_name] = array( '#type' => 'container', '#attributes' => array( 'class' => array('commerce-pricing-attributes-set-details-item'), ), ); $option_replacement = drupal_html_class(implode('-', array($element['#field_name'], $delta, $option_field_instance['id']))); $element['set_details'][$option_field_name]['enabled'] = array( '#type' => 'checkbox', '#title' => $option_field_instance['label'], '#title_display' => 'after', '#return_value' => 1, '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['enabled'])?$items[$delta]['set_details'][$option_field_name]['enabled']:0, '#attributes' => array( 'class' => array('commerce-pricing-attributes-set-details-item-enabled'), ), '#field_name' => $element['#field_name'], '#ajax' => array( 'callback' => 'commerce_pricing_attributes_widget_set_form_option_callback', 'wrapper' => 'commerce-pricing-attributes-set-details-options-' . $option_replacement, 'method' => 'replace', 'progress' => 'none', ), ); $element['set_details'][$option_field_name]['required'] = array( '#type' => 'checkbox', '#title' => t('Required'), '#title_display' => 'after', '#return_value' => 1, '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['required'])?$items[$delta]['set_details'][$option_field_name]['required']:0, ); $element['set_details'][$option_field_name]['show_values'] = array( '#type' => 'checkbox', '#title' => t('Show price values in the attributes list'), '#title_display' => 'after', '#return_value' => 1, '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['show_values'])?$items[$delta]['set_details'][$option_field_name]['show_values']:0, ); // If field type is list, we need to enable option selection. if($option_field['module'] == 'list'){ // Get the default settings for this field. $defaults = $option_field_instance['settings']['commerce_pricing_attributes']; $element['set_details'][$option_field_name]['show_hide'] = array( '#type' => 'container', 'markup' => array( '#markup' => ''.t('Show Advanced Settings').'', ), '#attributes' => array( 'class' => array('form-item'), ), '#id' => 'commerce-pricing-attributes-set-details-show-hide-' . $option_replacement, ); $element['set_details'][$option_field_name]['options'] = array( '#type' => 'container', '#attributes' => array( 'class' => array('commerce-pricing-attributes-set-details-options'), 'id' => 'commerce-pricing-attributes-set-details-options-' . $option_replacement, ), ); // If the field is enabled then remove show/hide links. if($element['set_details'][$option_field_name]['enabled']['#default_value'] == 0){ //$element['set_details'][$option_field_name]['show_values']['style'] = 'display:none'; $element['set_details'][$option_field_name]['show_hide']['#attributes']['style'] = 'display:none'; continue; } $element['set_details'][$option_field_name]['options']['#theme'] = 'commerce_pricing_attributes_details'; // Get the field options. $options = list_allowed_values($option_field, $option_field_instance); // Add default value. $options = array('_none' => t('- Select -')) + $options; // Iterate over default field options. foreach($options as $option_value => $option_key){ $option_value = (String)$option_value; $option_value_replacement = drupal_html_class(implode('-', array($element['#field_name'], $delta, $option_field_instance['id'], $option_value))); // If the field type is boolean list and doesn't have labels defined. if(empty($option_key)){ $option_key = $option_value; } // If option is _none then handle it in adifferent way. if($option_value === '_none'){ $element['set_details'][$option_field_name]['options'][$option_value]['enabled'] = array('#markup' => $option_key); } else{ $element['set_details'][$option_field_name]['options'][$option_value]['enabled'] = array( '#type' => 'checkbox', '#title' => $option_key, '#title_display' => 'after', '#return_value' => 1, '#id' => 'commerce-pricing-attributes-set-details-options-' . $option_value_replacement . '-enabled', '#attributes' => array( 'class' => array('commerce-pricing-attributes-set-details-options-enabled'), ), '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['enabled'])?$items[$delta]['set_details'][$option_field_name]['options'][$option_value]['enabled']:$defaults[$option_value]['enabled'], ); } // The element that handles the default option value. // for various cardinality settings. if($option_field['cardinality'] == 1){ // If deafult_value is array then unset it. if(isset($items[$delta]['set_details'][$option_field_name]['default']) and is_array($items[$delta]['set_details'][$option_field_name]['default'])){ $items[$delta]['set_details'][$option_field_name]['default'] = NULL; } $element['set_details'][$option_field_name]['options'][$option_value]['default'] = array( '#type' => 'radio', '#parents' => array_merge($element['#field_parents'], array($element['#field_name'], $element['#language'], $element['#delta'], 'set_details', $option_field_name, 'default')), '#return_value' => $option_value, '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['default'])?$items[$delta]['set_details'][$option_field_name]['default']:NULL, '#id' => 'commerce-pricing-attributes-set-details-options-' . $option_value_replacement . '-default', ); if($option_value === '_none' and !isset($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['default'])){ $element['set_details'][$option_field_name]['options'][$option_value]['default']['#default_value'] = TRUE; } } else if($option_value !== '_none'){ $element['set_details'][$option_field_name]['options'][$option_value]['default'] = array( '#type' => 'checkbox', '#parents' => array_merge($element['#field_parents'], array($element['#field_name'], $element['#language'], $element['#delta'], 'set_details', $option_field_name, 'default', $option_value)), '#return_value' => 1, '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['default'][$option_value])?$items[$delta]['set_details'][$option_field_name]['default'][$option_value]:NULL, '#id' => 'commerce-pricing-attributes-set-details-options-' . $option_value_replacement . '-default', ); } else{ // If the field's cardinality is not 1 then hide _none option's // default value selection element. $element['set_details'][$option_field_name]['options'][$option_value]['default']['#access'] = FALSE; $element['set_details'][$option_field_name]['options'][$option_value]['default']['#default_value'] = FALSE; } // If the option is disabled then it cannot be default option. if($option_value !== '_none' and $element['set_details'][$option_field_name]['options'][$option_value]['enabled']['#default_value'] == 0){ $element['set_details'][$option_field_name]['options'][$option_value]['default']['#disabled'] = TRUE; } // If the option is the default then price and price_op elements are // not needed. if($option_value === '_none'){ $element['set_details'][$option_field_name]['options'][$option_value]['price_op'] = array(); $element['set_details'][$option_field_name]['options'][$option_value]['price'] = array(); } else{ $element['set_details'][$option_field_name]['options'][$option_value]['price_op'] = array( '#type' => 'select', '#options' => array('plus' => '+', 'minus' => '-'), '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['price_op'])?$items[$delta]['set_details'][$option_field_name]['options'][$option_value]['price_op']:$defaults[$option_value]['price_op'], ); // Load the default currency for this instance. $default_currency_code = commerce_default_currency(); $element['set_details'][$option_field_name]['options'][$option_value]['price'] = array( '#type' => 'textfield', '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['price'])?commerce_currency_amount_to_decimal($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['price'], $default_currency_code):commerce_currency_amount_to_decimal($defaults[$option_value]['price'], $default_currency_code), '#required' => TRUE, '#size' => 10, ); // Build a currency options list from all enabled currencies. $options = array(); foreach (commerce_currencies(TRUE) as $currency_code => $currency) { $options[$currency_code] = check_plain($currency['code']); } // If only one currency option is available, don't use a select list. if (count($options) == 1) { $currency_code = key($options); $element['set_details'][$option_field_name]['options'][$option_value]['price']['#field_suffix'] = $currency_code; $element['set_details'][$option_field_name]['options'][$option_value]['currency_code'] = array( '#type' => 'value', '#default_value' => $currency_code, ); } else { $element['set_details'][$option_field_name]['options'][$option_value]['currency_code'] = array( '#type' => 'select', '#options' => $options, '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['currency_code']) ? $items[$delta]['set_details'][$option_field_name]['options'][$option_value]['currency_code'] : $default_currency_code, ); } // Initiate calculate setting if not set. if(!isset($defaults[$option_value]['calculate'])){ $defaults[$option_value]['calculate'] = 'per_item'; } $element['set_details'][$option_field_name]['options'][$option_value]['calculate'] = array( '#type' => 'select', '#options' => array('per_order' => t('per order'), 'per_item' => t('per item')), '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['calculate'])?$items[$delta]['set_details'][$option_field_name]['options'][$option_value]['calculate']:$defaults[$option_value]['calculate'], ); } $element['set_details'][$option_field_name]['options'][$option_value]['weight'] = array( '#type' => 'item', '#input' => TRUE, '#delta' => 100, '#default_value' => isset($items[$delta]['set_details'][$option_field_name]['options'][$option_value]['weight'])?$items[$delta]['set_details'][$option_field_name]['options'][$option_value]['weight']:0, '#process' => array('form_process_weight'), '#attributes' => array( 'class' => array('commerce-pricing-attributes-set-details-options-weight'), ), ); } } } } // Add css and js for the widget. $element['#attached']['css'][] = drupal_get_path('module', 'commerce_pricing_attributes') . '/commerce_pricing_attributes.css'; $element['#attached']['js'][] = drupal_get_path('module', 'commerce_pricing_attributes') . '/commerce_pricing_attributes.js'; // Custom validation over the submitted element values. $element['#element_validate'][] = 'commerce_pricing_attributes_widget_validate'; } return $element; } /** * Form validation handler for commerce_pricing_attributes_field_widget_form(). */ function commerce_pricing_attributes_widget_validate($element, &$form_state) { // If set_id is not empty. if(!empty($element['set_id']['#value'])){ $enabled_fields = 0; // If set_details is not empty. if(!empty($element['set_details'])){ // Iterate through the fields of the option set. foreach(element_children($element['set_details']) as $field_name){ // If field is enabled increment count variable. if($element['set_details'][$field_name]['enabled']['#value']){ $enabled_fields++; } // If field options exists then validate. if(!empty($element['set_details'][$field_name]['options'])){ foreach(element_children($element['set_details'][$field_name]['options']) as $option){ if($option === '_none') continue; // An option can not be disables and deafult value at the same time. if(!$element['set_details'][$field_name]['options'][$option]['enabled']['#value'] and (($element['set_details'][$field_name]['options'][$option]['default']['#value'] == $option and $element['set_details'][$field_name]['options'][$option]['default']['#type'] == 'radio') or ($element['set_details'][$field_name]['options'][$option]['default']['#value'] == 1 and $element['set_details'][$field_name]['options'][$option]['default']['#type'] == 'checkbox'))){ form_error($element['set_id'], t('You have selected as default value of the option set "%option_set" a disabled option. Please enable the option or change the default option.', array( '%option_set' => $element['set_id']['#options'][$element['set_id']['#value']], ))); } // Price value must be numeric. if (!is_numeric($element['set_details'][$field_name]['options'][$option]['price']['#value'])) { form_error($element['set_details'][$field_name]['options'][$option]['price'], t('%title: you must enter a numeric value for the price amount.', array('%title' => $element['set_id']['#options'][$element['set_id']['#value']]))); } else { form_set_value($element['set_details'][$field_name]['options'][$option]['price'], commerce_currency_decimal_to_amount($element['set_details'][$field_name]['options'][$option]['price']['#value'], $element['set_details'][$field_name]['options'][$option]['currency_code']['#value']), $form_state); } } } } } // If all fields are disabled then throw an error. if(!$enabled_fields){ form_error($element['set_id'], t('Option set "%option_set" must have at least one field eneabled.', array( '%option_set' => $element['set_id']['#options'][$element['set_id']['#value']], ))); } } } /** * Ajax callback for commerce_pricing_attributes_field_widget_form options(). */ function commerce_pricing_attributes_widget_set_form_option_callback($form, $form_state){ // Get the submitted values. $parents = array_slice($form_state['triggering_element']['#array_parents'], 0, -1); $element_state = drupal_array_get_nested_value($form_state['values'], $parents); // Get the form elements "options" and "show_hide" in order to use them later. $options = drupal_array_get_nested_value($form, array_merge($parents, array('options'))); $show_hide_id = drupal_array_get_nested_value($form, array_merge($parents, array('show_hide', '#id'))); $commands = array(); // Replace command of the curren't field set_details area. $commands[] = ajax_command_replace('#' . $form_state['triggering_element']['#ajax']['wrapper'], drupal_render($options)); // If enabling a field then display show/hide buttons and open by default the details setttings. if($element_state['enabled'] == 1){ $commands[] = ajax_command_css('#' . $show_hide_id, array('display' => 'inline')); $commands[] = ajax_command_css('#' . $show_hide_id . ' .commerce-pricing-attributes-set-details-item-hide', array('display' => 'inline')); $commands[] = ajax_command_css('#' . $show_hide_id . ' .commerce-pricing-attributes-set-details-item-show', array('display' => 'none')); $commands[] = ajax_command_css('#' . $form_state['triggering_element']['#ajax']['wrapper'], array('display' => 'block')); } else{ $commands[] = ajax_command_css('#'.$show_hide_id, array('display' => 'none')); } return array('#type' => 'ajax', '#commands' => $commands); } /** * Ajax callback for commerce_pricing_attributes_field_widget_form(). */ function commerce_pricing_attributes_widget_set_form_callback($form, $form_state){ $parents = array_slice($form_state['triggering_element']['#array_parents'], 0, -1); return drupal_array_get_nested_value($form, array_merge($parents, array('set_details'))); } /** * Theme function of set_details sortable table. */ function theme_commerce_pricing_attributes_details($variables){ $output = ''; $rows = array(); $i = 0; $display_default = isset($variables['element']['#display_default'])?$variables['element']['#display_default']:TRUE; foreach (element_children($variables['element']) as $item) { $cells = array( render($variables['element'][$item]['enabled']), render($variables['element'][$item]['default']), render($variables['element'][$item]['price_op']) . render($variables['element'][$item]['price']) . render($variables['element'][$item]['currency_code']) . render($variables['element'][$item]['calculate']), render($variables['element'][$item]['weight']), ); if(!$display_default){ unset($cells[1]); } $rows[$i] = array( 'data' => $cells, 'class' => array(), 'weight' => $variables['element'][$item]['weight']['#default_value'], ); if($item !== '_none'){ $rows[$i]['class'][] = 'draggable'; } else{ $rows[$i]['weight'] = -999999; } $i++; } uasort($rows, 'drupal_sort_weight'); $header = array(t('Default'), t('Price Calculation'), ''); if(!$display_default){ unset($header[1]); } // Support for table select all/none. drupal_add_js('misc/tableselect.js'); array_unshift($header, array('class' => array('select-all'), 'data' => t('Options'))); $output .= theme('table', array('header' => $header, 'rows' => $rows, 'attributes' => array('id' => $variables['element']['#id'].'-table'))); drupal_add_tabledrag($variables['element']['#id'].'-table', 'order', 'sibling', 'commerce-pricing-attributes-set-details-options-weight'); return $output; } /** * Implements hook_field_formatter_info(). */ function commerce_pricing_attributes_field_formatter_info() { return array( 'commerce_pricing_attributes_form' => array( 'label' => t('Option Set: Form'), 'description' => t('Display a form for the customer to enter the options.'), 'field types' => array('commerce_pricing_attributes'), ), 'commerce_pricing_attributes_attribute_view' => array( 'label' => t('Option Set: Attribute View'), 'description' => t('Display the options as attribute view..'), 'field types' => array('commerce_pricing_attributes'), ), ); } /** * Empty formatter of commerce_pricing_attributes field. * * @todo create a field formatter with a table of available options. */ function commerce_pricing_attributes_field_formatter_view($entity_type, $object, $field, $instance, $langcode, $items, $display) { $result = array(); return $result; } /** * Implementation of hook_form_alter() * * Here we modify the add to cart form. */ function commerce_pricing_attributes_form_alter(&$form, &$form_state, $form_id) { if (strstr($form_id, 'commerce_cart_add_to_cart_form')) { if (isset($form_state['default_product'])) { $current_product = $form_state['default_product']; } elseif (isset($form_state['products'])) { $current_product = reset($form_state['products']); } else { return; } $someFieldIsAdded = false; $options = array(); if (isset($form_state['line_item']->line_item_id) && $form_state['line_item']->line_item_id > 0 && commerce_product_attributes_access_to_line_item($form_state['line_item']->line_item_id)) { // We need to reduce the number of options. We can only have one option per product // and per product field and line item. This limits us only in the fact that we can // have one option set per one option set reference field. foreach (commerce_option_load_by_line_item($form_state['line_item']->line_item_id) as $key => $option) { $options[$option->set_id][$option->product_id][$option->field_name][$option->field_delta] = $option; } } // Iterates of the fields of this product. We search for // option set reference fields. foreach ($current_product as $field_name => $field) { $field_info = field_info_field($field_name); $type = $field_info['type']; if ($type == 'commerce_pricing_attributes') { $form[$field_name] = array( '#tree' => TRUE, ); $lang_code = field_language('commerce_product', $current_product, $field_name); if (isset($field[$lang_code])) { foreach($field[$lang_code] as $delta => $set_id) { if (count($options) > 0 && isset($options[$set_id['set_id']][$current_product->product_id][$field_name][$delta])) { $option = $options[$set_id['set_id']][$current_product->product_id][$field_name][$delta]; } else { $option = entity_create('commerce_option', $set_id); } $form_state['commerce_option'][$field_name][$delta]['option'] = $option; $form[$field_name][$delta] = array( '#parents' => array($field_name, $delta), ); field_attach_form('commerce_option', $option, $form[$field_name][$delta], $form_state); $someFieldIsAdded = true; } } } } // TODO: Implement the multi options functionality to the bundle integration if (isset($form_state['bundle'])) { foreach ($form_state['bundle'] as $id => &$bundle_set) { $sub_product = $form_state['bundle'][$id]['default_product']; $sub_product_wrapper = entity_metadata_wrapper('commerce_product', $sub_product); $form[$id] = array( '#tree' => TRUE, ); // Iterates of the fields of this product. We search for // option set reference fields. foreach ($sub_product as $field_name => $field) { $field_info = field_info_field($field_name); $type = $field_info['type']; $form[$id][$field_name] = array( '#tree' => TRUE, ); if ($type == 'commerce_pricing_attributes') { $lang_code = field_language('commerce_product', $sub_product, $field_name); if (isset($field[$lang_code])) { foreach($field[$lang_code] as $delta => $set_id) { if (count($options) > 0 && isset($options[$set_id['set_id']][$current_product->product_id][$field_name][$delta])) { $option = $options[$set_id['set_id']][$sub_product->product_id][$field_name][$delta]; } else { $option = entity_create('commerce_option', $set_id); } $form_state[$id]['commerce_option'][$field_name][$delta]['option'] = $option; $form[$id][$field_name][$delta] = array( '#parents' => array($id, $field_name, $delta), ); field_attach_form('commerce_option', $option, $form[$id][$field_name][$delta], $form_state); $someFieldIsAdded = true; } } } } } } if ($someFieldIsAdded) { $form['#submit'][] = 'commerce_pricing_attributes_add_to_cart_submit'; } } } /** * Cart submit callback function. This is required to create / update * the option related to the line item. * * @param $form Form array * @param $form_state The form state array. * @return void */ function commerce_pricing_attributes_add_to_cart_submit($form, $form_state){ if (isset($form_state['default_product'])) { $current_product = $form_state['default_product']; } elseif (isset($form_state['products'])) { $current_product = reset($form_state['products']); } elseif (empty($form_state['default_product']) && !empty($form_state['default_product_id'])){ $current_product = commerce_product_load($form_state['default_product_id']); } if (empty($current_product)) { return; } $options = array(); if (isset($form_state['line_item']->line_item_id) && $form_state['line_item']->line_item_id > 0 && commerce_product_attributes_access_to_line_item($form_state['line_item']->line_item_id)) { // We need to reduce the number of options. We can only have one option per product // and per product field and line item. This limits us only in the fact that we can // have one option set per one option set reference field. foreach (commerce_option_load_by_line_item($form_state['line_item']->line_item_id) as $key => $option) { $options[$option->set_id][$option->product_id][$option->field_name][$option->field_delta] = $option; } } foreach ($current_product as $field_name => $field) { $field_info = field_info_field($field_name); $type = $field_info['type']; if (isset($form_state['bundle'])) { foreach ($form_state['bundle'] as $id => &$bundle_set) { $sub_product = $form_state['bundle'][$id]['default_product']; $sub_product_wrapper = entity_metadata_wrapper('commerce_product', $sub_product); // Iterates of the fields of this product. We search for // option set reference fields. foreach ($sub_product as $field_name => $field) { $field_info = field_info_field($field_name); $type = $field_info['type']; if ($type == 'commerce_pricing_attributes') { $lang_code = field_language('commerce_product', $sub_product, $field_name); foreach ($field[$lang_code] as $delta => $set_id) { if (count($options) > 0 && isset($options[$set_id['set_id']][$current_product->product_id][$field_name][$delta])) { $option = $options[$set_id['set_id']][$sub_product->product_id][$field_name][$delta]; } else{ $option = $form_state[$id]['commerce_option'][$field_name][$delta]; } // Notify field widgets. field_attach_submit('commerce_option', $option, $form[$id][$field_name][$delta], $form_state); //$option->line_item_id = $form_state['bundle_product_line_items'][$sub_product_wrapper->product_id->value()]->line_item_id; $option->line_item_id = $form_state['line_item']->line_item_id; $option->field_name = $field_name; $option->field_delta = $delta; $option->product_id = $sub_product->product_id; // Save the product. commerce_option_save($option); } } } } } else { if ($type == 'commerce_pricing_attributes') { $lang_code = field_language('commerce_product', $current_product, $field_name); foreach ($field[$lang_code] as $delta => $set_id) { if (count($options) > 0 && isset($options[$set_id['set_id']][$current_product->product_id][$field_name][$delta])) { $option = $options[$set_id['set_id']][$current_product->product_id][$field_name][$delta]; } else{ $option = $form_state['commerce_option'][$field_name][$delta]['option']; } // Notify field widgets. // entity_form_submit_build_entity field_attach_submit('commerce_option', $option, $form[$field_name][$delta], $form_state); $option->line_item_id = $form_state['line_item']->line_item_id; $option->field_name = $field_name; $option->field_delta = $delta; $option->product_id = $current_product->product_id; // Save the product. commerce_option_save($option); } } } } } /** * Implements hook_field_attach_form(). */ function commerce_pricing_attributes_field_attach_form($entity_type, $entity, &$form, &$form_state, $langcode){ // If the field is of type "commerce_pricing_attributes". if($entity_type == 'commerce_option' and isset($entity->set_details)){ // If there are more than one product reset the form value on product change. if(isset($form_state['triggering_element']) and $form_state['triggering_element']['#name'] == 'product_id'){ $form_state['values'][$form['#parents'][0]] = array(); $form_state['input'][$form['#parents'][0]] = array(); } // Iterate through the fields of the form. foreach(element_children($form) as $field_name){ $langcode = $form[$field_name]['#language']; // If the field is enabled for this product. if(isset($entity->set_details[$field_name]) and $entity->set_details[$field_name]['enabled']){ $form[$field_name][$langcode]['#required'] = (bool)$entity->set_details[$field_name]['required']; $display=(bool) $entity->set_details[$field_name]['show_values']; // If the field has options. if(isset($entity->set_details[$field_name]['options']) and !empty($entity->set_details[$field_name]['options'])){ $options = array(); // If - None - value is deafult. if($entity->set_details[$field_name]['default'] === '_none' || $entity->set_details[$field_name]['default'] == ''){ $options['_none'] = t('- None -'); } // sort the field's options. uasort($entity->set_details[$field_name]['options'], 'drupal_sort_weight'); // Iterate through the field's options. foreach ($entity->set_details[$field_name]['options'] as $option_value => $option) { if($option_value === '_none') continue; // If the option is enabled. if($option['enabled']){ // switch the price operator sign. switch ($option['price_op']) { case 'plus': $sign = '+'; break; case 'minus': $sign = '-'; break; default: $sign = ''; break; } $default_currency_code = commerce_default_currency(); // If the currency of the option price is not set then set the deafult currency. if(!isset($option['currency_code']) or empty($option['currency_code'])){ $option['currency_code'] = $default_currency_code; } // Display the price inside the option item. if(isset($form[$field_name][$langcode]['#options']) ){ $options[$option_value] = $form[$field_name][$langcode]['#options'][$option_value]; if($option['price'] > 0 and $display){ $options[$option_value] .= ' (' . $sign . commerce_currency_format($option['price'], $option['currency_code']) . ')'; } } } } // Set the options after processed if are set. if(isset($form[$field_name][$langcode]['#options'])){ $form[$field_name][$langcode]['#options'] = $options; } } // Se the default value if isset. if(!isset($entity->set_details[$field_name]['default'])){ $entity->set_details[$field_name]['default'] = NULL; } if(is_array($entity->set_details[$field_name]['default'])){ $default_value = array(); foreach($entity->set_details[$field_name]['default'] as $key => $value){ if($value){ $default_value[] = $key; } } $form[$field_name][$langcode]['#default_value'] = $default_value; } else{ $form[$field_name][$langcode]['#default_value'] = $entity->set_details[$field_name]['default']; } // If a default value is selected then increment. if(!empty($form[$field_name][$langcode]['#default_value']) and $form[$field_name][$langcode]['#default_value'] !== '_none'){ $form[$field_name][$langcode]['#attributes']['class'][] = 'force-refresh'; } $form[$field_name][$langcode]['#ajax'] = array( 'callback' => 'commerce_pricing_attributes_add_to_cart_form_attributes_refresh', ); $form[$field_name][$langcode]['#attributes']['class'][] = 'commerce-pricing-attributes-item'; } // If field is disabled then do not display. else{ $form[$field_name]['#access'] = FALSE; $form[$field_name][$langcode]['#default_value'] = ''; } } // If at least one attribute has default value then update the price. $form['#attached']['js'][] = array( 'data' => ' (function($){ Drupal.behaviors.commerce_pricing_attributes_add_to_cart_form = { attach: function (context, settings) { if($(context).hasClass("commerce-add-to-cart")){ element = $(context); } else{ element = $(context).find(".commerce-add-to-cart"); } element.once(function(){ $(".commerce-pricing-attributes-item.force-refresh:first", $(this)).trigger("change"); }); } }; })(jQuery); ', 'type' => 'inline', ); } } /** * Ajax callback: returns AJAX commands when an attribute widget is changed. */ function commerce_pricing_attributes_add_to_cart_form_attributes_refresh($form, $form_state){ $commands = array(); if(empty($form_state['default_product']) && !empty($form_state['default_product_id'])){ $form_state['default_product'] = commerce_product_load($form_state['default_product_id']); } if(!empty($form_state['default_product']) and !empty($form_state['context'])){ $product = $form_state['default_product']; $commerce_option_list = array(); foreach($form_state['commerce_option'] as $field_name => $field){ foreach($field as $delta => $item){ $option = $item['option']; field_attach_submit('commerce_option', $option, $form[$field_name][$delta], $form_state); $option->field_name = $field_name; $option->field_delta = $delta; $option->product_id = $product->product_id; $commerce_option_list[] = $option; } } // First create a pseudo product line item that we will pass to Rules. $line_item = commerce_product_line_item_new($product); $line_item->data['commerce_option_list'] = $commerce_option_list; // Allow modules to prepare this as necessary. drupal_alter('commerce_product_calculate_sell_price_line_item', $line_item); // Pass the line item to Rules. rules_invoke_event('commerce_product_calculate_sell_price', $line_item); $product->commerce_price = $line_item->commerce_unit_price; $product->display_context = $form_state['context']; // Rebuild the same array of classes used when the field was first rendered. $replacement_class = drupal_html_class(implode('-', array($form_state['context']['class_prefix'], 'product', 'commerce_price', 'wrapper'))); $entity_type = $form_state['context']['entity_type']; if (isset($form_state['context']['entity'])) { $entity = $form_state['context']['entity']; } $classes = array(); foreach (commerce_info_fields('commerce_product_reference', $entity_type) as $field_name => $field) { if ($field['cardinality'] != 1) { // Construct an array of classes that will be used to theme and // target the rendered field for AJAX replacement. $classes = array( 'commerce-product-field', drupal_html_class('commerce-product-field-commerce-price'), drupal_html_class('field-commerce-price'), drupal_html_class(implode('-', array($form_state['context']['class_prefix'], 'product', 'commerce_price'))), ); } } // Set the product as prepared to prevend price recalculation. $product->_field_view_prepared = TRUE; // Render the commerce price field. $element = field_view_field('commerce_product', $product, 'commerce_price', $form_state['context']['view_mode']); $element += array( '#prefix' => '
', '#suffix' => '
', ); if(!empty($classes)){ $element['#prefix'] .= '
'; $element['#suffix'] .= '
'; } $commands[] = ajax_command_replace(' .' . $replacement_class, drupal_render($element)); } return array('#type' => 'ajax', '#commands' => $commands); } /** * Implements hook_entity_view(). * * Wrap commerce_price elements with a div to achieve ajax refresh on * attribute widget change. */ function commerce_pricing_attributes_entity_view($entity, $entity_type, $view_mode, $langcode){ list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); // If the entity has commerce_price rendered. if(isset($entity->content['product:commerce_price'])){ $entity->content['product:commerce_price'] += array( '#prefix' => '', '#suffix' => '', ); $class = drupal_html_class(implode('-', array($entity_type, $id, 'product', 'commerce_price', 'wrapper'))); $entity->content['product:commerce_price']['#prefix'] = '
' . $entity->content['product:commerce_price']['#prefix']; $entity->content['product:commerce_price']['#suffix'] .= '
'; } } /** * Implements hook_entity_property_info() on top of node module. */ function commerce_pricing_attributes_entity_property_info_alter(&$info) { $info['commerce_line_item']['properties']['commerce_pricing_attributes'] = array( 'label' => t('Commerce Pricing Attributes'), 'type' => 'text', ); } /** * Implements hook_entity_load(). * * Create an alias od commerce_pricing_attributes property in line item on load. * We need it to use it at card combines. */ function commerce_pricing_attributes_entity_load(&$entities, $type){ if($type == 'commerce_line_item'){ foreach ($entities as &$entity) { $entity->commerce_pricing_attributes = array(); $commerce_options = commerce_option_load_by_line_item($entity->line_item_id); foreach($commerce_options as $commerce_option){ $commerce_option_wrapper = entity_metadata_wrapper('commerce_option', $commerce_option); $fields = field_info_instances('commerce_option', $commerce_option->set_id); foreach($fields as $field_name => $field){ $entity->commerce_pricing_attributes[$commerce_option->set_id][$field_name] = $commerce_option_wrapper->{$field_name}->value(); asort($entity->commerce_pricing_attributes[$commerce_option->set_id]); } } asort($entity->commerce_pricing_attributes); $entity->commerce_pricing_attributes = serialize($entity->commerce_pricing_attributes); } } } /** * Implements hook_field_attach_submit(). * * Create an alias od commerce_pricing_attributes property in line item on * product add to cart. We need it to use it at card combines. */ function commerce_pricing_attributes_field_attach_submit($entity_type, &$entity, $form, &$form_state){ if($entity_type == 'commerce_line_item'){ if (isset($form_state['default_product'])) { $current_product = $form_state['default_product']; } elseif (isset($form_state['products'])) { $current_product = reset($form_state['products']); } else { return; } $entity->commerce_pricing_attributes = array(); foreach ($current_product as $field_name => $field) { $field_info = field_info_field($field_name); $type = $field_info['type']; if ($type == 'commerce_pricing_attributes') { $lang_code = field_language('commerce_product', $current_product, $field_name); if(isset($field[$lang_code])){ foreach ($field[$lang_code] as $delta => $set_id) { if(!empty($form_state['values'][$field_name])){ foreach($form_state['values'][$field_name][$delta] as $field_name => $values){ $entity->commerce_pricing_attributes[$set_id['set_id']][$field_name] = isset($values[$lang_code][0]['value'])?$values[$lang_code][0]['value']:''; } asort($entity->commerce_pricing_attributes[$set_id['set_id']]); } } } } } asort($entity->commerce_pricing_attributes); $entity->commerce_pricing_attributes = serialize($entity->commerce_pricing_attributes); } } /** * Implements hook_commerce_cart_product_comparison_properties_alter(). */ function commerce_pricing_attributes_commerce_cart_product_comparison_properties_alter(&$comparison_properties){ $comparison_properties[] = 'commerce_pricing_attributes'; } /** * Implements hook_menu(). */ function commerce_pricing_attributes_menu(){ $items = array(); $items['admin/commerce/products/option-sets/manage/%entity_object/fields/%field_ui_menu/pricing_attribute_settings'] = array( 'title' => 'Pricing Attributes', 'page callback' => 'drupal_get_form', 'page arguments' => array('commerce_pricing_attributes_default_setting_form', 5, 7), 'type' => MENU_LOCAL_TASK, 'weight' => 5, 'file' => 'commerce_pricing_attributes.admin.inc', 'access callback' => 'commerce_pricing_attributes_default_setting_access', 'access arguments' => array('administer option sets', 5, 7), ); return $items; } function commerce_pricing_attributes_default_setting_access($access, $entity, $field_instance){ if(user_access($access)){ $field_info = field_info_field($field_instance['field_name']); if($field_info['module'] == 'list'){ $options = list_allowed_values($field_info, $field_instance); return (!empty($options)); } } return FALSE; }