=== modified file 'uc_attribute/uc_attribute.admin.inc'
--- uc_attribute/uc_attribute.admin.inc	2009-06-09 15:01:27 +0000
+++ uc_attribute/uc_attribute.admin.inc	2009-06-11 07:32:06 +0000
@@ -499,6 +499,10 @@ function uc_object_attributes_form($form
         );
 
         $form['attributes'][$attribute->aid] = array(
+          'aid' => array(
+            '#type' => 'hidden',
+            '#default_value' => $attribute->aid,
+          ),
           'remove' => array(
             '#type' => 'checkbox',
             '#default_value' => 0,
@@ -623,78 +627,58 @@ function theme_uc_object_attributes_form
   return $output;
 }
 
+/**
+ * Submit handler for the product/class attributes form.
+ */
 function uc_object_attributes_form_submit($form, &$form_state) {
-  if ($form_state['values']['type'] == 'product') {
-    $attr_table = '{uc_product_attributes}';
-    $opt_table = '{uc_product_options}';
-    $id = 'nid';
-    $sql_type = '%d';
-  }
-  elseif ($form_state['values']['type'] == 'class') {
-    $attr_table = '{uc_class_attributes}';
-    $opt_table = '{uc_class_attribute_options}';
-    $id = 'pcid';
-    $sql_type = "'%s'";
-  }
-
+  $type = $form_state['values']['type'];
+  $id = $form_state['values']['id'];
+  
+  // The attribute edit form.
   if ($form_state['values']['view'] == 'overview' && is_array($form_state['values']['attributes'])) {
+    
+    $remove_count = 0;
     foreach ($form_state['values']['attributes'] as $aid => $attribute) {
+      // Remove was checked.
       if ($attribute['remove']) {
-        $remove_aids[] = $aid;
+        uc_attribute_subject_delete($aid, $id, $type);
+        $remove_count++;
       }
+      
+      // Update the attribute.
       else {
-        db_query("UPDATE $attr_table SET label = '%s', ordering = %d, required = %d, display = %d WHERE aid = %d AND $id = $sql_type", $attribute['label'], $attribute['ordering'], $attribute['required'], $attribute['display'], $aid, $form_state['values']['id']);
+        uc_attribute_subject_save($attribute, $id, $type);
         $changed = TRUE;
       }
     }
-
-    if (count($remove_aids) > 0) {
-      $id_value = $form_state['values']['id'];
-      $remove_aids_value = implode(', ', $remove_aids);
-
-      db_query("DELETE FROM $opt_table WHERE EXISTS (SELECT * FROM {uc_attribute_options} AS ao WHERE $opt_table.oid = ao.oid AND ao.aid IN (%s)) AND $opt_table.$id = $sql_type", $remove_aids_value, $id_value);
-      db_query("DELETE FROM $attr_table WHERE $id = $sql_type AND aid IN (%s)", $id_value, $remove_aids_value);
-      if ($form_state['values']['type'] == 'product') {
-        db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $id_value);
-      }
-
-      drupal_set_message(format_plural(count($remove_aids), '@count attribute has been removed.', '@count attributes have been removed.'));
+    
+    if ($remove_count) {
+      drupal_set_message(format_plural($remove_count, '@count attribute has been removed.', '@count attributes have been removed.'));
     }
-
+    
     if ($changed) {
       drupal_set_message(t('The changes have been saved.'));
     }
   }
+  
+  // The attribute add form.
   elseif ($form_state['values']['view'] == 'add') {
     foreach ($form_state['values']['add_attributes'] as $aid) {
-      // Enable all options for added attributes.
+      // Load the base attribute, and save it with all its options.
       $attribute = uc_attribute_load($aid);
-      foreach ($attribute->options as $option) {
-        db_query("INSERT INTO $opt_table ($id, oid, cost, price, weight, ordering) VALUES ($sql_type, %d, %f, %f, %f, %d)", $form_state['values']['id'], $option->oid, $option->cost, $option->price, $option->weight, $option->ordering);
-      }
-      // Make the first option (if any) the default.
-      $option = reset($attribute->options);
-      if ($option) {
-        $oid = $option->oid;
-      }
-      else {
-        $oid = 0;
-      }
-      db_query("INSERT INTO $attr_table ($id, aid, label, ordering, default_option, required, display) SELECT $sql_type, aid, label, ordering, %d, required, display FROM {uc_attributes} WHERE aid = %d", $form_state['values']['id'], $oid, $aid);
+      uc_attribute_subject_save($attribute, $id, $type, TRUE);
     }
+    
     if (count($form_state['values']['add_attributes']) > 0) {
-      if ($form_state['values']['type'] == 'product') {
-        db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $form_state['values']['id']);
-      }
       drupal_set_message(format_plural(count($form_state['values']['add_attributes']), '@count attribute has been added.', '@count attributes have been added.'));
     }
   }
 
   if ($form_state['values']['type'] == 'product') {
-    $form_state['redirect'] = 'node/'. $form_state['values']['id'] .'/edit/attributes';
+    $form_state['redirect'] = 'node/'. $id .'/edit/attributes';
   }
   else {
-    $form_state['redirect'] = 'admin/store/products/classes/'. $form_state['values']['id'] .'/attributes';
+    $form_state['redirect'] = 'admin/store/products/classes/'. $id .'/attributes';
   }
 }
 
