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 = ''. t('SKU') .': '. (empty($product_qty['model']) ? t('Any') : $product_qty['model']) .'
'; + $description .= ''. t('Quantity restriction') .': '. $product_qty['qty'] .'
'; + $description .= ''. t('Type') .': '. ($product_qty['lifetime'] ? t('Lifetime') : t('Cart max.')) .'
'; + + $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