? LICENSE.txt
? node_limit_interval
? node_limit_og
? node_limit_role
? node_limit_type
? node_limit_user
Index: README.txt
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_limit/README.txt,v
retrieving revision 1.1
diff -u -p -r1.1 README.txt
--- README.txt	3 Mar 2009 01:58:35 -0000	1.1
+++ README.txt	27 Dec 2009 14:20:48 -0000
@@ -17,4 +17,25 @@ DETAILS:
 FUTURE PLANS:
 
 - Allow for time-based node limits (a limit for a certain time period, or a limit that expires on a certain date or after a certain interval)
-- Integration with other modules that segregate content, such as Organic Groups
\ No newline at end of file
+- Integration with other modules that segregate content, such as Organic Groups
+
+HOOKS FOR SUBMODULES:
+- hook_node_limit_element($lid = 0):
+  return an array with a single key-value pair.  The key must be the name of the submodule, and the value is an element in form api.
+  see node_limit_user for an implementation.
+  You may set the value '#custom_render' => true if your element requires a custom rendering
+  The $lid parameter is given if the user is editing (as opposed to adding) a node limit.  The implementor should fill in a #default_value for the element
+  
+- hook_node_limit_render_element(&$element):
+  called if the element returned from hook_node_limit_element has '#custom_render' => true
+  should return a string of rendered html
+  
+- hook_node_limit_element_validate($element):
+  called when the user attempts to add or edit a Node Limit.  The implementor of the hook has the opportunity to validate the value the user entered.
+  
+- hook_node_limit_element_submit($lid, $applies, $element):
+  called when the user adds or edits a Node Limit (and has passed validation).  The implementor of the hook has the opportunity to save the user-entered value based on the passed Node Limit id.  $applies == true iff the limit uses this limitation.  Otherwise the submodule should delete the limitation from its table.
+  
+- hook_node_limit_load($lid):
+  Called when node_limit loads a limit from the database.  The implementor should return an array with a single key-value pair.  The key must be the name of the submodule, and the value is an array containing 
+  
\ No newline at end of file
Index: node_limit.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_limit/node_limit.info,v
retrieving revision 1.1
diff -u -p -r1.1 node_limit.info
--- node_limit.info	3 Mar 2009 01:58:35 -0000	1.1
+++ node_limit.info	27 Dec 2009 14:20:48 -0000
@@ -1,4 +1,11 @@
 name = Node Limit
-description = Allows admins to restrict the number of nodes a user or role may create
-package = Access control
-core = 6.x
\ No newline at end of file
+description = Allows admins to restrict the number of nodes that may be created.
+package = Node Limit
+core = 6.x
+
+; Information added by drupal.org packaging script on 2009-04-30
+version = "6.x-1.x-dev"
+core = "6.x"
+project = "node_limit"
+datestamp = "1241051019"
+
Index: node_limit.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_limit/node_limit.install,v
retrieving revision 1.1
diff -u -p -r1.1 node_limit.install
--- node_limit.install	3 Mar 2009 01:58:35 -0000	1.1
+++ node_limit.install	27 Dec 2009 14:20:51 -0000
@@ -1,70 +1,73 @@
 <?php
+// $Id: node_limit.install,v 1.1.2.4 2009/04/30 00:08:54 davedelong Exp $
+
+/**
+ * @file
+ * Installation functions for module node_limit.
+ */
 
 define("NODE_LIMIT_NO_LIMIT", -1);
+
 /**
  * Implementation of hook_install().
  */
 function node_limit_install() {
-	drupal_install_schema('node_limit');
+  drupal_install_schema('node_limit');
 }
 
 /**
  * Implementation of hook_schema().
  */
 function node_limit_schema() {
-	$schema['node_limit_role'] = array(
-		'description'			=> t('The base Node Limit role table.'),
-		'fields'				=> array(
-			'rid'				=> array(
-				'description'	=> t('The {role}.rid to which this {node_limit_role} entry applies.'),
-				'type'			=> 'int',
-				'not null'		=> TRUE,
-				'default'		=> 0),
-			'type'				=> array(
-				'description'	=> t('The {node}.type to which this {node_limit_role} entry applies'),
-				'type' => 'varchar',
-				'length' => 32,
-				'not null'		=> TRUE),
-			'limit'				=> array(
-				'description'	=> t('The number of nodes that members in this role may create'),
-				'type'			=> 'int',
-				'size'			=> 'big',
-				'unsigned'		=> FALSE,
-				'not null'		=> TRUE,
-				'default'		=> node_limit_NO_LIMIT)),
-		'primary key'			=> array('rid', 'type'),
-	);
-	$schema['node_limit_user'] = array(
-		'description'			=> t('The base Node Limit user table.'),
-		'fields'				=> array(
-			'uid'				=> array(
-				'description'	=> t('The {user}.uid to which this {node_limit_user} entry applies.'),
-				'type'			=> 'int',
-				'not null'		=> TRUE,
-				'default'		=> 0),
-			'type'				=> array(
-				'description'	=> t('The {node}.type to which this {node_limit_user} entry applies'),
-				'type' => 'varchar',
-				'length' => 32,
-				'not null'		=> TRUE),
-			'limit'				=> array(
-				'description'	=> t('The number of nodes that this user may create'),
-				'type'			=> 'int',
-				'size'			=> 'big',
-				'unsigned'		=> FALSE,
-				'not null'		=> TRUE,
-				'default'		=> node_limit_NO_LIMIT)),
-		'primary key'			=> array('uid', 'type'),
-	);
-	return $schema;
+  $schema['node_limit'] = array(
+    'description' => t('The base Node Limit table'),
+    'fields' => array(
+      'lid' => array(
+        'description' => t('The limit id'),
+        'type'        => 'serial',
+        'not null'    => TRUE
+      ),
+      'node_limit' => array(
+        'description' => t('The node limit for this limit'),
+        'type'        => 'int',
+        'not null'    => TRUE,
+        'default'     => NODE_LIMIT_NO_LIMIT,
+      ),
+      'title' => array(
+        'description' => t('The display name for this limit'),
+        'type'        => 'varchar',
+        'length'      => 255,
+        'not null'    => TRUE,
+        'default'     => '',
+      ),
+      'weight' => array(
+        'description' => t('The weight of this limit'),
+        'type'        => 'int',
+        'not null'    => TRUE,
+        'default'     => 0,
+      ),
+    ),
+    'primary key' => array('lid')
+  );
+  return $schema;
 }
 
