? 765154.uc_node_access_tac_lite.patch
Index: uc_node_access.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/uc_node_access/uc_node_access.install,v
retrieving revision 1.1.4.2
diff -u -p -r1.1.4.2 uc_node_access.install
--- uc_node_access.install	6 Jan 2009 19:26:19 -0000	1.1.4.2
+++ uc_node_access.install	1 May 2010 18:22:17 -0000
@@ -90,6 +90,12 @@ function uc_node_access_schema() {
 
   $schema['uc_node_access_expirations'] = array(
     'fields' => array(
+      'pfid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
       'uid' => array(
         'type' => 'int',
         'unsigned' => TRUE,
@@ -110,6 +116,7 @@ function uc_node_access_schema() {
       ),
     ),
     'indexes' => array(
+      'pfid' => array('pfid'),
       'uid' => array('uid'),
       'access_nid' => array('access_nid'),
     ),
@@ -174,3 +181,21 @@ function uc_node_access_update_6000() {
 
   return $ret;
 }
+
+/**
+ * Implementation of hook_update_N().
+ */
+function uc_node_access_update_6001() {
+  $ret = array();
+
+  $pfid = array(
+    'type' => 'int',
+    'unsigned' => TRUE,
+    'not null' => TRUE,
+    'default' => 0,
+  );
+
+  db_add_field($ret, 'uc_node_access_expirations', 'pfid', $pfid, array('indexes' => array('pfid' => array('pfid'))));
+  
+  return $ret;
+}
Index: uc_node_access.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/uc_node_access/uc_node_access.module,v
retrieving revision 1.1.4.3
diff -u -p -r1.1.4.3 uc_node_access.module
--- uc_node_access.module	7 Jan 2009 18:32:51 -0000	1.1.4.3
+++ uc_node_access.module	1 May 2010 18:22:17 -0000
@@ -30,13 +30,13 @@ function uc_node_access_cron() {
   $items = array();
 
   // Load and process any expired node access rules.
-  $result = db_query("SELECT uid, access_nid FROM {uc_node_access_expirations} WHERE expiration <= %d", time());
+  $result = db_query("SELECT pfid, uid, access_nid FROM {uc_node_access_expirations} WHERE expiration <= %d", time());
   while ($access = db_fetch_array($result)) {
     // Revoke access for the user through the current node access handler.
-    uc_node_access_op('revoke', $access['access_nid'], $access['uid']);
+    uc_node_access_op('revoke', $access['access_nid'], $access['uid'], $access['pfid']);
 
     // Delete the expiration from the database.
-    db_query("DELETE FROM {uc_node_access_expirations} WHERE uid = %d AND access_nid = %d", $access['uid'], $access['access_nid']);
+    db_query("DELETE FROM {uc_node_access_expirations} WHERE uid = %d AND pfid = %d", $access['uid'], $access['pfid']);
 
     $items[] = t('User @uid lost access to <a href="!url">node @nid</a>.', array('@uid' => $access['uid'], '@nid' => $access['access_nid'], '!url' => url('node/'. $access['access_nid'])));
 
@@ -154,29 +154,6 @@ function uc_node_access_feature_form($fo
     '#required' => TRUE,
   );
 
-  $form['delay_period'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Access time delay'),
-    '#collapsible' => FALSE,
-    '#description' => t('Specify the length of time after purchase the customer must wait to have access to the node.'),
-    '#attributes' => array('class' => 'time-period-fieldset'),
-  );
-  $form['delay_period']['delay_period_value'] = array(
-    '#type' => 'select',
-    '#options' => drupal_map_assoc(uc_range(1, 52)),
-    '#default_value' => $access['delay_period_value'],
-  );
-  $form['delay_period']['delay_period_unit'] = array(
-    '#type' => 'select',
-    '#options' => array(
-      'days' => t('day(s)'),
-      'weeks' => t('week(s)'),
-      'months' => t('month(s)'),
-      'years' => t('year(s)'),
-    ),
-    '#default_value' => $access['delay_period_unit'],
-  );
-
   $form['time_period'] = array(
     '#type' => 'fieldset',
     '#title' => t('Access time period'),
@@ -314,7 +291,7 @@ function uc_node_access_feature_delete($
   db_query("DELETE FROM {uc_node_access_products} WHERE pfid = %d", $feature['pfid']);
 }
 
-function uc_node_access_op($op, $nid, $uid) {
+function uc_node_access_op($op, $nid, $uid, $pfid) {
   $handler = variable_get('ucna_handler', '');
 
   // Fail if the handler hasn't been specified or has been uninstalled.
@@ -324,13 +301,13 @@ function uc_node_access_op($op, $nid, $u
   }
 
   // Fail if we receive an invalid nid or uid.
-  if (intval($nid) <= 0 || intval($uid) <= 0) {
+  if (intval($nid) <= 0 || intval($uid) <= 0 || intval($pfid) <= 0) {
     watchdog('uc_node_access', 'Invalid arguments passed to UC Node Access for processing.', array(), WATCHDOG_WARNING);
     return;
   }
 
   // Pass the arguments onto the handler for processing.
-  $result = $handler($op, $nid, $uid);
+  $result = $handler($op, $nid, $uid, $pfid);
 
   // Log failed node access operations.
   if ($result === FALSE) {
@@ -392,7 +369,7 @@ function uc_node_access_grant_user_acces
   }
 
   // Grant access for the user through the current node access handler.
-  uc_node_access_op('grant', $access['access_nid'], $uid);
+  uc_node_access_op('grant', $access['access_nid'], $uid, $pfid);
 
   // Let Drupal know to adjust the access for the target node.
   node_access_acquire_grants($node);
@@ -412,17 +389,17 @@ function uc_node_access_grant_user_acces
   // Set the expiration in the DB if necessary.
   if ($expiration) {
     // Get a prior expiration if one exists.
-    $result = db_result(db_query("SELECT expiration FROM {uc_node_access_expirations} WHERE uid = %d AND access_nid = %d", $uid, $access['access_nid']));
+    $result = db_result(db_query("SELECT expiration FROM {uc_node_access_expirations} WHERE pfid = %d", $pfid));
 
     // If there was no prior expiration, insert a new one.
     if (empty($result)) {
-      db_query("INSERT INTO {uc_node_access_expirations} (uid, access_nid, expiration) VALUES (%d, %d, %d)",
-        $uid, $access['access_nid'], $expiration);
+      db_query("INSERT INTO {uc_node_access_expirations} (pfid, uid, access_nid, expiration) VALUES (%d, %d, %d, %d)",
+        $pfid, $uid, $access['access_nid'], $expiration);
       watchdog('uc_node_access', 'User @uid granted access to node @nid until @date.', array('@uid' => $uid, '@nid' => $access['access_nid'], '@date' => format_date($expiration, 'custom', 'm/d/Y')));
     }
     else if ($result < $expiration) {
       // Otherwise we need to update the expiration to be later.
-      db_query("UPDATE {uc_node_access_expirations} SET expiration = %d WHERE uid = %d AND access_nid = %d", $expiration, $uid, $access['access_nid']);
+      db_query("UPDATE {uc_node_access_expirations} SET expiration = %d WHERE uid = %d AND pfid = %d", $expiration, $uid, $pfid);
       watchdog('uc_node_access', 'User @uid extended access for node @nid until @date.', array('@uid' => $uid, '@nid' => $access['access_nid'], '@date' => format_date($expiration, 'custom', 'm/d/Y')));
     }
     else {
@@ -431,7 +408,7 @@ function uc_node_access_grant_user_acces
   }
   else {
     // Delete any prior expiration reference.
-    db_query("DELETE FROM {uc_node_access_expirations} WHERE uid = %d AND access_nid = %d", $uid, $access['access_nid']);
+    db_query("DELETE FROM {uc_node_access_expirations} WHERE uid = %d AND pfid = %d", $uid, $pfid);
 
     watchdog('uc_node_access', 'User @uid granted indefinite access to node @nid.', array('@uid' => $uid, '@nid' => $access['access_nid']));
   }
Index: handlers/uc_node_access_tac_lite.info
===================================================================
RCS file: handlers/uc_node_access_tac_lite.info
diff -N handlers/uc_node_access_tac_lite.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/uc_node_access_tac_lite.info	1 May 2010 18:22:17 -0000
@@ -0,0 +1,9 @@
+; $Id$
+
+name = UC Node Access TAC Lite
+description = Integrates UC Node Access with Taxonomy Access Control Lite for granting taxonomy term access on purchase.
+dependencies[] = uc_node_access
+dependencies[] = tac_lite
+package = Access control
+core = 6.x
+php = 5.0
Index: handlers/uc_node_access_tac_lite.install
===================================================================
RCS file: handlers/uc_node_access_tac_lite.install
diff -N handlers/uc_node_access_tac_lite.install
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/uc_node_access_tac_lite.install	1 May 2010 18:22:17 -0000
@@ -0,0 +1,58 @@
+<?php
+// $Id$
+
+/**
+ * Implementation of hook_schema().
+ */
+function uc_node_access_tac_lite_schema() {
+  $schema['uc_node_access_tac_lite_product_term'] = array(
+    'description' => 'Links a product node to a taxonomy term and tac_lite permissions scheme.',
+    'fields' => array(
+      'pfid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Primary Key: The {uc_product_features}.pfid of the product node.',
+      ),
+      'scheme' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 1,
+        'description' => 'Primary Key: The tac_lite permissions scheme.',
+      ),
+      'vid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'The {vocabulary}.vid to grant access to.',
+      ),
+      'tid' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+        'description' => 'Primary Key: The {term_data}.tid to grant access to.',
+      ),
+    ),
+    'primary key' => array('pfid'),
+  );
+
+  return $schema;
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function uc_node_access_tac_lite_install() {
+  drupal_install_schema('uc_node_access_tac_lite');
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function uc_node_access_tac_lite_uninstall() {
+  drupal_uninstall_schema('uc_node_access_tac_lite');
+}
Index: handlers/uc_node_access_tac_lite.module
===================================================================
RCS file: handlers/uc_node_access_tac_lite.module
diff -N handlers/uc_node_access_tac_lite.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ handlers/uc_node_access_tac_lite.module	1 May 2010 18:22:17 -0000
@@ -0,0 +1,277 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Integrates UC Node Access with Taxonomy Access Control Lite for granting node
+ * access on purchase.
+ */
+
+/**
+ * Implementation of hook_menu().
+ */
+function uc_node_access_tac_lite_menu () {
+  $items = array();
+
+  $items['node/%node/edit/features/node_access/%/delete'] = array(
+    'title' => 'UC Node Access delete',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('uc_node_access_tac_lite_feature_delete_confirm', 1, 5),
+    'access arguments' => array('administer product features'),
+  );
+
+  return $items;
+}
+
+/**
+ * Implementation of hook_ucna_handler().
+ */
+function uc_node_access_tac_lite_ucna_handler() {
+  $handlers = array();
+
+  $handlers[] = array(
+    'title' => t('Taxonomy Access Control Lite'),
+    'callback' => 'ucna_handler_tac_lite_content_access',
+  );
+
+  return $handlers;
+}
+
+/**
+ * Controls the default node access system using Taxonomy Access Control Lite.
+ */
+function ucna_handler_tac_lite_content_access($op, $nid, $uid, $pfid) {
+  $product_term = db_fetch_object(db_query("SELECT scheme, vid, tid FROM {uc_node_access_tac_lite_product_term} WHERE pfid = %d", $pfid));
+  $account = user_load(array('uid' => $uid));
+
+  $config = _tac_lite_config($product_term->scheme);
+
+  $save = array();
+  $save[$config['realm']] = $account->$config['realm'];
+
+  switch ($op) {
+    case 'grant':
+      $save[$config['realm']][$product_term->vid][$product_term->tid] = $product_term->tid;
+
+      break;
+
+    case 'revoke':
+      unset($save[$config['realm']][$product_term->vid][$product_term->tid]);
+
+      break;
+  }
+
+  user_save($account, $save);
+}
+
+/**
+ * Implementation of hook_form_alter().
+ */
+function uc_node_access_tac_lite_form_alter(&$form, &$form_state, $form_id) {
+  // Add our own form controls to node access product feature form. Includes
+  // taxonomy term this product feature will grant access to.
+  if ($form_id == 'uc_node_access_feature_form' && variable_get('ucna_handler', '') == 'ucna_handler_tac_lite_content_access') {
+    $form += _uc_node_access_tac_lite_terms($form['pfid']['#value']);
+    $form['tac_lite']['#weight'] = 0;
+    $form['model']['#weight'] = -1;
+    unset($form['access_nid'], $form['#submit'][array_search('uc_node_access_feature_form_submit', $form['#submit'])]);
+    $form['#submit'][] = 'uc_node_access_tac_lite_feature_form_submit';
+  }
+}
+
+/**
+ * Submit handler for node access feature form.
+ */
+function uc_node_access_tac_lite_feature_form_submit($form, &$form_state) {
+  // Cheat by using the product nid as the 'access_nid', since we're actually
+  // granting access to a taxonomy term not a node.
+  $form_state['values']['access_nid'] = $form_state['values']['nid'];
+
+  $term = db_result(db_query("SELECT name FROM {term_data} WHERE tid = %d", $form_state['values']['term_access']));
+
+  $access = array(
+    'pfid' => $form_state['values']['pfid'],
+    'model' => $form_state['values']['model'],
+    'access_nid' => $form_state['values']['access_nid'],
+    'access_limit' => $form_state['values']['access_limit'],
+    'delay_period' => $form_state['values']['delay_period_value'] .' '. $form_state['values']['delay_period_unit'],
+    'time_period' => $form_state['values']['time_period_value'] .' '. $form_state['values']['time_period_unit'],
+    'end_date' => $form_state['values']['end_date'],
+  );
+
+  switch ($form_state['values']['access_limit']) {
+    case 'time_period':
+      $description = t('Grant access to taxonomy term @term for @period, @delay after purchase.', array('@term' => $term, '@period' => $access['time_period'], '@delay' => $access['delay_period']));
+      break;
+    case 'end_date':
+      $description = t('Grant access to taxonomy term @term until @date, @delay after purchase.', array('@term' => $term, '@date' => $access['end_date']['month'] .'/'. $access['end_date']['day'] .'/'. $access['end_date']['year'], '@delay' => $access['delay_period']));
+      break;
+    case 'indefinite':
+    default:
+      $description = t('Grant indefinite access to taxonomy term @term, @delay after purchase.', array('@term' => $term, '@delay' => $access['delay_period']));
+      break;
+  }
+
+  if (!empty($access['model'])) {
+    $description .= '<br />'. t('Applies to SKU: @sku', array('@sku' => $access['model']));
+  }
+  else {
+    $description .= '<br />'. t('Applies to any SKU.');
+  }
+
+  $data = array(
+    'pfid' => $access['pfid'],
+    'nid' => $form_state['values']['nid'],
+    'fid' => 'node_access',
+    'description' => $description,
+  );
+
+  $form_state['redirect'] = uc_product_feature_save($data);
+
+  // On an insert, get the pfid used for the node access feature.
+  if (empty($access['pfid'])) {
+    $access['pfid'] = db_last_insert_id('uc_product_features', 'pfid');
+  }
+
+  uc_node_access_feature_save($access);
+
+  // Now save a {uc_node_access_tac_lite_product_term} record.
+  $product_term = new stdClass();
+  $product_term->scheme = $form_state['values']['scheme'];
+  $vid_tid = explode('-', $form_state['values']['term_access']);
+  $product_term->vid = $vid_tid[0];
+  $product_term->tid = $vid_tid[1];
+
+  // Check a product feature id has been passed from the form and there is an
+  // existing tac_lite_product_term record.
+  if (!empty($form_state['values']['pfid']) && db_result(db_query("SELECT COUNT(pfid) FROM {uc_node_access_tac_lite_product_term} WHERE pfid = %d", $form_state['values']['pfid'])) > 0) {
+    // Existing record, use the pfid passed by the form.
+    $product_term->pfid = $form_state['values']['pfid'];
+    drupal_write_record('uc_node_access_tac_lite_product_term', $product_term, 'pfid');
+  }
+  else {
+    // New record, use the pfid gained above by db_last_insert_id().
+    $product_term->pfid = $access['pfid'];
+    drupal_write_record('uc_node_access_tac_lite_product_term', $product_term);
+  }
+}
+
+/**
+ * Helper function for generating form with list of tids to grant access to.
+ *
+ * @see tac_lite_user()
+ */
+function _uc_node_access_tac_lite_terms($pfid) {
+  $form = array();
+  $vocabularies = taxonomy_get_vocabularies();
+  $vids = variable_get('tac_lite_categories', null);
+  if (!count($vids)) {
+    return;
+  }
+
+  if (!empty($pfid)) {
+    $product_term = db_fetch_object(db_query("SELECT scheme, vid, tid FROM {uc_node_access_tac_lite_product_term} WHERE pfid = %d", $pfid));
+  }
+  else {
+    $product_term = NULL;
+  }
+
+  $schemes = array();
+  for ($i = 1; $i <= variable_get('tac_lite_schemes', 1); $i++) {
+    $config = _tac_lite_config($i);
+    if (empty($config['name'])) {
+      continue;
+    }
+    $schemes[$i] = $config['name'];
+  }
+
+  if (count($schemes) > 1) {
+    $form['tac_lite']['scheme'] = array(
+      '#type' => 'radios',
+      '#title' => t('Permissions scheme'),
+      '#options' => $schemes,
+      '#default_value' => $product_term->scheme,
+      '#multiple' => FALSE,
+      '#required' => TRUE,
+    );
+  }
+  else {
+    $form['tac_lite']['scheme'] = array(
+      '#type' => 'hidden',
+      '#value' => $product_term->scheme,
+    );
+  }
+
+  $options = array();
+  $options[''] = '<' . t('none') . '>';
+
+  foreach ($vids as $vid) {
+    $v = $vocabularies[$vid];
+
+    $tree = taxonomy_get_tree($vid);
+
+    if ($tree) {
+      foreach ($tree as $term) {
+        $choice = new stdClass();
+        $choice->option = array($vid . '-' . $term->tid => str_repeat('-', $term->depth) . $term->name);
+        $options[check_plain($v->name)][] = $choice;
+      }
+    }
+  }
+
+  $form['tac_lite']['term_access'] = array(
+    '#type' => 'select',
+    '#title' => t('Taxonomy term'),
+    '#options' => $options,
+    '#default_value' => $product_term->vid . '-' . $product_term->tid,
+    '#multiple' => FALSE,
+  );
+
+  return $form;
+}
+
+/**
+ * Page callback for deleting a UC Node Access product feature.
+ *
+ * Much of this was taken from Ubercart.
+ *
+ * @see uc_product_features()
+ * @see uc_node_access_tac_lite_feature_delete_confirm_submit()
+ */
+function uc_node_access_tac_lite_feature_delete_confirm(&$form_state, $node, $pfid) {
+  $form['#node'] = $node;
+  $form['pfid'] = array(
+    '#type' => 'value',
+    '#value' => $pfid,
+  );
+  $result = db_query("SELECT description FROM {uc_product_features} WHERE pfid = %d", $pfid);
+  if ($feature = db_fetch_array($result)) {
+    // Show the confirmation form for deleting this feature.
+    $question = $node->title;
+    $description = t('Are you sure you wish to delete this %feature?', array('%feature' => uc_product_feature_data('node_access', 'title')))
+      .'<div><strong>'. t('Description') .':</strong><p>'. $feature['description'] .'</p></div>';
+
+    return confirm_form($form, $question, 'node/'. $node->nid .'/edit/features', $description, t('Delete'), t('Cancel'), 'pf_delete');
+  }
+  else {
+    drupal_set_message(t("That product feature doesn't exist."), 'error');
+    drupal_goto('node/'. arg(1) .'/edit/features');
+  }
+}
+
+/**
+ * Confirm submit handler to delete a feature from a product.
+ *
+ * Wraps around the Ubercart uc_product_features() function. This wrapper
+ * deletes a record from {uc_node_access_tac_lite_product_term} then hands
+ * control over to Ubercart to delete its own Product Feature records.
+ *
+ * @see uc_node_access_tac_lite_feature_delete_confirm()
+ */
+function uc_node_access_tac_lite_feature_delete_confirm_submit($form, &$form_state) {
+  require_once(drupal_get_path('module', 'uc_product') . '/uc_product.admin.inc');
+  $pfid = $form['pfid']['#value'];
+  db_query("DELETE FROM {uc_node_access_tac_lite_product_term} WHERE pfid = %d", $pfid);
+
+  uc_product_features($form['#node']);
+}
