Index: uc_restrict_qty.module
===================================================================
--- uc_restrict_qty.module	(revision 4710)
+++ uc_restrict_qty.module	(working copy)
@@ -281,6 +281,50 @@ function uc_restrict_qty_form_alter(&$fo
   if ($form_id == 'uc_product_feature_settings_form') {
     $form['#validate'][] = 'uc_restrict_qty_settings_validate';
   }
+
+  if ($form_id == 'uc_product_class_form') {
+    $class = $form['#parameters'][2];
+
+    $form['uc_restrict_qty'] = array(
+      '#type' => 'fieldset',
+      '#title' => t('Restrict Qty'),
+      '#collapsible' => TRUE,
+      '#collapsed' => TRUE,
+    );
+    
+    $form['uc_restrict_qty']['uc_restrict_qty_default_qty'] = array(
+      '#title' => t("Default maximum limit for a product of this class"),
+      '#type' => 'textfield',
+      '#size' => 5,
+      '#maxlength' => 5,
+      '#description' => t('The number of products of this class that can be added to a cart. Set to 0 for unlimited.'),
+      '#default_value' => $class->uc_restrict_qty_default_qty,
+    );
+    $form['uc_restrict_qty']['uc_restrict_qty_default_lifetime'] = array(
+      '#title' => t("Is restriction the user's lifetime limit"),
+      '#type' => 'checkbox',
+      '#description' => t("Useful when you want to prevent double ordering of a product."),
+      '#default_value' => $class->uc_restrict_qty_default_lifetime,
+    );
+
+    $options = array(
+      '' => t('Do nothing'),
+      'batch_empty' => t('Update nodes with no current qty restriction'),
+      'batch_all' => t('Update every single node with new defaults (old restrictions will be overwritten)')
+    );
+    $form['uc_restrict_qty']['uc_restrict_qty_default_batch_update'] = array(
+      '#title' => t("Batch update existing nodes"),
+      '#type' => 'radios',
+      '#description' => t("This can be useful if you are enabling this after some products have been created, of you want overwrite restrictions with new information. Use the last option carefully."),
+      '#default_value' => '',
+      '#options' => $options,
+    );
+
+
+    $form['#submit'][] = 'uc_restrict_qty_uc_product_class_form_submit';
+
+    $form['submit']['#weight'] = 10;
+  }
 }
 
 // Themes cart Qty. boxes so they can't be changed. (currently not in use)