+function node_limit_update_6001() {
+  if (!module_exists('node_limit_type')) {
+    $ret = array();
+    drupal_set_message(t('node_limit module cannot be updated until after node_limit_type has been enabled. Please enable node_limit_type and then return to <a href="@update-php">update.php</a> and run the remaining updates.', array('@update-php' => base_path() .'update.php?op=selection')), 'warning', FALSE);
+    $ret['#abort'] = array('success' => FALSE, 'query' => t('node_limit.module has updates, but cannot be updated until after node_limit_type.module is enabled.'));
+    
+    return $ret;
+  }
+  db_query("INSERT INTO {node_limit_type} (SELECT lid, type FROM {node_limit})");
+  $ret = update_sql("ALTER TABLE {node_limit} DROP COLUMN `type`");
+  return $ret;
+}
 
 /*
- * Implementation of hook_uninstall
+ * Implementation of hook_uninstall().
  */
 function node_limit_uninstall() {
-	drupal_uninstall_schema('node_limit');
+  drupal_uninstall_schema('node_limit');
 }
-
-?>
\ No newline at end of file
Index: node_limit.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/node_limit/node_limit.module,v
retrieving revision 1.1
diff -u -p -r1.1 node_limit.module
--- node_limit.module	3 Mar 2009 01:58:35 -0000	1.1
+++ node_limit.module	27 Dec 2009 14:20:56 -0000
@@ -1,421 +1,627 @@
 <?php
+// $Id: node_limit.module,v 1.1.2.4 2009/04/30 00:08:54 davedelong Exp $
+
+/**
+ * @file
+ * Module to restrict the number of nodes a user or role may create.
+ */
 
 define("NODE_LIMIT_NO_LIMIT", -1);
+
 define("NODE_LIMIT_PERM_ADMIN", "administer node limits");
 
+define("NODE_LIMIT_LIMIT_DOESNT_APPLY", 0);
+define("NODE_LIMIT_LIMIT_DOES_APPLY", 1);
+define("NODE_LIMIT_LIMIT_NEUTRAL", 2);
 
 /**
- * implementation of hook_nodeapi()
+ * Implementation of hook_perm().
  */
