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); } } } }Sealcoating Heroes | Asphalt Sealcoating Direct

You are here

Sealcoating Heroes

City: 
Schnecksville
Walnutport
Cherryville
Slatington
Whitehall
Orefield
Allentown
Bethlehem
Lehigh Valley
Coplay
State: 
Pennsylvania
Contact Name: 
Kyle Bushner
Business Phone: 
(610) 751-2519
Business Email: 

Description:

Lehigh Valley's elite sealcoating & asphalt service. We are a family owned small business - fully licensed, insured, and accredited with the BBB. We professionally perform all residential & commercial asphalt services such as sealcoating, hot crack sealing, asphalt repairs, line striping, and asphalt paving. Proudly serving blacktop driveways & parking lots in Lehigh, Northampton, Bucks, Berks, Carbon, Montgomery, and Schuykill County Pennsylvania. Our sealcoating treatment is brush applied to ensure your asphalt receives maximum coverage & the longest lasting protection. We combine our methods with Sealmaster's highest grade of commercial sealer. Contact us for a free quote so we can save & seal your driveway or parking lot the right way... before it's too late!