'drupal_get_form', 'page arguments' => array('uc_order_attributes_form', 3, 4), 'access arguments' => array('edit orders'), 'type' => MENU_CALLBACK, ); // Attribute selection AHAH $items['admin/store/uc_order_attributes/ahah/%'] = array( 'page callback' => 'uc_order_attributes_ahah_callback', 'page arguments' => array(4), 'access arguments' => array('edit orders'), 'type' => MENU_CALLBACK, ); // Attribute options autocomplete $items['admin/store/uc_order_attributes/options/%'] = array( 'page callback' => 'uc_order_attributes_options_autocomplete', 'page arguments' => array(4), 'access arguments' => array('edit orders'), 'type' => MENU_CALLBACK, ); return $items; } /** * Implementation of hook_form_alter(). */ function uc_order_attributes_form_alter(&$form, $form_state, $form_id) { // Add Javascript to the order edit page (via the form). if ($form_id == 'uc_order_edit_form') { // Set up this page as a modalframe parent modalframe_parent_js(); // Add our JavaScript to the order edit form drupal_add_js(drupal_get_path('module', 'uc_order_attributes') . '/uc_order_attributes.js'); } // Alter the list of products in an order to add the "Attributes" column. else if ($form_id == 'uc_order_edit_products_form') { // Add a column to the products table for attributes. $form['products']['#columns']['attributes'] = array('cell' => t('Attributes'), 'weight' => 4); // If there are products, loop through them. if (!empty($form['products']) && $form['products'][0]['remove']['#value'] != 'This order contains no products.') { foreach ($form['products'] as $key => $product) { // If the $key is an integer (not a form property or other element). if (is_int($key)) { // Start a div to put the output in $output = '
'; // Unserialize the product data to get at the attributes. $data = unserialize($product['data']['#value']); // Loop through the attributes, if there are any. if (!empty($data['attributes'])) { foreach ($data['attributes'] as $attribute => $option) { // Add the attribute and it's option as a line in the output. $output .= $attribute . ': ' . reset($option) . "
"; } } // Close #attributes-[order_product_id] the div. $output .= '
'; // Add a link to the attribute editor (as a modalframe). $output .= l('Edit attributes', 'admin/store/uc_order_attributes/' . $product['order_product_id']['#value'] . '/' . $key, array('attributes' => array('onclick' => 'return Drupal.uc_order_attributes.modal(' . $product['order_product_id']['#value'] . ', ' . $key . ');'))); // Stick the output into the column. $form['products'][$key]['attributes'] = array ( '#value' => $output, '#name' => 'products[' . $key . '][attributes]', ); } } } } } /** * Implementation of hook_theme(). */ function uc_order_attributes_theme() { return array( // The product attributes form 'uc_order_attributes_form' => array( 'arguments' => array('form' => array()), ), ); } /************************************************************************* * Menu callbacks *************************************************************************/ /* * Menu callback for AHAH functionality. */ function uc_order_attributes_ahah_callback($element) { // Standard AHAH callback behavior. Taken from ahah_example.module in the examples project: drupal.org/project/examples $form_state = array('storage' => NULL, 'submitted' => FALSE); $form_build_id = $_POST['form_build_id']; $form = form_get_cache($form_build_id, $form_state); $args = $form['#parameters']; $form_id = array_shift($args); $form_state['post'] = $form['#post'] = $_POST; // Enable the submit/validate handlers to determine whether AHAH-submittted. $form_state['ahah_submission'] = TRUE; $form['#programmed'] = $form['#redirect'] = FALSE; drupal_process_form($form_id, $form, $form_state); $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id); // When the user selects an attribute from the dropdown list, this will regenerate that part of the form with an autocomplete box for selecting the option. $form_element = $form[$element]; $output = theme('status_messages'); $output .= drupal_render($form_element); // Final rendering callback. drupal_json(array('status' => TRUE, 'data' => $output)); exit(); } /* * Menu callback for attribute options autocomplete */ function uc_order_attributes_options_autocomplete($aid, $string='') { $matches = array(); if ($aid && $string) { $result = db_query('SELECT name FROM {uc_attribute_options} WHERE aid = %d AND name LIKE "%%%s%%"', $aid, $string); while ($row = db_fetch_array($result)) { if ($row) { $matches[$row['name']] = $row['name']; } } } drupal_json($matches); } /************************************************************************* * Form callbacks *************************************************************************/ /* * Form callback for the product attributes edit form. */ function uc_order_attributes_form(&$form_state, $order_product_id, $product_key) { $form = array(); // Format the page as a modalframe child dialog. modalframe_child_js(); // Include the autocomplete JS. drupal_add_js('misc/autocomplete.js', 'core'); // Load the product record from the {uc_order_products} table. $order_product = db_fetch_object(db_query('SELECT order_id, data, title FROM {uc_order_products} WHERE order_product_id = %d', $order_product_id)); // Unserialize the product data (where the attributes are stored). $order_product->data = unserialize($order_product->data); // Store the Order ID for later use $order_id = $order_product->order_id; // Set the page title drupal_set_title('Edit attributes for ' . $order_product->title); // Tell the form to use the theme function. $form['#theme'] = 'uc_order_attributes_form'; // Process the form as a tree to preserve it's array structure. $form['#tree'] = TRUE; // Store the order_product_id and key in a hidden field. $form['order_product_id'] = array( '#type' => 'hidden', '#name' => 'order_product_id', '#value' => $order_product_id ); $form['product_key'] = array( '#type' => 'hidden', '#name' => 'key', '#value' => $product_key, ); $i = 0; if (!empty($order_product->data['attributes'])) { foreach ($order_product->data['attributes'] as $attribute => $option) { $form['attributes'][$i]['delete'] = array( '#type' => 'checkbox', ); $form['attributes'][$i]['attribute'] = array( '#type' => 'textfield', '#default_value' => $attribute, '#required' => TRUE, ); $form['attributes'][$i]['option'] = array( '#type' => 'textfield', '#default_value' => reset($option), '#required' => TRUE, ); $form['attributes'][$i]['weight'] = array( '#type' => 'weight', '#delta' => count($order_product->data['attributes']), '#default_value' => $i, '#attributes' => array( 'class' => 'weight' ), ); $i++; } } // Add some fields for adding a new attribute+option. $form['add'] = array( '#type' => 'fieldset', '#title' => t('Add Attribute'), '#prefix' => '
', '#suffix' => '
', ); // Query the database for attributes. $query = db_query('SELECT aid, name, label FROM {uc_attributes} WHERE display = 1 ORDER BY ordering ASC'); // Format the attributes as select list options. $options = array( 0 => 'Please select...', ); while ($row = db_fetch_array($query)) { if ($row) { $options[$row['aid']] = $row['name']; } } $form['add']['attribute'] = array( '#type' => 'select', '#options' => $options, '#default_value' => $form_state['values']['add']['attribute'], '#ahah' => array( 'path' => 'admin/store/uc_order_attributes/ahah/add', 'wrapper' => 'add', 'effect' => 'fade', ), ); // The option field is an autocomplete that looks for options in the attribute chosen in the attributes dropdown (above). // This is only visible when an attribute has been chosen above. if ($form_state['values']['add']['attribute']) { $form['add']['option'] = array( '#type' => 'textfield', '#default_value' => '', '#autocomplete_path' => 'admin/store/uc_order_attributes/options/' . $form_state['values']['add']['attribute'], ); $form['add']['submit'] = array( '#type' => 'submit', '#value' => t('Add'), '#submit' => array('uc_order_attributes_add'), ); } // Apply button $form['submit'] = array( '#type' => 'submit', '#value' => t('Apply'), ); // Cancel button $form['cancel'] = array( '#type' => 'submit', '#value' => t('Cancel'), '#submit' => array('uc_order_attributes_form_cancel'), ); return $form; } /* * Product form submit */ function uc_order_attributes_form_submit($form, &$form_state) { // Do not do anything if this is just an AHAH submission. if (!empty($form_state['ahah_submission'])) { return; } // Start an arguments array to pass back to the parent window. $args = array(); // Load the order product record from the database if (!empty($form_state['values']['order_product_id']) && !empty($form_state['values']['attributes'])) { // Load the order_product record from the database $order_product = db_fetch_object(db_query('SELECT * FROM {uc_order_products} WHERE order_product_id=%d', $form_state['values']['order_product_id'])); if (!empty($order_product->data)) { // Unserialize the data array. $order_product->data = unserialize($order_product->data); // Clear the attributes array because we're going to replace it. $order_product->data['attributes'] = array(); // Start an empty list of attributes that will replace the existing list in the order edit page. $attribute_list = array(); // Create an index of the attributes sorted by their weight. foreach ($form_state['values']['attributes'] as $attribute) { $attributes_by_weight[$attribute['weight']] = $attribute; } ksort($attributes_by_weight); // Sort the array by it's key, low-to-high. // Loop through the attributes in the form. foreach ($attributes_by_weight as $attribute) { // Only add this attribute if it's "Remove" checkbox is not checked. if (!$attribute['delete']) { // Format the attribute and add it to the list for the order edit page. $attribute_list[] = $attribute['attribute'] . ': ' . $attribute['option']; // Add the attribute to the order_product's data array/ $order_product->data['attributes'][$attribute['attribute']][0] = $attribute['option']; } } // Save the order_product record back to the database. drupal_write_record('uc_order_products', $order_product, array('order_product_id')); // Invoke a Rules event if Rules are present. if (module_exists('rules')) { rules_invoke_event('uc_order_attributes_update', $order_product, $attributes_by_weight); } } // Save the order_product_id in the arguments array. $args['order_product_id'] = $form_state['values']['order_product_id']; // Save the key in the arguments array $args['product_key'] = $form_state['values']['product_key']; // Implode the attributesarray and save it in the arguments array $args['attributes'] = implode('
', $attribute_list); // Serialize the new product data and save it in the arguments array $args['data_serialized'] = serialize($order_product->data); } // Close the modalframe dialog $form_state['rebuild'] = TRUE; // The form needs to be rebuilt for the modalframe_close_dialog to work. modalframe_close_dialog($args); } /** * Form submit callback for the cancel button. */ function uc_order_attributes_form_cancel($form, &$form_state) { // Start an arguments array to pass back to the parent window. $args = array(); // If a product was added in uc_order_attributes_add, make sure it ends up back in the order edit page. if (!empty($form_state['values']['order_product_id']) && !empty($form_state['storage']['add']['attributes'])) { // Loop through that attributes and prepare them to be passed back to the order edit page (the parent window if the form is opened in a modal). foreach ($form_state['storage']['add']['attributes'] as $attribute => $option) { // Format the attribute and add it to the list for the order edit page. $attribute_list[] = $attribute . ': ' . reset($option); } // Save the order_product_id in the arguments array. $args['order_product_id'] = $form_state['values']['order_product_id']; // Implode the array and save it in the arguments array $args['attributes'] = implode('
', $attribute_list); } // Close the modalframe dialog $form_state['rebuild'] = TRUE; // The form needs to be rebuilt for the modalframe_close_dialog to work. modalframe_close_dialog($args); } /** * Form submit callback for the attribute add button. */ function uc_order_attributes_add($form, &$form_state) { // If an order_product_id, a new attribute ID, and a new option name are set in the form... if (!empty($form_state['values']['order_product_id']) && !empty($form_state['values']['add']['attribute']) && !empty($form_state['values']['add']['option'])) { // Load the order_product record from the database $order_product = db_fetch_object(db_query('SELECT * FROM {uc_order_products} WHERE order_product_id=%d', $form_state['values']['order_product_id'])); // If there is data... if (!empty($order_product->data)) { // Unserialize the data array. $order_product->data = unserialize($order_product->data); // Load the attribute information $attribute = uc_attribute_load($form_state['values']['add']['attribute']); // Add the new attribute to the array. $order_product->data['attributes'][$attribute->name][0] = $form_state['values']['add']['option']; // Save the order_product record back to the database. drupal_write_record('uc_order_products', $order_product, array('order_product_id')); // Save the attributes to the $form_state to use in uc_order_attributes_form_cancel, just in case. $form_state['storage']['add']['attributes'] = $order_product->data['attributes']; } } } /************************************************************************* * Theme functions *************************************************************************/ /** * Theme function for the attribute/option form */ function theme_uc_order_attributes_form($form) { $output = ''; // Explain to the user that updating attributes with this form will not update the product pricing. $output .= '