-function node_limit_nodeapi($node, $op) {
-	global $user;
-	if ($op == 'prepare') {
-		//The node is about to be shown on the add/edit form
-		$t = $node->type;
-		$uid = ($node->nid ? $node->uid : $user->uid);
-		$limits = node_limit_limit_for_user($uid);
-		$limit = (isset($limits[$t]) ? $limits[$t] : NODE_LIMIT_NO_LIMIT);
-		$counts = node_limit_node_counts_for_user($uid);
-		if ($uid != 1) {
-			if (!$node->nid) {
-				//this is a new node
-				if ($counts[$t] >= $limit && $limit != NODE_LIMIT_NO_LIMIT) {
-					//the user is at or beyond his limit
-					drupal_set_message(t("You have already created the maximum number (!limit) of @type nodes that you are allowed", array ('@type' => $t, '!limit' => $limit)), 'error');
-					drupal_goto('node/add');
-				} else {
-					//the user hasn't reached his limit yet (assuming he has one)
-					if ($limit != NODE_LIMIT_NO_LIMIT) {
-						drupal_set_message(t("This will be node #!count out of !limit @type nodes that you may create", array('!limit' => $limit, '!count' => $counts[$t]+1, '@type' => $t)));
-					}
-				}
-			} else {
-				//this is an existing node
-				if ($uid == $user->uid) {
-					//the person is editing his own node (and isn't user:1)
-					if ($limit != NODE_LIMIT_NO_LIMIT) {
-						drupal_set_message(t("This is node #!count out of !limit @type nodes that you may create", array('!limit' => $limit, '!count' => $counts[$t], '@type' => $t)));
-					}
-				} elseif ($uid != $user->uid) {
-					//the person is editing someone else's node (that doesn't belong to user:1)
-					if ($limit != NODE_LIMIT_NO_LIMIT) {
-						$u = db_fetch_array(db_query("SELECT name FROM {users} WHERE uid='%d'", $uid));
-						$username = $u['name'];
-						drupal_set_message(t("This is node #!count out of !limit @type nodes that @user may create", array('!limit' => $limit, '!count' => $counts[$t], '@type' => $t, '@user' => $username)));
-					}
-				}
-			}
-		}
-	}
-}
-
-/**
- * implementation of hook_access()
- */
-function node_limit_access($op, $node, $account) {
-	drupal_set_message('here');
-	if ($op == 'create') {
-		$uid = $account->uid;
-		$type = $node->type;
-		$limits = node_limit_limit_for_user($uid);
-		$limit = (isset($limits[$type]) ? $limits[$type] : NODE_LIMIT_NO_LIMIT);
-		$info = db_fetch_array(db_query("SELECT COUNT(nid) as count FROM {node} WHERE uid='%d' AND type='%s'", $uid, $type));
-		if ($info['count'] >= $limit && $limit != node_limit_NO_LIMIT) { return false; }
-	}
-	return null;
+function node_limit_perm() {
+  return array(NODE_LIMIT_PERM_ADMIN);
 }
 
 /**
- * implementation of hook_theme()
+ * Implementation of hook_menu().
  */
-function node_limit_theme() {
-	return array(
-		'node_limit_admin_settings' => array(
-			'arguments' => array('form' => NULL)
-		)
-	);
-}
+function node_limit_menu() {
+  $items = array();
+  
+  $items['admin/user/node_limit'] = array(
+    'title'             => t('Node Limits'),
+    'page callback'     => 'drupal_get_form',
+    'page arguments'    => array('node_limit_list_limits'),
+    'access arguments'  => array(NODE_LIMIT_PERM_ADMIN),
+  );
+  $items['admin/user/node_limit/list'] = array(
+    'title'             => t('List'),
+    'access arguments'  => array(NODE_LIMIT_PERM_ADMIN),
+    'weight'            => -10,
+    'type'              => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['admin/user/node_limit/add'] = array(
+    'title'             => t('Add Node Limit'),
+    'page callback'     => 'drupal_get_form',
+    'page arguments'    => array('node_limit_limit_form', '0'),
+    'access arguments'  => array(NODE_LIMIT_PERM_ADMIN),
+    'type'              => MENU_LOCAL_TASK
+  );
+  $items['admin/user/node_limit/%node_limit'] = array(
+    'title'             => t('Edit Node Limit'),
+    'page callback'     => 'drupal_get_form',
+    'page arguments'    => array('node_limit_limit_form', 3),
+    'access arguments'  => array(NODE_LIMIT_PERM_ADMIN),
+    'type'              => MENU_LOCAL_TASK
+  );
+  $items['admin/user/node_limit/%node_limit/delete'] = array(
+    'title'             => t('Delete Node Limit'),
+    'page callback'     => 'drupal_get_form',
+    'page arguments'    => array('node_limit_delete_form', 3),
+    'access arguments'  => array(NODE_LIMIT_PERM_ADMIN),
+    'type'              => MENU_CALLBACK
+  );
+  $items['admin/user/node_limit/%node_limit/clone'] = array(
+    'title'             => t('Clone Node Limit'),
+    'page callback'     => 'node_limit_clone_limit',
+    'page arguments'    => array(3),
+    'access arguments'  => array(NODE_LIMIT_PERM_ADMIN),
+    'type'              => MENU_CALLBACK
+  );
 
-/**
- * implementation of hook_perm()
- */
-function node_limit_perm() {
-	return array(NODE_LIMIT_PERM_ADMIN);
+  return $items;
 }
 
 /**
- * implementation of hook_menu()
+ * Implementation of hook_theme().
+ * Register the two forms that require custom rendering.
  */
-function node_limit_menu() {
-	$items = array();
-	$items['admin/user/node_limit'] = array(
-		'title' => t('Node Limit Settings'),
-		'page callback' => 'drupal_get_form',
-		'page arguments' => array('node_limit_admin_settings'),
-		'access arguments' => array(NODE_LIMIT_PERM_ADMIN)
-	);
-	
-	return $items;
-}
-
-/**
- * theme the admin settings form so that we have tables inside fieldsets
- */
-function theme_node_limit_admin_settings($form) {
-	$types = node_get_types('types');
-	$roles = user_roles();
-	$limits = _node_limit_limits();
-	
-	$rheaders = array(
-		t('Delete'),
-		t('Role'),
-		t('Limit')
-	);
-	
-	$uheaders = array(
-		t('Delete'),
-		t('User'),
-		t('Limit')
-	);
-	foreach($types as $type) {
-		$t = $type->type;
-		$rows = array();
-		if (!empty($limits['types'][$t])) {
-			foreach ($limits['types'][$t] as $rid => $limit) {
-				$rows[] = array(
-					drupal_render($form[$t]['roles'][$rid]['delete']),
-					drupal_render($form[$t]['roles'][$rid]['name']),
-					drupal_render($form[$t]['roles'][$rid]['limit'])
-				);
-			}
-		}
-		if (isset($form[$t]['add_role'])) {
-			$rows[] = array(
-				'',
-				drupal_render($form[$t]['add_role']),
-				drupal_render($form[$t]['add_role_limit'])
-			);
-		}
-		
-		$out = theme('table', $rheaders, $rows);
-		
-		$rows = array();
-		if (!empty($limits['users'][$t])) {
-			foreach ($limits['users'][$t] as $uid => $info) {
-				$rows[] = array(
-					drupal_render($form[$t]['users'][$uid]['delete']),
-					drupal_render($form[$t]['users'][$uid]['name']),
-					drupal_render($form[$t]['users'][$uid]['limit'])
-				);
-			}
-		}
-			
-		$rows[] = array(
-			'',
-			drupal_render($form[$t]['add_user']),
-			drupal_render($form[$t]['add_user_limit'])
-		);
-		$out .= theme('table', $uheaders, $rows);
-		$form[$t]['users']['#children'] = $out;
-	}
-	return drupal_render($form);
-}
-
-/**
- * the node_limit settings form
- */
-function node_limit_admin_settings() {
-	$types = node_get_types('types');
-	$roles = user_roles();
-	$form = array();
-	$form['#tree'] = true;
-	$form['instructions'] = array(
-		'#value' => t('In each textfield, enter the number of nodes a user with that role may create.  Leave blank to use the default values')
-	);
-	
-	$limits = _node_limit_limits();
-	
-	foreach($types as $type) {
-		$r = $roles;
-		$t = $type->type;
-		$n = $type->name;
-		$form[$t] = array(
-			'#type' => 'fieldset',
-			'#title' => t('@name', array('@name' => $n)),
-			'#collapsible' => true,
-			'#collapsed' => true
-		);
-		$form[$t]['roles'] = array();
-		if (count($limits['types'][$t]) > 0) {
-			foreach($limits['types'][$t] as $rid => $limit) {
-				$form[$t]['roles'][$rid]['delete'] = array(
-					'#type' => 'checkbox'
-				);
-				$form[$t]['roles'][$rid]['name'] = array(
-					'#value' => $r[$rid]
-				);
-				$form[$t]['roles'][$rid]['limit'] = array(
-					'#type' => 'textfield',
-					'#default_value' => $limit,
-					'#size' => 4
-				);
-				unset($r[$rid]);
-			}
-		}
-		if (count($r) > 0) {
-			$form[$t]['add_role'] = array(
-				'#type' => 'select',
-				'#options' => $r
-			);
-			$form[$t]['add_role_limit'] = array(
-				'#type' => 'textfield',
-				'#size' => 4,		
-			);
-		}
-		
-		$form[$t]['users'] = array();
-		if (is_array($limits['users'][$t])) {
-			foreach($limits['users'][$t] as $uid => $info) {
-				$form[$t]['users'][$uid]['delete'] = array(
-					'#type' => 'checkbox'
-				);
-				$form[$t]['users'][$uid]['name'] = array(
-					'#value' => l($info['name'], 'user/'.$uid)
-				);
-				$form[$t]['users'][$uid]['limit'] = array(
-					'#type' => 'textfield',
-					'#default_value' => $info['limit'],
-					'#size' => 4
-				);
-			}
-		}
-		
-		$form[$t]['add_user'] = array(
-			'#type' => 'textfield',
-			'#autocomplete_path' => 'user/autocomplete',
-			'#size' => 30
-		);
-		$form[$t]['add_user_limit'] = array(
-			'#type' => 'textfield',
-			'#size' => 4,		
-		);
-	}
-		
-	$form['save'] = array(
-		'#type' => 'submit',
-		'#value' => t('Save Node Limits')
-	);
-	return $form;
-}
-
-/**
- * the validation handler for the node_limit settings form
- */
-function node_limit_admin_settings_validate($form, &$state) {
-	$types = node_get_types('types');
-	$roles = user_roles();
-	foreach($types as $type) {
-		$t = $type->type;
-		foreach($roles as $rid => $rname) {
-			//we don't allow them to enter anything less than 0 (for roles)
-			if (intval($state['values'][$t]['roles'][$rid]['limit']) < 0) {
-				form_set_error($t.'][roles]['.$rid.'][limit', t('Invalid node limit'));
-			}
-		}
-		if (trim($state['values'][$t]['add_user']) != '') {
-			$user = trim($state['values'][$t]['add_user']);
-			$info = db_fetch_array(db_query("SELECT count(uid) as count FROM {users} WHERE name='%s'", $user));
-			if ($info['count'] != 1) {
-				form_set_error($t.'][add_user', t('Invalid username'));
-			}
-			if (trim($state['values'][$t]['add_user_limit']) == '') {
-				form_set_error($t.'][add_user_limit', t('Invalid node limit'));
-			}
-		} else {
-			if (trim($state['values'][$t]['add_user_limit']) != '') {
-				form_set_error($t.'][add_user', t('Invalid username'));
-			}
-		}
-		//they may enter -1 for individual users (to override a role node_limit)
-		if (intval($state['values'][$t]['add_user_limit']) < NODE_LIMIT_NO_LIMIT) {
-			form_set_error($t.'][add_user_limit', t('Invalid node limit'));
-		}
-	}
-}
-
-/**
- * the submit handler for the node_limit settings form
- */
-function node_limit_admin_settings_submit($form, &$state) {
-	$role_sql = "INSERT INTO {node_limit_role} VALUES('%d', '%s', '%d')";
-	$user_sql = "INSERT INTO {node_limit_user} VALUES('%d', '%s', '%d')";
-	$types = node_get_types('types');
-	$roles = user_roles();
-	$limits = _node_limit_limits();
-	
-	db_query("DELETE FROM {node_limit_role} WHERE 1");
-	db_query("DELETE FROM {node_limit_user} WHERE 1");
-	foreach($types as $type) {
-		$t = $type->type;
-		$n = $type->name;
-		foreach($roles as $rid => $rname) {
-			if (intval($state['values'][$t]['roles'][$rid]['limit']) >= 0 && 
-				trim($state['values'][$t]['roles'][$rid]['limit']) != '' && 
-				$state['values'][$t]['roles'][$rid]['delete'] != 1) {
-				db_query($role_sql, $rid, $t, intval($state['values'][$t]['roles'][$rid]['limit']));
-			}
-		}
-		if (intval($state['values'][$t]['add_role']) > 0 && 
-			trim($state['values'][$t]['add_role_limit']) != '' &&
-			intval($state['values'][$t]['add_role_limit']) >= 0) {
-			db_query($role_sql, $state['values'][$t]['add_role'], $t, $state['values'][$t]['add_role_limit']);			
-		}
-		
-		if (count($limits['users'][$t]) > 0) {
-			foreach($limits['users'][$t] as $uid => $info) {
-				if (intval($state['values'][$t]['users'][$uid]['limit']) >= 0 &&
-					trim($state['values'][$t]['users'][$uid]['limit']) != '' && 
-					$state['values'][$t]['users'][$uid]['delete'] != 1) {
-					db_query($user_sql, $uid, $t, intval($state['values'][$t]['users'][$uid]['limit']));
-				}
-			}
-		}
-		if (trim($state['values'][$t]['add_user']) != '') {
-			//trying to add a user
-			$uid = db_fetch_array(db_query("SELECT uid FROM {users} WHERE name='%s'", $state['values'][$t]['add_user']));
-			$uid = $uid['uid'];
-			//there's a chance the new user might already have a limit for this type
-			db_query("DELETE FROM {node_limit_user} WHERE uid='%d' AND type='%s'", $uid, $t);
-			if (intval($uid) > 1) {
-				db_query($user_sql, $uid, $t, $state['values'][$t]['add_user_limit']);
-			} else if (intval($uid) == 1) {
-				drupal_set_message(t('Setting a node limit for user 1 has no effect and is ignored.'), 'warning');
-			}
-		}
-	}
-	
-	drupal_set_message('Your changes have been saved');
-}
-
-/**
- * internal function used to return the existing node_limits (very simply)
- */
-function _node_limit_limits() {
-	$types = node_get_types('types');
-	$roles = user_roles();
-	$limits = array();
-	$sql = "SELECT * FROM {node_limit_role}";
-	$res = db_query("SELECT * FROM {node_limit_role} ORDER BY rid ASC");
-	while($row = db_fetch_array($res)) {
-		$limits['types'][$row['type']][$row['rid']] = $row['limit'];
-	}
-	$res = db_query("SELECT nu.*, u.name FROM {node_limit_user} as nu INNER JOIN {users} as u ON (nu.uid = u.uid) ORDER BY nu.uid ASC");
-	while($row = db_fetch_array($res)) {
-		$limits['users'][$row['type']][$row['uid']] = array('limit' => $row['limit'], 'name' => $row['name']);
-	}
-	
-	return $limits;
-}
-
-/**
- * Getting the limits for a particular user
- * @param $uid the user id of the user (can be 0 = anonymous)
- * @return an array of node_limits, keyed to the nodetype
- * if the return array is empty or doesn't have a value for a particular nodetype,
- * then it will be assumed that there is no limit
- * uid:1 always returns an empty array
- */
-function node_limit_limit_for_user($uid) {
-	$user = array();
-	if ($uid == 1) { return $user; }
-	$rid = (intval($uid) >= 1 ? DRUPAL_AUTHENTICATED_RID : DRUPAL_ANONYMOUS_RID);
-	
-	//get the for all the roles that this user has
-	//if $uid == 0, this returns empty
-	$res = db_query("SELECT MAX(nlr.limit) as node_limit, nlr.type as type FROM {users} as u 
-					 INNER JOIN {users_roles} as ur ON (u.uid = ur.uid) 
-					 INNER JOIN {node_limit_role} as nlr on (nlr.rid = ur.rid) 
-					 WHERE u.uid = '%d'
-					 GROUP BY nlr.type", $uid);
-	while($row = db_fetch_array($res)) {
-		$user[$row['type']] = $row['node_limit'];
-	}
-	
-	//get the node_limits for the authenticated or anonymous role
-	//however we have to remember that -1 is "no limit"
-	$res = db_query("SELECT nlr.limit as node_limit, nlr.type as type 
-					 FROM {node_limit_role} as nlr
-					 WHERE nlr.rid = '%d'", $rid);
-	while($row = db_fetch_array($res)) {
-		$user[$row['type']] = max($user[$row['type']], $row['node_limit']);
-	}
-	
-	//an individual user limit overrides all other node_limits
-	$res = db_query("SELECT `limit` as node_limit, `type` 
-					 FROM {node_limit_user} 
-					 WHERE uid = '%d'", $uid);
-	while($row = db_fetch_array($res)) {
-		$user[$row['type']] = $row['node_limit'];
-	}
-	
-	return $user;
-}
-
-/**
- * returns the number of nodes created by this user (keyed to the node type).
- * abstracting this to a function allows for exapansion in the future
- * such as only counting nodes in a certain timeframe
- * @param $uid the user id to look up
- * @return an array of node counts
- */
-function node_limit_node_counts_for_user($uid) {
-	$user = array();
-	if ($uid == 1) { return $user; }
-	$res = db_query("SELECT `type`, COUNT(nid) as count FROM {node} WHERE uid='%d' GROUP BY type;", $uid);
-	while($row = db_fetch_array($res)) {
-		$user[$row['type']] = $row['count'];
-	}
-	return $user;
+function node_limit_theme() {
+  return array(
+    'node_limit_limit_form' => array(
+      'arguments' => array('form' => NULL)
+    ),
+    'node_limit_list_limits' => array(
+      'arguments' => array('form' => NULL)
+    )
+  );
+}
+
+/**
+ * Implementation of hook_nodeapi().
+ *
+ * This is where we'll determine if the user may create new nodes or not.
+ * We'll key off $op == 'prepare', which is sent before the edit/add form
+ * is constructed.
+ */
+function node_limit_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
+  if ($op == 'prepare') {
+    if ($node->nid == 0 && _node_limit_violates_limit($node)) {
+      //we have a violation!
+      //and this is a new node
+      $nodetypename = node_get_types('name', $node);
+      drupal_set_message(t("You can't create more content of type !type", array('!type' => $nodetypename)), 'error');
+      drupal_goto('node/add');
+    }
+  }
+}
+
+/**
+ * Helper function to check limit violations for this node.
+ * Always returns FALSE for user 1.
+ *
+ * @param $node
+ *   The node to check.
+ */
+function _node_limit_violates_limit(&$node) {
+  if ($node->uid == 1) {
+    return FALSE;
+  }
+  $limits = node_limit_limits($node);
+  foreach ($limits as $idx => $lid) {
+    $limit = node_limit_load($lid);
+    if ($limit['node_limit'] == NODE_LIMIT_NO_LIMIT) {
+      continue;
+    }
+    $sql = _node_limit_sql($limit['lid']);
+    $count = db_result(db_query($sql));
+    if ($count >= $limit['node_limit']) {
+      return TRUE;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Generates the sql statement to find the nodes that apply to a particular limit.
+ * Modules that implement hook_node_limit_sql() should sprintf their arguments
+ * into the returned array.
+ * This will be changed in Drupal 7, which will be able to accept an array of
+ * arguments to db_query().
+ *
+ * @param $lid
+ *   Identifier of limit rule.
+ */
+function _node_limit_sql($lid) {
+  $sql = "SELECT COUNT(n.nid) AS number FROM {node} AS n";
+  $components = module_invoke_all('node_limit_sql', $lid);
+  $limit = node_limit_load($lid);
+  
+  $joins  = (isset($components['join']) ? implode(' ', $components['join']) : '');
+  $wheres = (isset($components['where']) ? 'WHERE ' . implode(' AND ', $components['where']) : '');
+
+  return $sql . ' ' . $joins . ' ' . $wheres;
+}
+
+/**
+ * Returns all the limits that can be applied to a specific node.
+ *
+ * @param $node
+ *   The node object that may be limited.
+ */
+function node_limit_limits(&$node) {
+  $user = user_load($node->uid);
+  //get all the limits:
+  $res = db_query("SELECT lid FROM {node_limit} ORDER BY weight ASC");
+  $applicable_limits = array();
+  while ($l = db_fetch_array($res)) {
+    // This particular limit id.
+    $lid = $l['lid'];
+    $applies = TRUE;
+
+    $submodule_applies = module_invoke_all('node_limit_applies_in_context', $lid, $node, $user);
+    foreach ($submodule_applies as $module => $module_applies) {
+      // A submodule returns DOESNT_APPLY if it requires a specific user or role, etc,
+      // and the context given does not satisfy that.
+      if ($module_applies == NODE_LIMIT_DOESNT_APPLY) {
+        $applies = FALSE;
+      }
+    }
+
+    if ($applies == TRUE) {
+      $applicable_limits[] = $lid;
+    }
+  }
+
+  return $applicable_limits;
+}
+
+/**
+ * Theme the node limit list form.
+ */
+function theme_node_limit_list_limits($form) {
+  $header = array(
+    t('Title'),
+    t('Limit'),
+    t('Weight'),
+    t('Actions')
+  );
+  $rows = array();
+  foreach (element_children($form['limits']) as $lid) {
+    $row = array();
+    $form['limits'][$lid]['weight']['#attributes']['class'] = 'node_limit-weight';
+    $row[] = drupal_render($form['limits'][$lid]['title']);
+    $row[] = drupal_render($form['limits'][$lid]['node_limit']);
+    $row[] = drupal_render($form['limits'][$lid]['weight']);
+    $row[] = drupal_render($form['limits'][$lid]['actions']);
+    $rows[] = array(
+      'data' => $row,
+      'class' => 'draggable'
+    );
+  }
+
+  if (count($rows) > 0) {
+    $out = theme('table', $header, $rows, array('id' => 'node_limit-table'));
+  }
+  $out .= drupal_render($form);
+  drupal_add_tabledrag('node_limit-table', 'order', 'sibling', 'node_limit-weight');
+  return $out;
+
+}
+
+/**
+ * Form for listing the created limits.
+ * Created as a form so that the user can adjust the weight.
+ */
+function node_limit_list_limits() {
+  $weights = array();
+  for ($i = -10; $i <= 10; $i++) {
+    $weights[$i] = $i;
+  }
+
+  $form = array('#tree' => TRUE);
+  $form['limits'] = array();
+  $sql = "SELECT * FROM {node_limit} ORDER BY weight ASC";
+  $res = db_query($sql);
+  $limit_count = 0;
+  while ($rec = db_fetch_array($res)) {
+    $limit_count++;
+    $form['limits'][$rec['lid']]['weight'] = array(
+      '#type' => 'select',
+      '#options' => $weights,
+      '#default_value' => $rec['weight']
+    );
+    $form['limits'][$rec['lid']]['title'] = array(
+      '#value' => $rec['title']
+    );
+    $form['limits'][$rec['lid']]['node_limit'] = array(
+      '#value' => $rec['node_limit']
+    );
+    $form['limits'][$rec['lid']]['actions'] = array(
+      '#value' => l(t('Edit'),   'admin/user/node_limit/' . $rec['lid']) . ' '. 
+                  l(t('Delete'), 'admin/user/node_limit/' . $rec['lid'] . '/delete') . ' ' .
+                  l(t('Clone'),  'admin/user/node_limit/' . $rec['lid'] . '/clone')
+    );
+  }
+
+  if ($limit_count > 0) {
+    $form['save'] = array(
+      '#type'  => 'submit',
+      '#value' => t('Save Limits')
+    );
+  }
+  else {
+    $form['create'] = array(
+      '#value' => l(t('Add a new node limit'), 'admin/user/node_limit/add')
+    );
+  }
+
+  return $form;
+}
+
+/**
+ * Save the module weights.
+ */
+function node_limit_list_limits_submit($form_id, &$form_state) {
+  $node_limit = new stdClass();
+  
+  foreach ($form_state['values']['limits'] as $lid => $info) {
+    $node_limit->lid    = $lid;
+    $node_limit->weight = $info['weight'];
+    
+    drupal_write_record('node_limit', $node_limit, array('lid'));
+  }
+  
+  drupal_set_message(t('Limits saved!'));
+}
+
+/**
+ * Theme the admin settings form so that we have tables inside fieldsets.
+ */
+function theme_node_limit_limit_form($form) {
+  if (!isset($form['info'])) {
+    return;
+  }
+  $rows = array();
+
+  foreach ($form['node_limit_elements'] as $module => &$element) {
+    if (is_array($element['applies']) && is_array($element['element'])) {
+      if ($element['element']['#custom_render'] === TRUE && module_hook($module, 'node_limit_render_element')) {
+        // We can't use module_invoke because form elements must be passed byref.
+        $func = $module .'_node_limit_render_element';
+        $rendered_element = $func($element['element']);
+      }
+      else {
+        $rendered_element = drupal_render($element['element']);
+      }
+      $rows[] = array(
+        drupal_render($element['applies']),
+        $rendered_element
+      );
+    }
+  }
+
+  $output = drupal_render($form['info']);
+  if (count($rows) > 0) {
+    $output .= theme('table', array(t('Applies to:'), ''), $rows);
+  }
+  $output .= drupal_render($form);
+  return $output;
+}
+
+/**
+ * The node_limit settings form.
+ */
+function node_limit_limit_form(&$form_state, $limit) {
+  if ($limit == FALSE) {
+    $limit = array();
+  }
+  $form = array();
+  $form['#tree'] = TRUE;
+  // Set LID only for already avaiable limit
+  if ($limit['lid'] > 0) {
+    $form['lid'] = array(
+      '#type'         => 'hidden',
+      '#value'        => $limit['lid'],
+    );
+  }
+
+  $form['info']['title'] = array(
+    '#type'           => 'textfield',
+    '#title'          => t('Description'),
+    '#description'    => t('The description for this Node Limit'),
+    '#default_value'  => $limit['title'],
+    '#required'       => TRUE,
+  );
+  $form['info']['node_limit'] = array(
+    '#type'           => 'textfield',
+    '#title'          => t('Limit'),
+    '#description'    => t('The number of nodes for this limit.  Must be an integer greater than 0 or %nolimit for no limit', array('%nolimit' => NODE_LIMIT_NO_LIMIT)),
+    '#default_value'  => (isset($limit['node_limit']) ? $limit['node_limit'] : NODE_LIMIT_NO_LIMIT),
+    '#size'           => 10,
+    '#required'       => TRUE,
+  );
+  $form['info']['weight'] = array(
+    '#type'           => 'hidden',
+    '#value'          => $limit['weight'],
+  );
+
+  $elements = module_invoke_all('node_limit_element', $limit['lid']);
+  
+  $form['node_limit_elements'] = array();
+  
+  foreach ($elements as $module => $element) {
+    $form['node_limit_elements'][$module]['applies'] = array(
+      '#type'           => 'checkbox',
+      '#title'          => $element['#title'],
+      '#default_value'  => isset($limit[$module]),
+    );
+    $element['#title'] = '';
+    $form['node_limit_elements'][$module]['element'] = $element;
+  }
+
+  $form['save'] = array(
+    '#type'   => 'submit',
+    '#value'  => ($limit['lid'] > 0 ? t('Edit Node Limit') : t('Add Node Limit')),
+  );
+  
+  return $form;
+}
+
+/**
+ * Validation hook for node_limit_limit_form.
+ *
+ * Allows submodules that are applied to validate their own input.
+ */
+function node_limit_limit_form_validate($form_id, &$form_state) {
+  if (!is_numeric($form_state['values']['info']['node_limit'])) {
+    form_set_error('info][node_limit', t('Node limits must be an integer'));
+  }
+  else if (intval($form_state['values']['info']['node_limit']) != floatval($form_state['values']['info']['node_limit'])) {
+    form_set_error('info][node_limit', t('Node limits must be an integer'));
+  }
+  else if (intval($form_state['values']['info']['node_limit']) < NODE_LIMIT_NO_LIMIT) {
+    form_set_error('info][node_limit', t('Node limits cannot be less that %nolimit', array('%nolimit' => NODE_LIMIT_NO_LIMIT)));
+  }
+
+  if (trim($form_state['values']['info']['title']) == '') {
+    form_set_error('info][title', t('Invalid Node Limit title'));
+  }
+
+  foreach ($form_state['values']['node_limit_elements'] as $module => $element) {
+    if ($element['applies'] === 1) {
+      // They checked the box!
+      $result = module_invoke($module, 'node_limit_element_validate', $element['element']);
+      if (is_array($result) && isset($result['error'])) {
+        $path = $module .'][element';
+        if (isset($result['element'])) {
+          $path .= ']['. $result['element'];
+        }
+        form_set_error('node_limit_elements]['. $path, $result['error']);
+      }
+    }
+  }
+}
+
+/**
+ * Submission hook for node_limit_limit_form.
+ *
+ * Calls the submission hook on applied submodules to allow them to save their data.
+ */
+function node_limit_limit_form_submit($form_id, &$form_state) {
+  // Inizialize new limit
+  $limit = array();
+  
+  // Check if limit is update or new
+  if (isset($form_state['values']['lid'])) {
+    $limit['lid'] = $form_state['values']['lid'];
+  }
+  
+  // Set value for this limit
+  $limit['node_limit'] = intval($form_state['values']['info']['node_limit']);
+  $limit['title']      = $form_state['values']['info']['title'];
+  $limit['weight']     = $form_state['values']['info']['weight'];
+  
+  // Read data for other modules
+  foreach ($form_state['values']['node_limit_elements'] as $module => $element) {
+    if ($element['applies']) {
+      $limit[$module] = $element['element'];
+    }
+  }
+  
+  // Save new limit
+  node_limit_save($limit);
+
+  // Redirect user to new page
+  $form_state['redirect'] = 'admin/user/node_limit';
+  
+  // Notify to user creation of new limit
+  drupal_set_message(t('Saved limit "%limit"', array('%limit' => $limit['title'])));
+}
+# REFACTORING
+/*
+function node_limit_limit_form_submit($form_id, &$form_state) {
+  if (isset($form_state['values']['lid'])) {
+    $lid = $form_state['values']['lid'];
+  }
+  else {
+    // TODO: Remove this and use drupal_write_record with or without update array.
+    $lid = _node_limit_next_limit_id();
+  }
+  $limit = array();
+  $limit['lid'] = $lid;
+  $limit['node_limit'] = intval($form_state['values']['info']['node_limit']);
+  $limit['title'] = $form_state['values']['info']['title'];
+  $limit['weight'] = $form_state['values']['info']['weight'];
+
+  foreach ($form_state['values']['node_limit_elements'] as $module => $element) {
+    if ($element['applies']) {
+      $limit[$module] = $element['element'];
+    }
+  }
+  
+  // Save news limit
+  node_limit_save($limit);
+
+  $form_state['redirect'] = 'admin/user/node_limit';
+  
+  // Notify to user creation of new limit
+  drupal_set_message(t('Saved limit "%limit"', array('%limit' => $limit['title'])));
+}
+*/
+
+/**
+ * Confirmation form to delete a node limit.
+ */
+function node_limit_delete_form(&$form_state, $limit) {
+  if ($limit == FALSE) {
+    drupal_goto('admin/user/node_limit');
+  }
+  $form = array(
+    'lid' => array(
+      '#type' => 'hidden',
+      '#value' => $limit['lid']
+    )
+  );
+  return confirm_form($form, t('Are you sure you want to delete %name?', array('%name' => $limit['title'])), 'admin/user/node_limit');
+}
+
+/**
+ * Submission hook for node limit deletion.
+ */
+function node_limit_delete_form_submit($form_id, &$form_state) {
+  $lid = $form_state['values']['lid'];
+  node_limit_delete($lid);
+  $form_state['redirect'] = 'admin/user/node_limit';
+}
+
+/**
+ * Callback to clone a limit.
+ */
+function node_limit_clone_limit($limit) {
+  // Save old title
+  $old_title = $limit['title'];
+  
+  // Remove LID (generate new LID)
+  unset($limit['lid']);
+  
+  // Set news title
+  $limit['title'] = t('Clone of !title', array('!title' => $old_title));
+  
+  // Save new lilmit with this data
+  node_limit_save($limit);
+  
+  // Notify to user creation of new limit
+  drupal_set_message(t('Cloned limit "%limit"', array('%limit' => $old_title)));
+  drupal_goto('admin/user/node_limit');
+}
+#REFACTORING
+/*
+function node_limit_clone_limit($limit) {
+  $old_title = $limit['title'];
+  $limit['lid'] = _node_limit_next_limit_id();
+  $limit['title'] = t('Clone of !title', array('!title' => $old_title));
+  node_limit_save($limit);
+  drupal_set_message(t('Cloned limit "%limit"', array('%limit' => $old_title)));
+  drupal_goto('admin/user/node_limit');
+}
+*/
+
+/**
+ * Helper function to get the next available node limit id.
+ */
+# REFACTORING
+/*
+function _node_limit_next_limit_id() {
+  $next_lid = db_result(db_query("SELECT (MAX(lid)+1) AS lid FROM {node_limit}"));
+  return max($next_lid, 1);
+}
+*/
+
+/**
+ * Loads a node limit.
+ *
+ * @param $lid
+ *   The limit id.
+ * @return
+ *   FALSE if the limit couldn't be loaded; otherwise the limit rule.
+ */
+function node_limit_load($lid) {
+  if (!is_numeric($lid)) {
+    return FALSE;
+  }
+  $info = db_fetch_array(db_query("SELECT * FROM {node_limit} WHERE lid = %d", $lid));
+  if ($info['lid'] == $lid && intval($lid) >= 0) {
+    // load up the information from the other modules
+    // perhaps this isn't necessary.  does node_limit ever use the other modules info?
+    // yes (for setting the default state of the "applies" checkbox when editing a limit)
+    $res = module_invoke_all('node_limit_load', $lid);
+    return array_merge($info, $res);
+  }
+  else {
+    return FALSE;
+  }
+}
+
+/**
+ * Callback to delete a node limit from the database.
+ */
+function node_limit_delete($lid) {
+  db_query("DELETE FROM {node_limit} WHERE lid = %d", $lid);
+  
+  module_invoke_all('node_limit_delete', $lid);
+}
+
+/**
+ * Callback to save a node limit back to the database.
+ */
+function node_limit_save($limit) {
+  $update = array();
+  $node_limit = new stdClass();
+  
+  if($limit['lid']) {
+    $update = array('lid');
+    $node_limit->lid = $limit['lid'];
+  }
+  
+  $node_limit->node_limit = $limit['node_limit'];
+  $node_limit->title      = $limit['title'];
+  $node_limit->weight     = $limit['weight'];
+  
+  drupal_write_record('node_limit', $node_limit, $update);
+  
+  $modules = module_implements('node_limit_save');
+  foreach ($modules as $module) {
+    module_invoke($module, 'node_limit_save', $node_limit->lid, isset($limit[$module]), $limit[$module]);
+  }
+}
+# REFACTORING!
+/*
+function node_limit_save($limit) {
+  node_limit_delete($limit['lid']);
+  $update = $limit['lid'] ? array('lid') : array();
+
+  $node_limit = new stdClass();
+  
+  $node_limit->lid        = $limit['lid'];
+  $node_limit->node_limit = $limit['node_limit'];
+  $node_limit->title      = $limit['title'];
+  $node_limit->weight     = $limit['weight'];
+  
+  drupal_write_record('node_limit', $node_limit);
+  
+  $modules = module_implements('node_limit_save');
+  foreach ($modules as $module) {
+    module_invoke($module, 'node_limit_save', $node_limit->lid, isset($limit[$module]), $limit[$module]);
+  }
 }
-
-?>
\ No newline at end of file
+*/
