le * Show/hide attributes based on the values of other attributes. * * Some attributes may not be applicable depending upon the value of another * attribute. It may be desireable to hide the attribute unless an appropriate * value is selected for the other attribute to avoid confusing users. This * module has an administrative interface for specifying the dependencies * and Javascript code for hiding and showing the attributes. */ /** * Implements hook_menu(). */ function uc_dropdown_attributes_menu() { $items = array(); $items['node/%node/edit/dependencies'] = array( 'title' => 'Dependencies', 'description' => 'Product attribute dependency administration.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_dropdown_attributes_product', 1), 'access callback' => 'uc_attribute_product_access', 'access arguments' => array(1), 'theme callback' => 'uc_dropdown_attributes_admin_theme', 'type' => MENU_LOCAL_TASK, 'weight' => 1, 'file' => 'dependent_dropdown.inc', ); $items['node/%/dependencies/%/dependency/%'] = array( 'type' => MENU_CALLBACK, 'page callback' => 'uc_dropdown_attributes_dependency', 'page arguments' => array(1, 3, 5), 'access callback' => TRUE, ); $items['admin/store/products/classes/%/dependencies'] = array( 'title' => 'Dependencies', 'description' => 'Product class attribute dependency administration.', 'type' => MENU_LOCAL_TASK, 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_dropdown_attributes_class', 4), 'access arguments' => array('administer product classes'), 'file' => 'dependent_dropdown.inc', ); return $items; } /** * Implements hook_form_alter(). */ function uc_dropdown_attributes_form_alter(array &$form, array &$form_state, string $form_id): void { if (preg_match('/^uc_product_kit_add_to_cart_form.*/', $form_id)) { foreach ($form['products'] as $key => $value) { if (is_numeric($key)) { $type = uc_dropdown_attributes_dependency_type((int) $key); if (!is_null($type)) { $values = $form_state['values'] ?? []; uc_dropdown_attributes_product_alter((int) $key, $form['products'][$key], $values, $type, $form_state['rebuild']); if (!isset($form['#after_build']) || !in_array('_uc_dropdown_attributes_kit_build', $form['#after_build'])) { $form['#after_build'][] = '_uc_dropdown_attributes_kit_build'; } } } } } if (preg_match('/^uc_product_add_to_cart_form.*/', $form_id)) { $nid = $form['nid']['#value']; $type = uc_dropdown_attributes_dependency_type((int) $nid); if (!is_null($type)) { $values = $form_state['values'] ?? []; uc_dropdown_attributes_product_alter((int) $nid, $form, $values, $type, $form_state['rebuild']); $form['node_id'] = array( '#type' => 'hidden', '#value' => $nid, ); switch ($type) { case 'node': $form['#after_build'][] = '_uc_dropdown_attributes_product_build'; break; case 'class': $form['#after_build'][] = '_uc_dropdown_attributes_class_build'; break; } } } } /** * Alter products in preparation for drop down attributes. * * Adds the 'Please select' and removes the default value. Ubercart does this * for required attributes but since these attributes can no longer be required * if the attributes are dependent then this reproduces the same thing. * * @param int $nid * Node ID. * @param array $form * Product form. * @param array $form_values * Values from $form_state. * @param string $type * 'node' for dependencies defined on the node level; * 'class' for dependencies defined on the product class. * @param bool $rebuild * Whether the form is being rebuilt. */ function uc_dropdown_attributes_product_alter(int $nid, array &$form, array $form_values, string $type, bool $rebuild): void { switch ($type) { case 'node': $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_attributes} WHERE nid=:nid'; $attributes = db_query($sql, array(':nid' => $nid)); break; case 'class': $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $pcid = uc_dropdown_attributes_get_type($nid); $attributes = db_query($sql, array(':pcid' => $pcid)); break; default: $attributes = new stdClass(); break; } $parent_aids = array(); if (!$rebuild) { $data = $form['node']['#value']->data; } $load = FALSE; foreach ($attributes as $attribute) { $parent_aids[$attribute->parent_aid] = $attribute->parent_aid; if (isset($form['attributes'][$attribute->aid]['#options']) && count($form['attributes'][$attribute->aid]['#options']) && $attribute->required) { switch ($form['attributes'][$attribute->aid]['#type']) { case 'select': $form['attributes'][$attribute->aid]['#options'] = array('' => t('Please select')) + $form['attributes'][$attribute->aid]['#options']; $form['attributes'][$attribute->aid]['#default_value'] = ''; if (!$rebuild && isset($data['attributes'][$attribute->aid])) { $data['attributes'][$attribute->aid] = ''; $load = TRUE; } break; case 'radios': $form['attributes'][$attribute->aid]['#default_value'] = NULL; if (!$rebuild && isset($data['attributes'][$attribute->aid])) { $data['attributes'][$attribute->aid] = ''; $load = TRUE; } // Validation does not work if required set later. if (isset($form_values['attributes'][$attribute->parent_aid])) { $parent_value = $form_values['attributes'][$attribute->parent_aid]; $values = unserialize($attribute->parent_values); $parent_aid = $attribute->parent_aid; if (is_array($values) && in_array($parent_value, $values)) { $form['attributes'][$attribute->aid]['#required'] = TRUE; } } break; case 'checkboxes': $form['attributes'][$attribute->aid]['#default_value'] = array(); if (!$rebuild && isset($data['attributes'][$attribute->aid])) { $data['attributes'][$attribute->aid] = array(); $load = TRUE; } break; } } } if ($load) { $form['node']['#value'] = uc_product_load_variant($nid, $data); } $update_node = variable_get('uc_product_update_node_view', 0); if (!$update_node) { // If Ubercart update is not enabled then ajax needs to be attached to // parent attributes. foreach ($parent_aids as $aid) { $form['attributes'][$aid]['#ajax'] = array( 'callback' => 'uc_dropdown_attributes_ajax_callback', 'wrapper' => $form['attributes']['#id'], ); } } } /** * Ajax callback for attribute selection form elements. */ function uc_dropdown_attributes_ajax_callback(array $form, array $form_state) { if (count($form_state['triggering_element']['#parents']) == 4) { // This is a product kit. $key = $form_state['triggering_element']['#parents'][1]; return $form['products'][$key]['attributes']; } return $form['attributes']; } /** * Form build for products. * * Callback for $form['#after_build'] for products. Adds the CSS to hide * the dependent attributes. */ function _uc_dropdown_attributes_product_build(array $form, array &$form_state) { $nid = $form['nid']['#value']; $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_attributes} WHERE nid=:nid'; $attributes = db_query($sql, array(':nid' => $nid)); if (isset($form_state['triggering_element'])) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents) - 1]; $parent_value = $form_state['triggering_element']['#value']; uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, (int) $nid, 'node', $form_state['values']); } foreach ($attributes as $attribute) { if (!isset($form_state['values']['attributes'][$attribute->parent_aid])) { continue; } $parent_value = $form_state['values']['attributes'][$attribute->parent_aid]; if ($form['attributes'][$attribute->parent_aid]['#type'] == 'checkboxes') { $parent_values = array_diff((array) $parent_value, array(0)); $values = unserialize($attribute->parent_values); if (is_array($values) && count(array_intersect($parent_values, $values))) { // Show dependent attribute. if ($attribute->required) { $form['attributes'][$attribute->aid]['#required'] = TRUE; } } else { // Hide dependent attribute. $form['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values, regardless of 'required' status. uc_dropdown_attributes_clear_input($form, $form_state, (int) $attribute->aid); } } else { if ($parent_value) { // A value has been entered in parent attribute. $values = unserialize($attribute->parent_values); if (is_array($values) && array_key_exists($parent_value, $values)) { // Show dependent attribute. if ($attribute->required) { $form['attributes'][$attribute->aid]['#required'] = TRUE; } } else { // Hide dependent attribute. $form['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values, regardless of 'required' status. uc_dropdown_attributes_clear_input($form, $form_state, (int) $attribute->aid); } } else { $form['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values, regardless of 'required' status. uc_dropdown_attributes_clear_input($form, $form_state, (int) $attribute->aid); } } } return $form; } /** * Clear inputs for attribute. * * @param array $form * Form array. * @param array $form_state * Form state array. * @param int $aid * Attribute ID. */ function uc_dropdown_attributes_clear_input(array &$form, array &$form_state, int $aid): void { // Use nullsafe operator and null coalescing for PHP 8.0 compatibility $attributes_input = $form_state['input']['attributes'] ?? null; if ($attributes_input === null || !isset($form['attributes'][$aid])) { return; } $type = $form['attributes'][$aid]['#type'] ?? null; if ($type === null) { return; } switch ($type) { case 'checkboxes': if (isset($attributes_input[$aid])) { foreach ($attributes_input[$aid] as $oid => $value) { $form_state['input']['attributes'][$aid][$oid] = NULL; } } break; case 'radios': case 'select': $form_state['input']['attributes'][$aid] = ''; break; case 'textfield': $form_state['input']['attributes'][$aid] = ''; break; default: break; } } /** * Form build for classes. * * Callback for $form['#after_build'] for product classes. Adds * the CSS to hide the dependent attributes. */ function _uc_dropdown_attributes_class_build(array $form, array &$form_state) { $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $pcid = uc_dropdown_attributes_get_type($form['nid']['#value']); $attributes = db_query($sql, array(':pcid' => $pcid)); if (isset($form_state['triggering_element'])) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents) - 1]; $parent_value = $form_state['triggering_element']['#value']; uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, $pcid, 'class', $form_state['values']); } $parent_value = null; // Initialize parent_value before the loop foreach ($attributes as $attribute) { if (isset($form_state['values']['attributes'])) { $parent_value = $form_state['values']['attributes'][$attribute->parent_aid]; } if ($parent_value) { $values = unserialize($attribute->parent_values); if ($form['attributes'][$attribute->parent_aid]['#type'] == 'checkboxes') { $parent_values = array_diff((array) $parent_value, array(0)); if (is_array($values) && count(array_intersect($parent_values, $values))) { if ($attribute->required) { $form['attributes'][$attribute->aid]['#required'] = TRUE; } } else { $form['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if (count($form['attributes'][$attribute->aid]['#value']) > 0) { $form['attributes'][$attribute->aid]['#value'] = array(); } } } else { if (is_array($values) && array_key_exists($parent_value, $values)) { if ($attribute->required) { $form['attributes'][$attribute->aid]['#required'] = TRUE; } } else { $form['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if ($form['attributes'][$attribute->aid]['#value'] != '') { $form['attributes'][$attribute->aid]['#value'] = ''; } } } } else { $form['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if (isset($form['attributes'][$attribute->aid]['#value']) && $form['attributes'][$attribute->aid]['#value'] != '') { $form['attributes'][$attribute->aid]['#value'] = ''; } } } return $form; } /** * Form build for product kits. * * Callback for $form['#after_build'] for product kits. Renders the dependent * attributes and stores the html as a Javascript array. */ function _uc_dropdown_attributes_kit_build(array $form, array &$form_state) { foreach ($form['products'] as $key => $value) { if (is_numeric($key)) { $type = uc_dropdown_attributes_dependency_type((int) $key); switch ($type) { case 'node': $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_attributes} WHERE nid=:nid'; $id = $key; $attributes = db_query($sql, array(':nid' => $key)); break; case 'class': $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $pcid = uc_dropdown_attributes_get_type((int) $key); $id = $pcid; $attributes = db_query($sql, array(':pcid' => $pcid)); break; default: $attributes = array(); break; } if (isset($form_state['triggering_element'])) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents) - 1]; $parent_value = $form_state['triggering_element']['#value']; uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, $id, $type, $form_state['values']['products'][$key]); } foreach ($attributes as $attribute) { $aid = $attribute->aid; $parent_aid = $attribute->parent_aid; $parent_value = $form_state['values']['products'][$key]['attributes'][$parent_aid]; if ($parent_value) { $values = unserialize($attribute->parent_values); if (is_array($values) && array_key_exists($parent_value, $values)) { if ($attribute->required) { $form['products'][$key]['attributes'][$aid]['#required'] = TRUE; } } else { $form['products'][$key]['attributes'][$aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if ($form['products'][$key]['attributes'][$aid]['#value'] != '') { $form['products'][$key]['attributes'][$aid]['#value'] = ''; } } } else { $form['products'][$key]['attributes'][$aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if ($form['products'][$key]['attributes'][$aid]['#value'] != '') { $form['products'][$key]['attributes'][$aid]['#value'] = ''; } } } } } return $form; } /** * Unset values of orphaned children. * * Called recursively to remove orphaned values from form_state. * * @param int $parent_aid * Attribute ID of the triggering element. * @param mixed $parent_value * Option ID of the value of the triggering element. * @param mixed $id * Node ID or product class ID. * @param string $type * 'node' or 'class'. * @param array $form_values * Part of the form_state array containing the attributes. */ function uc_dropdown_attributes_remove_values(int $parent_aid, $parent_value, $id, string $type, array &$form_values): void { switch ($type) { case 'node': $sql = 'SELECT aid, required, parent_values FROM {uc_dropdown_attributes} WHERE nid=:nid AND parent_aid=:parent_aid'; $children = db_query($sql, array( ':nid' => $id, ':parent_aid' => $parent_aid) ); break; case 'class': $sql = 'SELECT aid, required, parent_values FROM {uc_dropdown_classes} WHERE pcid=:pcid AND parent_aid=:parent_aid'; $children = db_query($sql, array( ':pcid' => $id, ':parent_aid' => $parent_aid) ); break; default: $children = array(); break; } foreach ($children as $child) { if ($child->required) { if (isset($form_values['attributes'][$child->aid])) { $value = $form_values['attributes'][$child->aid]; $form_values['attributes'][$child->aid] = ''; if ($value) { $values = unserialize($child->parent_values); if (is_array($values) && !in_array($parent_value, $values)) { uc_dropdown_attributes_remove_values((int) $child->aid, $value, $id, $type, $form_values); } } } } } } /** * Add the style to hide the attribute. * * @param string $html * HTML for the element. * * @return string * Modified element HTML. */ function uc_dropdown_attributes_post_render(string $html): string { $pos = strpos($html, '>'); if ($pos !== false) { $html = substr_replace($html, ' style="display:none;">', $pos, 1); } return $html; } /** * Retrieves the attribute dependencies. * * Callback to supply attribute dependencies to Javascript. * * @param int $nid * Node ID. * @param string $id * String containing attribute ID. * * @return object * JSON structure. */ function uc_dropdown_attributes_dependency(int $nid, string $id, string $parent_id) { $temp = explode('-', $id); if ($temp[1] == 'attributes') { $aid = $temp[2]; } else { $aid = $temp[4]; } $result = new stdClass(); $query = 'SELECT parent_values, required FROM {uc_dropdown_attributes} WHERE nid=:nid && aid=:aid'; $db_result = db_query($query, array(':nid' => $nid, ':aid' => $aid)); foreach ($db_result as $item) { $result->status = TRUE; $result->nid = $nid; $result->id = $id; $result->parent_id = $parent_id; $result->parent_values = unserialize($item->parent_values); $result->required = $item->required; drupal_json_output($result); return; } $pcid = uc_dropdown_attributes_get_type($nid); $query = 'SELECT parent_values, required FROM {uc_dropdown_classes} WHERE pcid=:pcid && aid=:aid'; $db_result = db_query($query, array(':pcid' => $pcid, ':aid' => $aid)); foreach ($db_result as $item) { $result->status = TRUE; $result->nid = $nid; $result->id = $id; $result->parent_id = $parent_id; $result->parent_values = unserialize($item->parent_values); $result->required = $item->required; drupal_json_output($result); return; } $result->status = FALSE; drupal_json_output($result); } /** * Create an attribute dependency. * * A public function that creates and stores an attribute dependency for a * product. * * @param int $nid * Node ID. * @param int $aid * Attribute ID of the dependent (child) attribute. * @param int $parent_aid * Attribute ID of the parent attribute. * @param array $options * Array of the Option IDs that trigger the dependent attribute. * @param bool $required * TRUE if the dependent (child) attribute is required when it appears and * FALSE if it is not required. */ function uc_dropdown_attributes_product_create_dependency(int $nid, int $aid, int $parent_aid, array $options, bool $required): void { $attribute = uc_attribute_load($aid); $dep = db_insert('uc_dropdown_attributes') ->fields(array( 'nid' => $nid, 'aid' => $aid, 'parent_aid' => $parent_aid, 'parent_values' => serialize($options), 'required' => $required, )) ->execute(); // Need to check to make sure attribute is not required all the time. $sql = 'SELECT nid, aid, required FROM {uc_product_attributes} WHERE nid=:nid && aid=:aid'; $result = db_query($sql, array(':nid' => $nid, ':aid' => $aid)); foreach ($result as $item) { if ($item->required == 1) { $dep = db_update('uc_product_attributes') ->fields(array( 'required' => 0, )) ->condition('nid', $item->nid) ->condition('aid', $item->aid) ->execute(); } } } /** * Create an attribute dependency for product classes. * * A public function that creates and stores an attribute dependency for * product classes. * * @param int $pcid * Product class ID. * @param int $aid * Attribute ID of the dependent (child) attribute. * @param int $parent_aid * Attribute ID of the parent attribute. * @param array $options * Array of the Option IDs that trigger the dependent attribute. * @param bool $required * TRUE if the dependent (child) attribute is required when it appears and * FALSE if it is not required. */ function uc_dropdown_attributes_class_create_dependency(int $pcid, int $aid, int $parent_aid, array $options, bool $required): void { $attribute = uc_attribute_load($aid); $dep = db_insert('uc_dropdown_classes') ->fields(array( 'pcid' => $pcid, 'aid' => $aid, 'parent_aid' => $parent_aid, 'parent_values' => serialize($options), 'required' => $required, )) ->execute(); // Need to check to make sure attribute is not required all the time. $sql = 'SELECT pcid, aid, required FROM {uc_class_attributes} WHERE pcid=:pcid && aid=:aid'; $result = db_query($sql, array(':pcid' => $pcid, ':aid' => $aid)); foreach ($result as $item) { if ($item->required == 1) { $dep = db_update('uc_class_attributes') ->fields(array( 'required' => 0, )) ->condition('pcid', $item->pcid) ->condition('aid', $item->aid) ->execute(); } } } /** * Implements hook_theme(). */ function uc_dropdown_attributes_theme(): array { return array( 'uc_dropdown_attributes_product' => array( 'render element' => 'form', ), 'uc_dropdown_attributes_class' => array( 'render element' => 'form', ), ); } /** * Retrieve product class from the node ID. * * @param int $nid * Node id. * * @return string * The type field from the node object. */ function uc_dropdown_attributes_get_type(int $nid): string { $sql = 'SELECT type FROM {node} WHERE nid=:nid'; $type = db_query($sql, array(':nid' => $nid))->fetchField(); return $type; } /** * Retrieve whether dependencies are defined by node or class. * * @param int $nid * Node id. * * @return string|null * 'node' for dependencies defined on the node level; * 'class' for dependencies defined on the product class; otherwise, NULL. */ function uc_dropdown_attributes_dependency_type(int $nid): ?string { $sql = 'SELECT COUNT(*) FROM {uc_dropdown_attributes} WHERE nid=:nid'; $count = db_query($sql, array(':nid' => $nid))->fetchField(); if ($count > 0) { return 'node'; } $pcid = uc_dropdown_attributes_get_type($nid); $sql = 'SELECT COUNT(*) FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $count = db_query($sql, array(':pcid' => $pcid))->fetchField(); if ($count > 0) { return 'class'; } return NULL; } /** * Determine the correct theme to use for dependencies admin page. * * @return string * theme to use or empty string if default theme. */ function uc_dropdown_attributes_admin_theme(): string { if (variable_get('node_admin_theme', 0) == 1) { return (string)variable_get('admin_theme', ''); } return ''; } /** * Implements hook_form_FORM_ID_alter() for uc_order_edit_form(). */ function uc_dropdown_attributes_form_uc_order_edit_form_alter(array &$form, array &$form_state): void { if (isset($form_state['products_action']) && $form_state['products_action'] == 'add_product') { $product_type = $form['product_controls']['node']['#value']->type; if ($product_type == 'product_kit') { foreach ($form['product_controls']['sub_products'] as $nid => $product) { if (is_numeric($nid)) { $type = uc_dropdown_attributes_dependency_type((int) $nid); if (!is_null($type)) { uc_dropdown_attributes_order_product_alter((int) $nid, $form['product_controls']['sub_products'][$nid]['attributes'], $type); if (!in_array('_uc_dropdown_attributes_order_product_kit_build', $form['#after_build'])) { $form['#after_build'][] = '_uc_dropdown_attributes_order_product_kit_build'; } } } } } else{ $nid = $form['product_controls']['node']['#value']->nid; $type = uc_dropdown_attributes_dependency_type((int) $nid); if (!is_null($type)) { uc_dropdown_attributes_order_product_alter((int) $nid, $form['product_controls']['attributes'], $type); $form['nid'] = array( '#type' => 'hidden', '#value' => $nid, ); switch ($type) { case 'node': $form['#after_build'][] = '_uc_dropdown_attributes_order_product_build'; break; case 'class': $form['#after_build'][] = '_uc_dropdown_attributes_order_class_build'; break; } } } } } /** * Alter products on oder page in preparation for drop down attributes. * * Adds the 'Please select' and removes the default value. Ubercart does this * for required attributes but since these attributes can no longer be required * if the attributes are dependent then this reproduces the same thing. * * @param int $nid * Node ID. * @param array $form_attributes * Attributes part of the product form. * @param string $type * 'node' for dependencies defined on the node level; * 'class' for dependencies defined on the product class. */ function uc_dropdown_attributes_order_product_alter(int $nid, array &$form_attributes, string $type): void { switch ($type) { case 'node': $sql = 'SELECT aid, parent_aid, required FROM {uc_dropdown_attributes} WHERE nid=:nid'; $attributes = db_query($sql, array(':nid' => $nid)); break; case 'class': $sql = 'SELECT aid, parent_aid, required FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $pcid = uc_dropdown_attributes_get_type($nid); $attributes = db_query($sql, array(':pcid' => $pcid)); break; } $parent_aids = array(); foreach ($attributes as $attribute) { $parent_aids[$attribute->parent_aid] = $attribute->parent_aid; if (isset($form_attributes[$attribute->aid]['#options']) && count($form_attributes[$attribute->aid]['#options']) && $attribute->required) { switch ($form_attributes[$attribute->aid]['#type']) { case 'select': $form_attributes[$attribute->aid]['#options'] = array('' => t('Please select')) + $form_attributes[$attribute->aid]['#options']; $form_attributes[$attribute->aid]['#default_value'] = ''; break; case 'radios': $form_attributes[$attribute->aid]['#default_value'] = ''; break; case 'checkboxes': $form_attributes[$attribute->aid]['#default_value'] = array(); break; } } } foreach ($parent_aids as $aid) { $form_attributes[$aid]['#ajax'] = array( 'callback' => 'uc_dropdown_attributes_order_ajax_callback', 'wrapper' => $form_attributes['#id'], ); } } /** * Ajax callback for order attribute selection form elements. */ function uc_dropdown_attributes_order_ajax_callback(array $form, array $form_state) { if (in_array('sub_products', $form_state['triggering_element']['#parents'])) { // This is a product kit. $nid = $form_state['triggering_element']['#parents'][2]; return $form['product_controls']['sub_products'][$nid]['attributes']; } return $form['product_controls']['attributes']; } /** * Form build for products for the order page. * * Callback for $form['#after_build'] for products. Adds the CSS to hide * the dependent attributes. */ function _uc_dropdown_attributes_order_product_build(array $form, array &$form_state) { $nid = $form['nid']['#value']; $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_attributes} WHERE nid=:nid'; $attributes = db_query($sql, array(':nid' => $nid)); if (isset($form_state['triggering_element'])) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents)-1]; $parent_value = $form_state['triggering_element']['#value']; uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, (int) $nid, 'node', $form_state['values']['product_controls']); } uc_dropdown_attributes_order_attribute_display( $attributes, $form['product_controls']['attributes'], $form_state['values']['product_controls']['attributes'] ); return $form; } /** * Form build for classes for the order page. * * Callback for $form['#after_build'] for product classes. * Adds the CSS to hide the dependent attributes. */ function _uc_dropdown_attributes_order_class_build(array $form, array &$form_state) { $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $pcid = uc_dropdown_attributes_get_type($form['nid']['#value']); $attributes = db_query($sql, array(':pcid' => $pcid)); if (isset($form_state['triggering_element'])) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents)-1]; $parent_value = $form_state['triggering_element']['#value']; // Note that values are stored in input, not values. uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, $pcid, 'class', $form_state['input']['product_controls']); } uc_dropdown_attributes_order_attribute_display( $attributes, $form['product_controls']['attributes'], $form_state['values']['product_controls']['attributes'] ); return $form; } /** * Form build for product kits for the order page. * * Callback for $form['#after_build'] for products. Adds the CSS to hide * the dependent attributes. */ function _uc_dropdown_attributes_order_product_kit_build(array $form, array &$form_state) { foreach ($form['product_controls']['sub_products'] as $nid => $product) { if (is_numeric($nid)) { $type = uc_dropdown_attributes_dependency_type((int) $nid); switch ($type) { case 'node': $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_attributes} WHERE nid=:nid'; $attributes = db_query($sql, array(':nid' => $nid)); if (isset($form_state['triggering_element']) && $nid == $form_state['triggering_element']['#parents'][2]) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents)-1]; $parent_value = $form_state['triggering_element']['#value']; uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, (int) $nid, 'node', $form_state['values']['product_controls']['sub_products'][$nid]); } uc_dropdown_attributes_order_attribute_display( $attributes, $form['product_controls']['sub_products'][$nid]['attributes'], $form_state['values']['product_controls']['sub_products'][$nid]['attributes'] ); break; case 'class': $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $pcid = uc_dropdown_attributes_get_type((int) $nid); $attributes = db_query($sql, array(':pcid' => $pcid)); if (isset($form_state['triggering_element']) && $nid == $form_state['triggering_element']['#parents'][2]) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents)-1]; $parent_value = $form_state['triggering_element']['#value']; // Note that values are stored in input, not values. uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, $pcid, 'class', $form_state['input']['product_controls']['sub_products'][$nid]); } uc_dropdown_attributes_order_attribute_display( $attributes, $form['product_controls']['sub_products'][$nid]['attributes'], $form_state['values']['product_controls']['sub_products'][$nid]['attributes'] ); break; } } } return $form; } /** * Alter display of attributes on the oder page. * * @param array $attributes * Array of attribute dependency objects. * @param array &$form_attributes * Attributes part of the product form. * @param array $form_state_attributes * Attributes part of the product form_state. */ function uc_dropdown_attributes_order_attribute_display(array $attributes, array &$form_attributes, array $form_state_attributes): void { foreach ($attributes as $attribute) { if (!isset($form_state_attributes[$attribute->parent_aid])) { continue; } $parent_value = $form_state_attributes[$attribute->parent_aid]; if ($parent_value) { // A value has been entered in parent attribute. $values = unserialize($attribute->parent_values); if (is_array($values) && array_key_exists($parent_value, $values)) { // Show dependent attribute. if ($attribute->required) { $form_attributes[$attribute->aid]['#required'] = TRUE; } } else { // Hide dependent attribute. $form_attributes[$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if (isset($form_attributes[$attribute->aid]['#value']) && $form_attributes[$attribute->aid]['#value'] != '') { $form_attributes[$attribute->aid]['#value'] = ''; } } } else { $form_attributes[$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if (isset($form_attributes[$attribute->aid]['#value']) && $form_attributes[$attribute->aid]['#value'] != '') { $form_attributes[$attribute->aid]['#value'] = ''; } } } } /** * Form build for classes in product kits for the order page. * * Callback for $form['#after_build'] for product classes. Adds * the CSS to hide the dependent attributes. */ function _uc_dropdown_attributes_order_class_kit_build(array $form, array &$form_state) { $sql = 'SELECT aid, parent_aid, parent_values, required FROM {uc_dropdown_classes} WHERE pcid=:pcid'; $pcid = uc_dropdown_attributes_get_type($form['nid']['#value']); $attributes = db_query($sql, array(':pcid' => $pcid)); if (isset($form_state['triggering_element'])) { $parents = $form_state['triggering_element']['#parents']; $parent_aid = $parents[count($parents)-1]; $parent_value = $form_state['triggering_element']['#value']; uc_dropdown_attributes_remove_values((int) $parent_aid, $parent_value, $pcid, 'class', $form_state['values']['product_controls']); } $parent_value = null; foreach ($attributes as $attribute) { if (isset($form_state['values']['product_controls']['attributes'][$attribute->parent_aid])) { $parent_value = $form_state['values']['product_controls']['attributes'][$attribute->parent_aid]; } if ($parent_value) { $values = unserialize($attribute->parent_values); if (is_array($values) && array_key_exists($parent_value, $values)) { if ($attribute->required) { $form['product_controls']['attributes'][$attribute->aid]['#required'] = TRUE; } } else { $form['product_controls']['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if (isset($form['product_controls']['attributes'][$attribute->aid]['#value']) && $form['product_controls']['attributes'][$attribute->aid]['#value'] != '') { $form['product_controls']['attributes'][$attribute->aid]['#value'] = ''; } } } else { $form['product_controls']['attributes'][$attribute->aid]['#post_render'][] = 'uc_dropdown_attributes_post_render'; // Bug fix: Always clear hidden attribute values. if (isset($form['product_controls']['attributes'][$attribute->aid]['#value']) && $form['product_controls']['attributes'][$attribute->aid]['#value'] != '') { $form['product_controls']['attributes'][$attribute->aid]['#value'] = ''; } } } return $form; } /** * Implements hook_node_load(). * * Ubercart fails to load attributes for product classes. */ function uc_dropdown_attributes_node_load(array $nodes): void { foreach ($nodes as $node) { if (uc_product_is_product($node)) { if (empty($node->attributes)) { $node->attributes = uc_class_get_attributes($node->type); } } } }CARO-LINE Parking Lot Striping | Charlotte NC | Asphalt Sealcoating Direct

You are here

CARO-LINE Parking Lot Striping | Charlotte NC

City: 
Charlotte
Concord
Rock Hill
Columbia
Durham
Mooresville
Cornelius NC
Huntersville NC
Matthews NC
Pineville NC
State: 
North Carolina
Contact Name: 
Phil
Business Phone: 
(704) 850-5850
Business Email: 

Description:

Large Commercial Parking lot services:
Parking lot Striping, Line striping, Line painting, Warehouse floor striping, warehouse line striping, epoxy line striping, parking lot sealing, asphalt sealcoating, pipe bollard repair, pipe bollard installation

Parking Lot Striping Charlotte NC serves the following communities in NC:
Charlotte, Raleigh, Greensboro, Winston-Salem, Durham, Fayetteville, Cary, Wilmington, High Point, Asheville, Concord, Gastonia, Rocky Mount, Chapel Hill, Burlington, Wilson, Huntersville, Kannapolis, Hickory, Apex, Goldsboro, Salisbury, Indian Trail, Monroe, Mooresville, Wake Forest, New Bern, Sanford, Matthews, Thomasville, Garner, Asheboro, Cornelius, Holly Springs, Statesville, Kernersville, Mint Hill, Kinston, Lumberton, Shelby, Carrboro, Lexington, Elizabeth City, Clemmons, Lenoir, Fuquay-Varina, Boone, Morganton, Clayton, Laurinburg, Roanoke Rapids, Eden, Henderson, Hope Mills, Reidsville, Graham, Stallings, Mount Holly, Leland, Hendersonville, Pinehurst, Newton, Lewisville, Southern Pines, Spring Lake, Harrisburg, Archdale, Mebane, Davidson, Lincolnton, Mount Airy, Kings Mountain, Summerfield, Belmont, Waxhaw, Weddington, Ballantyne