@@ -306,12 +350,9 @@ function uc_restrict_qty_count($form_val
  * ************************************************************************* */
 
 /**
- * Delete all data associated with a given node.
- *
- * @param $nid
- *   A Drupal node ID.
+ * Nodeapi 'delete' callback handler
  */
-function uc_restrict_qty_node_delete($nid) {
+function uc_restrict_qty_nodeapi_delete($nid) {
   db_query("DELETE FROM {uc_restrict_qty_products} WHERE nid = %d", $nid);
 }
 
@@ -324,3 +365,180 @@ function uc_restrict_qty_node_delete($ni
 function uc_restrict_qty_feature_delete($pfid) {
   db_query("DELETE FROM {uc_restrict_qty_products} WHERE pfid = %d", $pfid);
 }
\ No newline at end of file
+
+/**
+ * Implementation of hook_schema_alter().
+ */
+function uc_restrict_qty_schema_alter(&$schema) {
+  // Add field to existing schema.
+  $schema['uc_product_classes']['fields']['uc_restrict_qty_default_qty'] = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'description' => 'The number of products of this class that can be added to a cart.',
+  );
+  $schema['uc_product_classes']['fields']['uc_restrict_qty_default_lifetime'] = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'description' => 'Is restriction the user\'s lifetime limit.',
+  );
+}
+
+/**
+ * Submit handler for uc_poroduct_class_form().
+ */
+function uc_restrict_qty_uc_product_class_form_submit($form, &$form_state) {
+  // @see uc_product_class_form_submit().
+  $is_new = $form['pcid']['#type'] == 'textfield';
+  $pcid = $form_state['values']['pcid'];
+  if ($is_new) {
+    // Convert whitespace to underscores, and remove other non-alphanumeric characters.
+    $pcid = preg_replace(array('/\s+/', '/\W/'), array('_', ''), strtolower($pcid));
+  }
+
+  // We assume it has to be there as the previous submit handler should have
+  // created it
+  db_query("UPDATE {uc_product_classes} SET uc_restrict_qty_default_qty = %d, uc_restrict_qty_default_lifetime = %d WHERE pcid = '%s'", $form_state['values']['uc_restrict_qty_default_qty'], $form_state['values']['uc_restrict_qty_default_lifetime'], $pcid);
+
+  if (!empty($form_state['values']['uc_restrict_qty_default_batch_update'])) {
+    $batch_parameters = array(
+      'pcid' => $pcid,
+      'batch_update' => $form_state['values']['uc_restrict_qty_default_batch_update'],
+    );
+    $batch = array(
+      'operations' => array(
+        array('uc_restrict_qty_batch_process', array($batch_parameters)),
+        ),
+      'finished' => 'uc_restrict_qty_batch_finished',
+      'title' => t('Batch updating existing nodes'),
+      'init_message' => t('Batch updating is starting.'),
+      'progress_message' => t('Processing nodes.'),
+      'error_message' => t('Batch updating has encountered an error.'),
+      // in case we want to move batch logic into a separate file
+      //'file' => drupal_get_path('module', 'uc_restrict_qty') . '/uc_restrict_qty.batch.inc',
+    );
+    batch_set($batch);
+  }
+}
+
+function uc_restrict_qty_batch_process($batch_parameters, &$context) {
+  if (!isset($context['sandbox']['progress'])) {
+    $context['sandbox']['progress'] = 0;
+    $context['sandbox']['current_node'] = 0;
+    if ($batch_parameters['batch_update'] == 'batch_all') {
+      $context['sandbox']['max'] = db_result(db_query("SELECT COUNT(DISTINCT n.nid) FROM {node} n WHERE n.type = '%s'", $batch_parameters['pcid']));
+    }
+    else {
+      $context['sandbox']['max'] = db_result(db_query("SELECT COUNT(DISTINCT n.nid) FROM {node} n LEFT JOIN {uc_product_features} pf ON n.nid = pf.nid AND pf.fid = 'restrict_qty' LEFT JOIN {uc_restrict_qty_products} rq ON n.nid = rq.nid WHERE n.type = '%s' AND rq.nid IS NULL AND pf.nid IS NULL", $batch_parameters['pcid']));
+    }
+  }
+
+  // Set the amount of nodes we will process on each batch page reload
+  $limit = 5;
+ 
+  // With each pass through the callback, retrieve the next group of nids.
+  if ($batch_parameters['batch_update'] == 'batch_all') {
+    $result = db_query_range("SELECT DISTINCT n.nid FROM {node} n WHERE n.nid > %d AND n.type = '%s' ORDER BY nid ASC", $context['sandbox']['current_node'], $batch_parameters['pcid'], 0, $limit);
+  }
+  else {
+    $result = db_query_range("SELECT DISTINCT n.nid FROM {node} n  LEFT JOIN {uc_product_features} pf ON n.nid = pf.nid AND pf.fid = 'restrict_qty' LEFT JOIN {uc_restrict_qty_products} rq ON n.nid = rq.nid WHERE n.nid > %d AND n.type = '%s' AND rq.nid IS NULL AND pf.nid IS NULL ORDER BY nid ASC", $context['sandbox']['current_node'], $batch_parameters['pcid'], 0, $limit);
+  }
+
+  while ($row = db_fetch_array($result)) {
+
+    // Here we actually perform our processing on the current node.
+    $node = node_load($row['nid'], NULL, TRUE);
+
+    // delete node's features
+    db_query("DELETE FROM {uc_product_features} WHERE fid ='restrict_qty' AND nid = %d", $node->nid);
+    // delete node's internal data
+    uc_restrict_qty_nodeapi_delete($node->nid);
+
+    uc_restrict_qty_nodeapi_insert($node);
+
+    // Store some result for post-processing in the finished callback.
+    $context['results'][] = check_plain($node->title);
+
+    // Update our progress information.
+    $context['sandbox']['progress']++;
+    $context['sandbox']['current_node'] = $node->nid;
+    $context['message'] = t('Now processing %node (%progress/%max)', array('%node' => $node->title, '%progress' => $context['sandbox']['progress'], '%max' => $context['sandbox']['max']));
+  }
+
+  // Inform the batch engine that we are not finished,
+  // and provide an estimation of the completion level we reached.
+  if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
+    $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
+  }
+}
+
+/**
+ * Batch 'finished' callback
+ */
+function uc_restrict_qty_batch_finished($success, $results, $operations) {
+  if ($success) {
+    // Here we do something meaningful with the results.
+    $message = count($results) .' processed.';
+    $message .= theme('item_list', $results);
+  }
+  else {
+    // An error occurred.
+    // $operations contains the operations that remained unprocessed.
+    $error_operation = reset($operations);
+    $message = t('An error occurred while processing %error_operation with arguments: @arguments', array('%error_operation' => $error_operation[0], '@arguments' => print_r($error_operation[1], TRUE)));
+  }
+  drupal_set_message($message);
+}
+
+/**
+ * Nodeapi 'insert' callback handler
+ */
+function uc_restrict_qty_nodeapi_insert($node) {
+  $class = uc_product_class_load($node->type);
+  $models = uc_product_get_models($node);
+  $product_qty = array(
+    'nid'      => $node->nid,
+    'qty'      => $class->uc_restrict_qty_default_qty,
+    'lifetime' => $class->uc_restrict_qty_default_lifetime,
+  );
+
+  $description = '<strong>'. t('SKU') .':</strong> '. (empty($product_qty['model']) ? t('Any') : $product_qty['model']) .'<br/>';
+  $description .= '<strong>'. t('Quantity restriction') .':</strong> '. $product_qty['qty'] .'<br/>';
+  $description .= '<strong>'. t('Type') .':</strong> '. ($product_qty['lifetime'] ? t('Lifetime') : t('Cart max.')) .'<br/>';
+
+  $data = array(
+    'pfid' => $product_qty['pfid'],
+    'nid' => $product_qty['nid'],
+    'fid' => 'restrict_qty',
+    'description' => $description,
+  );
+
+  uc_product_feature_save($data);
+
+  // Insert or update uc_file_product table
+  if (empty($product_qty['pfid'])) {
+    $product_qty['pfid'] = db_last_insert_id('uc_product_features', 'pfid');
+  }
+
+  drupal_write_record('uc_restrict_qty_products', $product_qty);
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ */
+function uc_restrict_qty_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+  $class = uc_product_class_load($node->type);
+
+  if ($class->uc_restrict_qty_default_qty) {
+    switch ($op) {
+      case 'insert':
+        uc_restrict_qty_nodeapi_insert($node);
+        break;
+
+      case 'delete':
+        uc_restrict_qty_nodeapi_delete($node->nid);
+        break;
+    }
+  }
+}
\ No newline at end of file
Index: uc_restrict_qty.install
===================================================================
--- uc_restrict_qty.install	(revision 4710)
+++ uc_restrict_qty.install	(working copy)
@@ -73,6 +73,22 @@ function uc_restrict_qty_schema() {
  */
 function uc_restrict_qty_install() {
   drupal_install_schema('uc_restrict_qty');
+
+  $ret = array();
+  $field = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'description' => 'The number of products of this class that can be added to a cart.',
+  );
+  db_add_field($ret,'uc_product_classes', 'uc_restrict_qty_default_qty', $field);
+  $field = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'description' => 'The number of products of this class that can be added to a cart.',
+  );
+  db_add_field($ret, 'uc_product_classes', 'uc_restrict_qty_default_lifetime', $field);
 }
 
 /**
@@ -83,6 +99,10 @@ function uc_restrict_qty_install() {
 function uc_restrict_qty_uninstall() {
   drupal_uninstall_schema('uc_restrict_qty');
   db_query("DELETE FROM {variable} WHERE name LIKE 'uc_restrict_qty_%%'");
+
+  $ret = array();
+  db_drop_field($ret, 'uc_product_classes', 'uc_restrict_qty_default_qty');
+  db_drop_field($ret, 'uc_product_classes', 'uc_restrict_qty_default_lifetime');
 }
 
 /**
@@ -105,3 +125,25 @@ function uc_restrict_qty_update_6201() {
   db_add_field($ret, 'uc_restrict_qty_products', 'lifetime', array('type' => 'int', 'size' => 'tiny', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 1));
   return $ret;
 }
+
+/**
+ * Add fields to the uc_product_classes schema
+ */
+function uc_restrict_qty_update_6202() {
+  $ret = array();
+  $field = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'description' => 'The number of products of this class that can be added to a cart.',
+  );
+  db_add_field($ret,'uc_product_classes', 'uc_restrict_qty_default_qty', $field);
+  $field = array(
+    'type' => 'int',
+    'not null' => TRUE,
+    'default' => 0,
+    'description' => 'The number of products of this class that can be added to a cart.',
+  );
+  db_add_field($ret, 'uc_product_classes', 'uc_restrict_qty_default_lifetime', $field);
+  return $ret;
+}
\ No newline at end of file