@@ -771,7 +755,7 @@ function uc_object_options_form($form_st
         );
       }
 
-      $form['attributes'][$aid]['default'] = array(
+      $form['attributes'][$aid]['default_option'] = array(
         '#type' => 'radios',
         '#options' => $options,
         '#default_value' => /* $attribute->required ? NULL : */ $attribute->default_option,
@@ -779,7 +763,7 @@ function uc_object_options_form($form_st
       );
     }
     else {
-      $form['attributes'][$aid]['default'] = array(
+      $form['attributes'][$aid]['default_option'] = array(
         '#value' => t('This attribute does not have any options.'),
       );
     }
@@ -820,12 +804,12 @@ function theme_uc_object_options_form($f
 
     $rows = array();
 
-    if (element_children($form['attributes'][$key]['default'])) {
+    if (element_children($form['attributes'][$key]['default_option'])) {
 
-      foreach (element_children($form['attributes'][$key]['default']) as $oid) {
+      foreach (element_children($form['attributes'][$key]['default_option']) as $oid) {
         $row = array(
           drupal_render($form['attributes'][$key]['options'][$oid]['select']),
-          drupal_render($form['attributes'][$key]['default'][$oid]),
+          drupal_render($form['attributes'][$key]['default_option'][$oid]),
           drupal_render($form['attributes'][$key]['options'][$oid]['cost']),
           drupal_render($form['attributes'][$key]['options'][$oid]['price']),
           drupal_render($form['attributes'][$key]['options'][$oid]['weight']),
@@ -842,7 +826,7 @@ function theme_uc_object_options_form($f
     }
     else {
       $row = array();
-      $row[] = array('data' => drupal_render($form['attributes'][$key]['default']), 'colspan' => 6);
+      $row[] = array('data' => drupal_render($form['attributes'][$key]['default_option']), 'colspan' => 6);
       $rows[] = $row;
     }
 
@@ -883,8 +867,8 @@ function uc_object_options_form_validate
           }
         }
       }
-      if (!empty($selected_opts) && !$form['attributes'][$aid]['default']['#disabled'] && !in_array($attribute['default'], $selected_opts)) {
-        form_set_error($attribute['default']);
+      if (!empty($selected_opts) && !$form['attributes'][$aid]['default_option']['#disabled'] && !in_array($attribute['default_option'], $selected_opts)) {
+        form_set_error($attribute['default_option']);
         $error = TRUE;
       }
     }
@@ -895,41 +879,36 @@ function uc_object_options_form_validate
   }
 }
 
+/**
+ * Submit handler for the product/class attribute options form.
+ */
 function uc_object_options_form_submit($form, &$form_state) {
-  if ($form_state['values']['type'] == 'product') {
-    $attr_table = '{uc_product_attributes}';
-    $opt_table = '{uc_product_options}';
-    $id = 'nid';
-    $sql_type = '%d';
-  }
-  elseif ($form_state['values']['type'] == 'class') {
-    $attr_table = '{uc_class_attributes}';
-    $opt_table = '{uc_class_attribute_options}';
-    $id = 'pcid';
-    $sql_type = "'%s'";
-  }
-
+  $id = $form_state['values']['id'];
+  $type = $form_state['values']['type'];
+  
   foreach ($form_state['values']['attributes'] as $attribute) {
-    db_query("UPDATE $attr_table SET default_option = %d WHERE $id = $sql_type AND aid = %d", $attribute['default'], $form_state['values']['id'], $attribute['aid']);
-
+    // Save the default option.
+    uc_attribute_subject_save($attribute, $id, $type);
+    
+    // Deal with options if they exist.
     if (is_array($attribute['options'])) {
       foreach ($attribute['options'] as $oid => $option) {
-        db_query("DELETE FROM $opt_table WHERE $id = $sql_type AND oid = %d", $form_state['values']['id'], $oid);
-
+        
+        // Checked?
         if ($option['select']) {
-          db_query("INSERT INTO $opt_table ($id, oid, cost, price, weight, ordering) VALUES ($sql_type, %d, %f, %f, %f, %d)",
-                   $form_state['values']['id'], $oid, $option['cost'], $option['price'], $option['weight'], $option['ordering']);
+          $option['oid'] = $oid;
+          uc_attribute_subject_option_save($option, $id, $type);
         }
-        elseif ($form_state['values']['type'] == 'product') {
-          $aid = $attribute['aid'];
-          $match = 'i:'. $aid .';s:'. strlen($oid) .':"'. $oid .'";';
-          db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d AND combination LIKE '%%%s%%'", $form_state['values']['id'], $match);
+        
+        // Unchecked means delete the option.
+        else {
+          uc_attribute_subject_option_delete($oid, $id, $type);
         }
       }
     }
   }
 
-  drupal_set_message(t('The !type options have been saved.', array('!type' => $form_state['values']['type'] == 'product' ? t('product') : t('product class'))));
+  drupal_set_message(t('The !type options have been saved.', array('!type' => $type == 'product' ? t('product') : t('product class'))));
 }
 
 /**

=== modified file 'uc_attribute/uc_attribute.module'
--- uc_attribute/uc_attribute.module	2009-06-10 20:28:03 +0000
+++ uc_attribute/uc_attribute.module	2009-06-11 07:31:03 +0000
@@ -292,6 +292,10 @@ function uc_attribute_form_alter(&$form,
 function uc_attribute_nodeapi(&$node, $op, $arg3 = NULL, $arg4 = NULL) {
   if (uc_product_is_product($node->type)) {
     switch ($op) {
+      // Copy any attributes/options to the new translated node.
+      case 'prepare translation':
+        $node->translation_attributes = uc_product_get_attributes($node->translation_source->nid); 
+        
       case 'load':
         $attributes = uc_product_get_attributes($node->nid);
         if (is_array($attributes) && !empty($attributes)) {
@@ -311,7 +315,17 @@ function uc_attribute_nodeapi(&$node, $o
             db_query("INSERT INTO {uc_product_options} (nid, oid, cost, price, weight, ordering) SELECT %d, oid, cost, price, weight, ordering FROM {uc_class_attribute_options} WHERE pcid = '%s'", $node->nid, $node->type);
             break;
         }
-
+        
+        if (isset($node->translation_attributes)) {
+          foreach ($attributes as $attribute) {
+            foreach ($attribute->options as $option) {
+              $option->nid = $node->nid;
+              drupal_write_record('uc_product_options', $option);
+            }
+            $attribute->nid = $node->nid;
+            drupal_write_record('uc_product_attributes', $attribute);
+          }
+        }
         break;
       case 'delete':
         db_query("DELETE FROM {uc_product_options} WHERE nid = %d", $node->nid);
@@ -773,3 +787,210 @@ function _uc_attribute_get_name($attribu
     return (empty($attribute->label) || ($attribute->label == '<none>' && $title) ? $attribute->name : $attribute->label);
   }
 }
+
+/**
+ * Delete a product/class attribute.
+ * 
+ * @param $aid
+ *   The base attribute ID.
+ * @param $id
+ *   The product/class ID.
+ * @param $type
+ *   Is this a product or a class?
+ */
+function uc_attribute_subject_delete($aid, $id, $type) {
+  $sql = uc_attribute_type_info($type);
+  
+  // Delete all the options associated with this product/class attribute, and
+  // then the attribute itself.
+  $result = db_query("SELECT oid FROM {uc_attributes} WHERE aid = %d", $aid);
+  while ($oid = db_result($result)) {
+    // Don't delete the adjustments one at a time. We'll do it in bulk soon for
+    // efficiency.
+    uc_attribute_subject_option_delete($oid, $id, $type, FALSE);
+  }
+  db_query("DELETE FROM {$sql['attr_table']} WHERE {$sql['id']} = {$sql['placeholder']} AND aid = %d", $id, $aid);
+  
+  // If this is a product attribute, wipe any associated adjustments.
+  if ($type == 'product') {
+    db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d", $id);
+  }
+}
+
+/**
+ * Save a product/class attribute.
+ * 
+ * @param &$attribute
+ *   The product/class attribute.
+ * @param $id
+ *   The product/class ID.
+ * @param $type
+ *   Is this a product or a class?
+ * @param $save_options
+ *   Save the product/class attribute's options, too?
+ */
+function uc_attribute_subject_save(&$attribute, $id, $type, $save_options = FALSE) {
+  $sql = uc_attribute_type_info($type);
+  
+  // Convert to an array if needed.
+  if (is_object($attribute)) {
+    $attribute = (array) $attribute;
+    $object = TRUE;
+  }
+  else {
+    $object = FALSE;
+  }
+  
+  // Insert or update?
+  $key = uc_attribute_subject_exists($attribute['aid'], $id, $type) ? array('aid', $sql['id']) : NULL;
+  
+  // First, save the options. First because if this is an insert, we'll set
+  // a default option for the product/class attribute.
+  if ($save_options) {
+    foreach ($attribute['options'] as $option) {
+      uc_attribute_subject_option_save($option, $id, $type);
+    }
+    
+    // Is this an insert? If so, we'll set the default option.
+    if (empty($key)) {
+      $default_option = 0;
+      // Make the first option (if any) the default.
+      if (is_array($attribute['options'])) {
+        $option = (array) reset($attribute['options']);
+        $default_option = $option['oid'];
+      }
+      $attribute['default_option'] = $default_option;
+    }
+  }
+  
+  // Merge in the product/class attribute's ID and save.
+  $attribute = array($type == 'product' ? 'nid' : 'pcid' => $id) + $attribute;
+  drupal_write_record(trim($sql['attr_table'], '{}'), $attribute, $key);
+  
+  // Convert back if necessary, for the caller.
+  if ($object) {
+    $attribute = (object) $attribute;
+  }
+}
+
+/**
+ * Delete a product/class attribute option.
+ * 
+ * @param $oid
+ *   The base attribute's option ID.
+ * @param $id
+ *   The product/class ID.
+ * @param $type
+ *   Is this a product or a class?
+ */
+function uc_attribute_subject_option_delete($oid, $id, $type, $adjustments = TRUE) {
+  $sql = uc_attribute_type_info($type);
+  
+  // Delete the option.
+  db_query("DELETE FROM {$sql['opt_table']} WHERE {$sql['id']} = {$sql['placeholder']} AND oid = %d", $id, $oid);
+    
+  // If this is a product, clean up the associated adjustments.
+  if ($adjustments && $type == 'product') {
+    $aid = db_result(db_query("SELECT aid FROM {uc_attribute_options} WHERE oid = %d", $oid));
+    
+    $match = 'i:'. $aid .';s:'. strlen($oid) .':"'. $oid .'";';
+    db_query("DELETE FROM {uc_product_adjustments} WHERE nid = %d AND combination LIKE '%%%s%%'", $form_state['values']['id'], $match);
+  }
+}
+
+/**
+ * Save a product/class attribute option.
+ * 
+ * @param &$option
+ *   The product/class attribute option.
+ * @param $id
+ *   The product/class ID.
+ * @param $type
+ *   Is this a product or a class?
+ */
+function uc_attribute_subject_option_save(&$option, $id, $type) {
+  $sql = uc_attribute_type_info($type);
+
+  // Convert to an array if needed.
+  if (is_object($option)) {
+    $option = (array) $option;
+    $object = TRUE;
+  }
+  else {
+    $object = FALSE;
+  }
+  
+  // Insert or update?
+  $key = uc_attribute_subject_option_exists($option['oid'], $id, $type) ? array('oid', $sql['id']) : NULL;
+  
+  // Merge in the product/class attribute option's ID, and save.
+  $option = array($type == 'product' ? 'nid' : 'pcid' => $id) + $option;
+  drupal_write_record(trim($sql['opt_table'], '{}'), $option, $key);
+  
+  // Convert back if necessary, for the caller.
+  if ($object) {
+    $option = (object) $option;
+  }
+}
+
+/**
+ * Check if a product/class attribute exists.
+ * 
+ * @param $aid
+ *   The base attribute ID.
+ * @param $id
+ *   The product/class attribute's ID.
+ * @param $type
+ *   Is this a product or a class?
+ * @return (bool)
+ */
+function uc_attribute_subject_exists($aid, $id, $type) {
+  $sql = uc_attribute_type_info($type);
+  return FALSE !== db_result(db_query("SELECT aid FROM {$sql['attr_table']} WHERE aid = %d AND {$sql['id']} = {$sql['placeholder']}", $aid, $id));
+}
+
+/**
+ * Check if a product/class attribute option exists.
+ * 
+ * @param $oid
+ *   The base attribute option ID.
+ * @param $id
+ *   The product/class attribute option's ID.
+ * @param $type
+ *   Is this a product or a class?
+ * @return (bool)
+ */
+function uc_attribute_subject_option_exists($oid, $id, $type) {
+  $sql = uc_attribute_type_info($type);
+  return FALSE !== db_result(db_query("SELECT oid FROM {$sql['opt_table']} WHERE oid = %d AND {$sql['id']} = {$sql['placeholder']}", $oid, $id));
+}
+
+/**
+ * Return a list of db helpers to abstract the queries between products/classes.
+ * @param $type
+ *   Is this a product or a class?
+ * @return (array)
+ */
+function uc_attribute_type_info($type) {
+  switch ($type) {
+    case 'product':
+      return array(
+        'attr_table' => '{uc_product_attributes}',
+        'opt_table' => '{uc_product_options}',
+        'id' => 'nid',
+        'placeholder' => '%d',
+      );
+    break;
+    
+    case 'class':
+      return array(
+        'attr_table' => '{uc_class_attributes}',
+        'opt_table' => '{uc_class_attribute_options}',
+        'id' => 'pcid',
+        'placeholder' => "'%s'",
+      );
+    break;
+  }
+}
+
+