PLEASE NOTE: Editing the attributes of this product will not automatically update the product\'s price, cost, or weight in the order. You must do this manually in the order editor or add the product to the order again from scratch.


'; $attributes = element_children($form['attributes']); if (!empty($attributes)) { // Give the existing attributes a title $output .= '

Existing Attributes

'; // Create a table for the existing attributes and options $table_header = array( 'attribute' => t('Attribute name'), 'option' => t('Option name'), 'delete' => t('Remove'), 'order' => t('Order'), ); // Output the list of attributes/options as table rows. $table_rows = array(); foreach ($attributes as $key) { $table_rows[] = array( 'data' => array( drupal_render($form['attributes'][$key]['attribute']), drupal_render($form['attributes'][$key]['option']), drupal_render($form['attributes'][$key]['delete']), drupal_render($form['attributes'][$key]['weight']), ), 'class' => 'draggable', ); } // Theme the table $output .= theme('table', $table_header, $table_rows, array('id' => 'attributes-table')); } // Render the rest of the form $output .= drupal_render($form); // Give the user the ability to rearrange the attributes. drupal_add_tabledrag('attributes-table', 'order', 'sibling', 'weight'); return $output; } /************************************************************************* + * Token-related functions + *************************************************************************/ /** * Implementation of hook_token_list(). */ function uc_order_attributes_token_list($type = 'all') { if ($type == 'uc_order_product') { $tokens['uc_order_attributes'] = array( 'uc_order_product_id' => t('Ubercart Order Product ID'), 'uc_order_product_order_id' => t('Order ID'), 'uc_order_product_nid' => t('Node ID of the Product'), 'uc_order_product_title' => t('Product Title'), 'uc_order_product_manufacturer' => t('Manufacturer'), 'uc_order_product_model' => t('Product Model'), 'uc_order_product_qty' => t('Product Quantity'), 'uc_order_product_cost' => t('Product Cost'), 'uc_order_product_price' => t('Product Price'), 'uc_order_product_weight' => t('Weight'), 'uc_order_product_data' => t('Additional Data'), ); } return $tokens; } /** * Implementation of hook_token_values(). */ function uc_order_attributes_token_values($type, $object = NULL, $options = array()) { if ($type == 'uc_order_product') { $tokens = array( 'uc_order_product_id' => $object->order_product_id, 'uc_order_product_order_id' => $object->order_id, 'uc_order_product_nid' => $object->nid, 'uc_order_product_title' => $object->title, 'uc_order_product_manufacturer' => $object->manufacturer, 'uc_order_product_model' => $object->model, 'uc_order_product_qty' => $object->qty, 'uc_order_product_cost' => $object->cost, 'uc_order_product_price' => $object->price, 'uc_order_product_weight' => $object->weight, 'uc_order_product_data' => $object->data, ); } return $tokens; }