This module idea is a rewrite of another module idea I had and posted at http://drupal.org/node/52582

It also came to light that there are several modules in some state or another that implement a similar concept. Here is a review

So now I offer this module called nodelimit which I think (I hope) incorporates most of the ideas of all of them into one.

The code was also written with an eye towards further enhancement. There is a 'mode' value (the current modes are 'No limits', 'One limit applied to all users' and 'Multiple limits applied by permissions'). The code performs a switch statement on the 'mode', so if someone had other idea for another 'mode', the idea could be incorporated easily by adding new case statements in each of the modules functions to handle the 'mode' specific logic. Along with the 'mode' value is the 'quantity' value that could be anything needed to support the new 'mode' (with the current modes as examples: 'One limit applied to all users' accepts only a single number and 'Multiple limits applied by permissions' accepts a comma separated list of numbers). And lastly there is the anonymous user exception value (true or false) which allows anonymous user override of a limit.

So if someone wanted to incorporate this with user points or e-commerce, this module shouldn't be too difficult for a coder to build on. I had commented earlier, that I think some sort of infrastructure that limits node creation at the individual user level would require fairly different logic than these (bioesque, noderestrict, usernodes and bio) modules provide. I had that thought in the bakc of my head as I was writing this code, so I think (or I hope) the logic needed for a new 'mode' idea could easily be incorporated into this module.

To install - copy the code to your text editor, save as nodelimit.module. Create a nodelimit directory in your modules directory and upload file to the new directory. Note when copying, remember to remove any whitespace after the closing ?> php tag at the end of the file.

Would certainly appreciate any thoughts, comments and suggestions.

/**
 * Global values to define node limit mode
 */
define('NODELIMIT_NONE', 0);
define('NODELIMIT_SIMPLE', 1);
define('NODELIMIT_PERM', 2);

/* Implementation of hook_help...
 */
function nodelimit_help($section) { 
  switch ($section) { 
    case 'admin/modules#description': 
      // admin/modules description
      $out = 'Provides the ability to limit node creation (authoring). The settings are configurable for each node type.';
      return t($out); 
    case 'admin/help#nodelimit':
      // admin/help - overall
      // basic description of module and link to help text
      $out = '<p>This module provides the ability to limit the number of nodes your users may create (or author). ';
      $out .= 'The necessary settings are configurable for each node type.</p>';
      
      $out .= '<fieldset><legend>Overview</legend>';
      $out .= '<p>The module settings page (at %module-settings-link) provides access to both the global module settings and each node types specific settings. ';
      $out .= ' There is a short note about the current node limit settings for each type presented on the node types workflow page.</p>';
      $out .= '<p>Some interesting and useful capabilities may be provided by this module. Some thoughts to consider before implementing any sort of limit.</p>';
      $out .= '<ul>';
      $out .= ' <li>Limiting users to creating only one node may provide an interesting extention to user specific information differently than user profiles currently allow.</li>';
      $out .= ' <li>With any limit, you should consider how your users may react.  When a users limit is reached ... ';
      $out .= '  <ul>';
      $out .= '   <li>will there be a tendency to stop contributing content ?</li>';
      $out .= '   <li>or maybe a tendency to overwrite existing nodes ?';
      $out .= '    <ul>';
      $out .= '     <li>this could lead to a loss of historical information,</li>';
      $out .= '     <li>or, this may help keep a sites information more current and relevant.</li>';
      $out .= '    </ul>';
      $out .= '   </li>';
      $out .= '  </ul>';
      $out .= ' </li>';
      $out .= '</ul>';
      $out .= '<p>Unless otherwise noted, the following points are true for all the Node Limit Options outlined in the next section.</p>';
      $out .= '<dl>';
      $out .= '<dt>Node Data</dt>';
      $out .= ' <dd>When a setting is made or changed, no changes to node data are made. ';
      $out .= '  You made need to change the author of existing nodes to agree with your desired setting. ';
      $out .= '  A review of users exceeding an established limit is provided on the bottom of the same page where the settings are made.</dd>';
      $out .= '<dt>Redirection</dt>';
      $out .= ' <dd>After a user has created his/her limit of nodes, the create option will still be listed on the create content page. ';
      $out .= '  When a user attempts to create an addtional node, the user will be redirected to either<br/> ';
      $out .= '  (1) a page displaying a list of the users nodes, or<br/>(2) the node page of the users node (if only one node is allowed).</dd>';
      $out .= '<dt>Administer Nodes Permission</dt>';
      $out .= ' <dd>Users with administer nodes permission are able to create nodes for other users. ';
      $out .= '  These users will not be able to change any part of a node (title, teaser, etc.) if the indicated author of the node has more than his/her allowed limit.</dd>';
      $out .= '<dt>Exception Setting</dt>';
      $out .= ' <dd>Regarding the Exception setting: This setting allows the anonymous user to create nodes beyond any limit setting. ';
      $out .= '  If your anonymous user account has permission to post nodes, you will need to allow this exception. ';
      $out .= '  This setting may also be helpful if you have a situation where you need to create additional nodes, unrelated to a specific user. ';
      $out .= '  The downside of this abiltiy is that the actual node author of these nodes will not be known.</dd>';
      $out .= '</dl>';
      $out .= '</fieldset>';
      
      $out .= '<fieldset><legend>Node Limit Options</legend>';
      $out .= '<p>The choices for Node Limit Option are as follows:</p>';
      $out .= '<p><strong>No limit</strong><br/>';
      $out .= ' provides no limits on node creation</p>';
      $out .= '<ul>';      
      $out .= ' <li><strong>Quantity</strong>: n/a</li>';
      $out .= ' <li><strong>Exception</strong>: n/a</li>';
      $out .= '</ul>';
      $out .= '<p><strong>One limit applied to all users</strong><br/>';
      $out .= ' provides simple abiltiy to apply a single limit to all your users.  This option applies the limit to user 1.</p>';
      $out .= '<ul>';
      $out .= ' <li><strong>Quantity</strong>: the number of nodes each user is limited to creating (or authoring).</li>';
      $out .= ' <li><strong>Exception</strong>: no exceptions or anonymous user<br/>';
      $out .= '</ul>';
      $out .= '<p><strong>Mutliple limits applied by permission</strong><br/>';
      $out .= ' provides ability to create multiple limits and apply those limits to your users with user roles.  This option does not apply to user 1.</p>';
      $out .= '<ul>';
      $out .= ' <li><strong>Quantity</strong>: a comma separated list of numbers representing the different possible limits (example: 50,25,10). Enter the list of numbers in descending order (highest to lowest).<br/>';
      $out .= '  <ul>';
      $out .= '   <li>A new permission for each number on the list will be created. ';
      $out .= '    The new permissions can then be assigned to your users via %access-control-link.</li>';
      $out .= '   <li>For example: with a quantity of 50,25,10 the following permissions are created:<br/>';
      $out .= '    - nodetype limit 50<br/>';
      $out .= '    - nodetype limit 25<br/>';
      $out .= '    - nodetype limit 10';
      $out .= '   </li>';
      $out .= '   <li>If a user has permission to create nodes but has none of the nodetype limit permissions, the user is allowed to create unlimited nodes.</li>';
      $out .= '   <li>If a user is assigned more than one of the nodetype limit permissions, the limit first in order is the one that is applied to the user.</li>';
      $out .= '  </ul>';
      $out .= ' <li><strong>Exception</strong>: no excpetions or anonymous user<br/>';
      $out .= '</ul>';
      $out .= '</fieldset>';
      
      $out .= '<fieldset><legend>Reviewing Node Data</legend>';
      $out .= '<p>As noted above, when a setting is made or changed, no changes to node data are made. ';
      $out .= 'You may need to change the author of existing nodes to agree with your desired setting. ';
      $out .= 'A review of users exceeding an established limit is generally provided on the bottom of the same page where the settings are made.</p>';
      $out .= '</fieldset>';

      $out .= '<fieldset><legend>Global Module Settings</legend>';
      $out .= '<p>This module has 2 settings, both in regards to displaying a menu on the user account page. The settings are as follows:</p>';
      $out .= '<p><strong>1. Display on user account page</strong>, the options are:</p>';
      $out .= '<ul>';
      $out .= ' <li><strong>No</strong><br/>';
      $out .= '  default, no additional information is provided with the user account page</li>';
      $out .= ' <li><strong>Yes</strong>, with all user accounts (for users with permission to access user profiles)<br/>';
      $out .= '  provides additional information on user account page</li>';
      $out .= '  <ul>';
      $out .= '   <li>a horizontal menu (tab) will appear on the user account page</li>';
      $out .= '   <li>if multiple node types are limited, each node type will have a submenu (sub tab)</li>';
      $out .= '   <li>menu items only seen by users with permission to create a node of the type</li>';
      $out .= '   <li>the information displayed includes:';
      $out .= '    <ul>';
      $out .= '     <li>node name</li>';
      $out .= '     <li>create content description for the node type</li>';
      $out .= '     <li>if the user of the account being viewed has created nodes of the type, a list of the nodes (title, created and changed information), with links to view and/or edit the node as appropriate.</li>';
      $out .= '     <li>if the user of the account being viewed is the account of the logged-in user, a link to create a node of the type if limit allows.</li>';
      $out .= '    </ul>';
      $out .= '   </li>';
      $out .= '  </ul>';
      $out .= ' </li>';
      $out .= ' <li><strong>Yes</strong>, but only with the user account of the logged-in user<br/>';
      $out .= '  very similar to above, except that the menu will only appear when the logged-in user is viewing his/her own account.</li>';
      $out .= '</ul>';
      $out .= '<p><strong>2. Menu Title:</strong></p>';
      $out .= '<p>With a yes option above, you should enter a short title for the menu. ';
      $out .= ' Suggestions:  If you have only one node type with a limit, use the name of the node type. ';
      $out .= ' If you have multiple node types with limits, you should use a more global title that describes all of them, ';
      $out .= ' such as: content, bio, special, my items, etc.';
      $out .= '</p>';
      $out .= '</fieldset>';
      
      $links = array();
      $links['%module-settings-link'] = l('admin/settings/nodelimit', 'admin/settings/nodelimit');
      $links['%access-control-link'] = l('admin - access control', 'admin/access');
      return t($out, $links); 
  }
} 

/**
 * Implementation of hook_perm...
 */
function nodelimit_perm() { 
  $perms = array();
  $types = node_list();
  foreach ($types as $type) {
    if (variable_get('nodelimit_mode_'. $type, NODELIMIT_NONE) == NODELIMIT_PERM) {
      $nodelimit_quantity = variable_get('nodelimit_quantity_'. $type, 0);
      $quantities = explode(',', $nodelimit_quantity);
      $node_name = node_invoke($type, 'node_name');
      foreach ($quantities as $quantity) {
        $perms[] = t('%node-name limit %quantity', array('%node-name'=>$node_name, '%quantity'=>$quantity));
      }
    }
  }
  return $perms;
}

/**
 * Implementation of hook_settings...
 */
function nodelimit_settings() { 
  // this implementation is a little different than a standard implementation in that it provides for some administration of nodes
  
  // basic description of module and link to help text
  $grp = 'This module provides the ability to limit the number of nodes your users may create (or author). ';
  $grp .= 'The necessary settings are configurable for each node type. ';
  $grp .= 'Please read the %help-link section for this module.';
  $output = '<p>'. t($grp, array('%help-link'=>l(t('help'), 'admin/help/nodelimit'))) .'</p>';
  
  // review of current limitation settings
  // this part of the implementation of this hook is the non standard part 
  // a list of all node types and current limitation settings
  // this section is enclosed in fieldset tags to offset it from the module settings
  $header = array(t('node type'), t('current setting'));
  $rows = array();
  $types = node_list();
  foreach ($types as $type) {
    $node_name = node_invoke($type, 'node_name');
    $nodelimit_mode = variable_get('nodelimit_mode_'. $type, NODELIMIT_NONE);
    $nodelimit_quantity = variable_get('nodelimit_quantity_'. $type, 0);
    $nodelimit_anon = variable_get('nodelimit_anon_'. $type, 0);
    switch ($nodelimit_mode) {
      case NODELIMIT_NONE:
        $current = t('No limit');
      break;
      case NODELIMIT_SIMPLE:
        $current =  t('%quantity node per user limit', array('%quantity'=>$nodelimit_quantity));
        $current .= $nodelimit_anon == 0 ? '' : t(' except for the anonymous user');
      break;
      case NODELIMIT_PERM:
        $current = t('Multiple limits by permission');
        $current .= $nodelimit_anon == 0 ? '' : t(' except for the anonymous user');
      break;
    }
    $rows[] = array(l($node_name, 'admin/settings/nodelimit/'. $type .'/0'), $current);
  }
  if (count($rows)) {
    $grp = 'Below is a list of node types and their current setting. ';
    $grp .= 'Node type is a link to a form where you may configure the settings. ';
    $grp = '<p>'. t($grp) .'</p>';
    $grp .= theme('table', $header, $rows);
  }
  else {
    $grp = '<p>'. t('No node types were found.') .'</p>';
  }
  $output .= '<fieldset><legend>'. t('Node Types and Limits') .'</legend>'. $grp .'</fieldset>';
  
  // module configuration
  // this part of the implementation of this hook is fairly standard
  $output .= '<h2>'. t('Nodelimit Module Configuration') .'</h2>';
  // there are 2 settings
  // (1) setting to indicate if a menu should appear on the user account page
  $grp = '<p>'. t('This module has a few module specific settings. Please note: changing these settings has no impact on the <strong>One Node Per User Limit</strong> settings above.') .'</p>';
  $desc = 'With a yes option, a horizontal menu (tab) will appear on the user account page.  ';
  $desc .= 'If you have multiple node types with a limit, each type will have a submenu (sub tab). ';
  $desc .= 'These menu items will appear only if the logged-in user has permission to create the node type. ';
  $desc .= 'Displayed on the page is the name of the node type along with its create content description, and:<ul>';
  $desc .= '<li>a list of the node(s) created (authored) by the user include: the title, created and updated information with a link to view the node</li>';
  $desc .= '<li>if the user being viewed is the account of the logged-in user, a link to create a node if the users limit has not been reached</li>';
  $desc .= '</ul>';
  $desc = '<p>'. t($desc) .'</p>';
  $options = array(0 => t('No'), 1 => t('Yes, with all user accounts (for users with permission to access user profiles)'), 2 => t('Yes, but only with the user account of the logged-in user'));
  $grp .= form_radios(t('Display on user account page'), 'nodelimit_menu', variable_get('nodelimit_menu', 0), $options, $desc);
  // (2) setting to indicate title of menu
  $desc = 'With a yes option above, you should enter a short title for the menu. ';
  $desc .= 'Suggestions:  If you have only one node type with a limit, use the name of the node type. ';
  $desc .= 'If you have multiple node types with a limit, you should use a more global title that describes all of them, ';
  $desc .= 'such as: content, bio, special, my items, etc.';
  $desc = '<p>'. t($desc) .'</p>';
  $grp .= form_textfield(t('Menu title'), 'nodelimit_menu_title', variable_get('nodelimit_menu_title', 'content'), 25, 25, $desc, NULL, TRUE);
  $output .= $grp;
  
  // return the output
  return $output;
} 

/**
 * Implementation of hook_menu...
 */
function nodelimit_menu($may_cache) { 
  global $user;
  $items = array(); 

  if ($may_cache) { 
  }
  else {
    if (arg(0) == 'admin' && arg(1) == 'settings' && arg(2) == 'nodelimit' && arg(3) != '') {
      $type = arg(3);
      $limit = is_numeric(arg(4)) ? arg(4) : 0;
      $uid = is_numeric(arg(5)) ? arg(5) : NULL;
      $items[] = array('path' => 'admin/settings/nodelimit/admin', 
        'title' => 'module settings', 
        'type' =>  MENU_DEFAULT_LOCAL_TASK,
        );
      $items[] = array('path' => 'admin/settings/nodelimit/'. $type .'/'. $limit, 
        'title' => 'node type settings', 
        'callback' => 'nodelimit_admin_callback',
        'callback arguments' => array($type, $limit, $uid),
        'type' =>  MENU_LOCAL_TASK,
        'weight'=> 2,
        );
    }
    if (arg(0)=='user' && is_numeric(arg(1))) {
      $nodelimit_menu = variable_get('nodelimit_menu', 0);
      if ($nodelimit_menu > 0) {
        $uid = arg(1);
        if (($nodelimit_menu == 1) || ($nodelimit_menu == 2 && $uid == $user->uid)) {
          $title = variable_get('nodelimit_menu_title', 'content');
          $menus = 1;
          $types = node_list();
          foreach ($types as $type) {
            if (node_access('create', $type) && variable_get('nodelimit_mode_'. $type, 0) > 0) {
              $node_name = node_invoke($type, 'node_name');
              if ($menus == 1) {
                $items[] = array('path' => 'user/'. $uid .'/nodelimit', 
                  'title' => $title, 
                  'callback' => 'nodelimit_user_callback',
                  'callback arguments' => array($type, $uid),
                  'weight' => 5,
                  'type' =>  MENU_LOCAL_TASK,
                  );
                $items[] = array('path' => 'user/'. $uid .'/nodelimit/'. $type, 
                  'title' => $node_name, 
                  'type' =>  MENU_DEFAULT_LOCAL_TASK,
                  );
              }
              else {
                $items[] = array('path' => 'user/'. $uid .'/nodelimit/'. $type, 
                  'title' => $node_name, 
                  'callback' => 'nodelimit_user_callback',
                  'callback arguments' => array($type, $uid),
                  'type' =>  MENU_LOCAL_TASK,
                  );
              }
              $menus++;
            }
          }
        }
      }
    }
    if (arg(0) == 'nodelimit' && is_numeric(arg(1)) && arg(2) != '') {
      $uid = arg(1);
      $type = arg(2);
      if (node_access('create', $type) && variable_get('nodelimit_mode_'. $type, 0) > 0) {
        $node_name = node_invoke($type, 'node_name');
        $items[] = array('path' => 'nodelimit/'. $uid .'/'. $type, 
          'title' => $title, 
          'callback' => 'nodelimit_user_callback',
          'callback arguments' => array($type, $uid),
          'access' => user_access('access content'),
          );
      }
    }
  } 
  return $items; 
} 

/**
 * Menu callback:  presents a list of nodes of a type by an user in a themed/paged table.
 *   this callback provides the content for the nodelimit pages and the additional user account pages
 *
 * @param $type
 *   the node type
 * @param $uid
 *   the id of an user
 */
function nodelimit_user_callback($type, $uid) {
  global $user;
  // get limit values
  $nodelimit_mode = variable_get('nodelimit_mode_'. $type, 0);
  $nodelimit_quantity = variable_get('nodelimit_quantity_'. $type, 0);
  $nodelimit_anon = variable_get('nodelimit_anon_'. $type, 0);
  // get node name  
  $node_name = node_invoke($type, 'node_name');
  
  // output the node name and the node create content text
  $output .= "<h2>'". $node_name ."' posts</h2>";
  $output .= '<ul>';
  $output .= '<li>'. implode("\n", module_invoke_all('help', 'node/add#'. $type)) .'</li>';
  // additional output: a create content link, user limit note, etc.
  if ($user->uid == $uid) {
    // if the $uid == the logged-in user
    // outputs based on mode...
    switch ($nodelimit_mode) {
      case NODELIMIT_NONE:
        // unlimited post
        // fall thru ...
      case NODELIMIT_SIMPLE:
        // this mode has one limit for all users
        // fall thru ...
      case NODELIMIT_PERM:
        // this mode has multiple limits, get users limit
        $user_limit = nodelimit_user_limit($uid, $type);
      
        switch ($user_limit) {
          case 0:
            // indicates unlimited, no additional note
          break;
          case 1:
            $output .= '<li>'. t("Your permissions allow you to create one '%node-name' post.", array('%node-name'=>$node_name)) .'</li>';
          break;
          default;
            $output .= '<li>'. t("Your permissions allow you to create %user-limit '%node-name' posts.", array('%node-name'=>$node_name, '%user-limit'=>$user_limit)) .'</li>';
        }
        // get users node count
        $user_node_count = nodelimit_user_node_count($uid, $type);
        if ($user_limit == 0 || $user_node_count < $user_limit) {
          // provide link to create a node of the type if user's limit has not been reached
          $output .= '<li>'. l(t('create %node-name', array('%node-name'=>$node_name)), 'node/add/'. $type) .'</li>';
        }
        elseif ($user_node_count > $user_limit) {
          // provide note informing node count currently exceeds limit
          $output .= '<li>'. t("You currently have more '%node-name' posts than the allowed limit. There is nothing special you need to do as there are many reasons for exceptions. A site administrator will contact you if there is a problem.", array('%node-name'=>$node_name)) .'</li>';
        }
      break;
    }
  }
  $output .= '</ul>';
  
  // list nodes of the type for the user in a paged table
  $user_node_list = nodelimit_user_node_list($uid, $type);
  $output .= $user_node_list;
  if (!$user_node_list) {
    $output .= '<p>'. t('No %node-name nodes were found.', array('%node-name'=>$node_name)) .'</p>';
  }
  
  // set page title to user account name
  if ($user->uid == $uid) {
    // already have the name
    drupal_set_title($user->name);
  }
  else {
    // get user name
    $sql = "SELECT u.name FROM {users} u WHERE u.uid=%d";
    $result = db_query($sql, $uid);
    if (db_num_rows($result) > 0) {
      $row = db_fetch_object($result);
      drupal_set_title($row->name);
    }
  }
  // print page
  print theme('page', $output);
      
}

/**
 * Menu callback: presents one of the following ...
 *   (1) (A) a limitation settings form for the type and (B) a list of users exceeding the current limit
 *   (2) a list of nodes of a type by an user in a themed/paged table
 *   this callback provides the content for the additional admin pages
 *
 * @param $type
 *   the node type
 * @param $limit
 *   a numeric value representing a node limit
 * @param $uid
 *   the id of an user
 */
function nodelimit_admin_callback($type, $limit=0, $uid=NULL) {
  global $user;
  $node_name = node_invoke($type, 'node_name');
  
  // process the settings form if submitted ...
  $op = $_POST['op'];
  $edit = $_POST['edit'];  
  $set_mode = variable_get('nodelimit_mode_'. $type, 0);
  $set_quantity = variable_get('nodelimit_quantity_'. $type, 0);
  $set_anon = variable_get('nodelimit_anon_'. $type, 0);
  
  if ($op == t('Save configuration')) {
    // configuration saved ...
    $set_mode = $edit['set_mode'];
    $set_quantity = $edit['set_quantity'];
    $set_anon = $edit['set_anon'];
    $set_vars = FALSE;
    
    switch ($set_mode) {
      case NODELIMIT_NONE:
        // treat same as setting to default (delete the variables)
        variable_del('nodelimit_mode_'. $type);
        variable_del('nodelimit_quantity_'. $type);
        variable_del('nodelimit_anon_'. $type);
        $set_mode = 0;
        $set_quantity = 0;
        $set_anon = 0;
        drupal_set_message(t('settings have been saved'));
      break;
      case NODELIMIT_SIMPLE:
        // requires quantity to be set and numeric ...
        if (is_numeric($set_quantity)) {
          // and greater than zero ...
          if ($set_quantity > 0) {
            drupal_set_message(t('settings have been saved'));
            $set_vars = TRUE;
          }
          else {
            form_set_error('set_quantity', 'the Node Limit option you selected requires a numeric value greater than zero to be entered for Quantity');
          }
        }
        else {
          form_set_error('set_quantity', 'the Node Limit option you selected requires a numeric value greater than zero to be entered for Quantity');
        }
      break;
      case NODELIMIT_PERM:
        // requires quantity to be set ...
        if (strlen($set_quantity) > 0) {
          // and follow the format ##,##,## ...
          // strip disallowed characters
          $strip = ereg_replace('[^0123456789,]', '', $set_quantity);
          if ($strip != '' && $strip != ',' ) {
            // there is still a value
            // create array from string
            $array = explode(',', $strip);
            // array to unique
            $array = array_unique($array);
            // sort array descending order
            rsort($array);
            // zero and nulls not allowed
            while (count($array) > 1 && $array[count($array)-1] == 0) {
              array_pop($array);
            }
            // create new string from array
            $set_quantity = implode(',', $array);
            // last check for a value
            if ($set_quantity != '' && $set_quantity != '0') {
              $set_vars = TRUE;
            }
          }
        }
        if ($set_vars == TRUE) {
          drupal_set_message(t('settings have been saved'));          
        }
        else {
          form_set_error('set_quantity', 'the Node Limit option you selected requires a comma separated list of numbers to be entered for Quantity');
        }
      break;
      
    }
    if ($set_vars) {
      variable_set('nodelimit_mode_'. $type, $set_mode);
      variable_set('nodelimit_quantity_'. $type, $set_quantity);
      variable_set('nodelimit_anon_'. $type, $set_anon);
    }
  }
  elseif ($op == t('Reset to defaults')) {
    // configuration reset to default ...
    variable_del('nodelimit_mode_'. $type);
    variable_del('nodelimit_quantity_'. $type);
    variable_del('nodelimit_anon_'. $type);
    drupal_set_message(t('settings have been reset to their defaults'));
  }
  
  // outputs the additional pages seen with the module settings
  // there are 2 possiblities
  // (1) a form to set the limit for type with list of users with count of nodes of type exceeding limit
  // (2) a list of nodes of type by a user

  // get values of the settings
  $nodelimit_mode = variable_get('nodelimit_mode_'. $type, 0);
  $nodelimit_quantity = variable_get('nodelimit_quantity_'. $type, 0);
  $nodelimit_anon = variable_get('nodelimit_anon_'. $type, 0);

  // both outputs will include the following text 
  $output .= '<h2>'. t("'%node-name' nodes", array('%node-name'=>$node_name)) .'</h2>';
  // defined by the mode
  switch ($nodelimit_mode) {
    case NODELIMIT_NONE:
      $output .= '<p>'. t("'%node-name' nodes are currently configured with NO limits on the number of nodes a user may create (or author).", array('%node-name'=>$node_name)) .'</p>';
    break;
    case NODELIMIT_SIMPLE:
      $output .= '<p>'. t("'%node-name' nodes are currently configured with a %limit node per user limit. ", array('%node-name'=>$node_name, '%limit'=>$nodelimit_quantity));
      $output .= ($nodelimit_anon == 1 ? t('The limit does not apply to the anonymous user.') : '') .'</p>';
    break;
    case NODELIMIT_PERM:
      $output .= '<p>'. t("'%node-name' nodes are currently configured with multiple node per user limits as defined by permission", array('%node-name'=>$node_name));
      $output .= ($nodelimit_anon == 1 ? t('The limits do not apply to the anonymous user.') : '') .'</p>';
    break;
  }
  
  // the output from this point forward depends on $uid value ...
  if (is_null($uid)) {
    // $uid is null
    // this output provides the settings form and node/user list ...
    
    // the settings form ...
    // mode
    $desc = NULL;
    $options = array(
      NODELIMIT_NONE              => t('No Limits'),
      NODELIMIT_SIMPLE            => t('One limit applied to all users'),
      NODELIMIT_PERM              => t('Multiple limits applied by permissions'),
      );
    $form = form_radios(t('Node Limit Option'), 'set_mode', $set_mode, $options, $desc);
    // quantity
    $desc = 'Indicates the number of %node-name nodes each user may create (or author).<br/>';
    $desc .= 'Please read the %help-link section for more detailed information on the possible values.<br/>';
    $desc .= '<strong>for One limit applied to all users:</strong><br/>Enter the number of %node-name nodes each user may create.<br/>';
    $desc .= '<strong>for Multiple limits applied by permissions:</strong><br/>Enter a comma separated list of numbers representing the different possible limits (example: <strong>50,25,10</strong>).';
    $desc = t($desc, array('%node-name'=>$node_name, '%help-link'=>l('help','admin/help/nodelimit')));
    $form .= form_textfield(t('Quantity'), 'set_quantity', $set_quantity, 20, 60, $desc);
    // anon (anonymous user exception)
    $desc = 'provides the ability to override the limit for the anonymous user.';
    $desc = t($desc);
    $options = array(0=>t('no exceptions'), 1=>t('anonymous user'));
    $form .= form_select(t('Exception'), 'set_anon', $set_anon, $options, $desc);
    // form submission
    $form .= form_submit(t('Save configuration'));
    $form .= form_submit(t('Reset to defaults'));
    // place form in fieldset tags
    $output .= '<fieldset><legend>'. t("Settings for '%node-name' nodes", array('%node-name'=>$node_name)) .'</legend>'. form($form) .'</fieldset>';
    
    // list all users with more than the allowed number of nodes of type in a paged table
    // defined by the mode
    switch ($nodelimit_mode) {
      case NODELIMIT_NONE:
        // provides no list 
      break;
      
      case NODELIMIT_SIMPLE:
        // list of users with more than the allowed quantity of nodes of type
        $sql = "SELECT COUNT(n.nid) ncount, n.uid, u.name FROM {node} n LEFT JOIN {users} u ON n.uid=u.uid WHERE n.type='$type' GROUP BY n.uid, u.name HAVING COUNT(n.nid) > $nodelimit_quantity";
        $sql_count = "SELECT COUNT(DISTINCT n.uid) FROM {node} n WHERE n.type='$type' HAVING COUNT(n.nid) > $nodelimit_quantity";
        
        // get the data
        $rows = array();
        $result = pager_query($sql, 20, 0, $sql_count);
        while ($user_row = db_fetch_object($result)) {
          // each row includes user, node count, link to view nodes
          $rows[] = array(format_name($user_row), array('data'=>$user_row->ncount, 'align'=>'center'), l('view nodes', 'admin/settings/nodelimit/'. $type .'/0/'. $user_row->uid));
        }
        if (count($rows)) {
          // if data...
          // create header
          $header = array(t('user'), t('nodes'), '&nbsp;');
          // create pager 
          $pager = theme('pager', NULL, 20, 0);
          if (!empty($pager)) {
            $rows[] = array(array('data' => $pager, 'colspan' => count($header)));
          }
          // text describing what is displayed
          $grp = '<p>';
          $grp .= t('The following users exceed the currently configured limit for %node-name nodes. ', array('%node-name'=>$node_name));
          $grp .= t('To agree with the current limit, there should NOT be any users listed here');
          $grp .= ($nodelimit_anon == 1 ? t(' except possibly the anonymous user') : '');
          $grp .= '.</p>';
          // themed table
          $grp .= theme('table', $header, $rows);
        }
        else {
          // if no data, text informing user
          $grp = '<p>'. t('There are currently NO users indicated as the author of more than the allowed node per user limit for %node-name posts.', array('%node-name'=>$node_name)) .'</p>';
        }
        // wrap in fieldset tags
        // enclose in fieldset tags
        $output .= '<fieldset><legend>'. t('Users exceeding the current limit') .'</legend>'. $grp .'</fieldset>';
        
      break;
      
      case NODELIMIT_PERM:
        // this mode allows for multiple limits
        // the user lists is presented by each limit, a limit must be selected before the list is diplayed
        // list the possible limits as links selecting a limit
        $quantities = explode(',', $nodelimit_quantity);
        $limit_list = '';
        foreach ($quantities as $quantity) {
          $limit_list .= ' ( '. ($quantity == $limit ? '<strong>'. t('limit of ') . $quantity .'</strong>' : l(t('limit of ') . $quantity, 'admin/settings/nodelimit/'. $type .'/'. $quantity)) .' ) ';
        }
        
        // has $limit been passed ??
        if ($limit == 0) {
          // no, inform user of options
          $grp = '<p>'. t('click one of the limits (below) to view a list of any users that exceed their limit') .'<br/>'. $limit_list .'</p>';
        }
        else {
          // yes, inform user of options
          $grp = '<p>'. t('click one of the limits (below) to view a list of any users that exceed their limit') .'<br/>'. $limit_list .'</p>';
        
          // create sql strings need to generate user list
          // list of users with more nodes than allowed (as passed by $limit)
          // this sql string is somewhat complex...
          $sql_join = " LEFT JOIN (users_roles ur$limit INNER JOIN permission p$limit ON ur$limit.rid=p$limit.rid AND INSTR(p$limit.perm, '$node_name limit $limit')>0) ON n.uid = ur$limit.uid";
          $sql_where = " WHERE n.type = '$type' AND p$limit.rid > 0";
          $quantities = explode(',', $nodelimit_quantity);
          foreach ($quantities as $quantity) {
            if ($quantity > $limit) {
              $sql_where .= " AND p$quantity.rid IS NULL";
              $sql_join .= " LEFT JOIN (users_roles ur$quantity INNER JOIN permission p$quantity ON ur$quantity.rid=p$quantity.rid AND INSTR(p$quantity.perm, '$node_name limit $quantity')>0) ON n.uid = ur$quantity.uid";
            }
          }
          // sql string
          $sql = "SELECT DISTINCT u.uid, u.name, COUNT(n.nid) ncount ";
          $sql .= "FROM node n LEFT JOIN users u ON n.uid=u.uid ";
          $sql .= $sql_join . $sql_where;
          $sql .= " GROUP BY u.uid, u.name ";
          $sql .= " HAVING COUNT(n.nid) > $limit";
          
          // sql count string
          $sql_count = "SELECT COUNT(DISTINCT n.uid) ucount ";
          $sql_count .= "FROM node n LEFT JOIN users u ON n.uid=u.uid ";
          $sql_count .= $sql_join . $sql_where;
          $sql_count .= " GROUP BY u.uid, u.name ";
          $sql_count .= " HAVING COUNT(DISTINCT n.nid) > $limit";
          
          // get the data
          $rows = array();
          $result = pager_query($sql, 20, 0, $sql_count);
          while ($row = db_fetch_object($result)) {
            // each row includes user, node count, node limit, link to view nodes
            $rows[] = array(format_name($row), array('data'=>$row->ncount, 'align'=>'center'), l('view nodes', 'admin/settings/nodelimit/'. $type .'/'. $limit .'/'. $row->uid));
          }
          if (count($rows)) {
            // if data...
            // create header
            $header = array(t('user'), t('nodes'), '&nbsp;');
            // create pager 
            $pager = theme('pager', NULL, 20, 0);
            if (!empty($pager)) {
              $rows[] = array(array('data' => $pager, 'colspan' => count($header)));
            }
            // text describing what is displayed
            $grp .= '<p>';
            $grp .= t('The following users with an <strong>%node-name limit of %quantity</strong> currently exceed their limit. ', array('%node-name'=>$node_name, '%quantity'=>$limit));
            $grp .= t('To agree with the current configuration settings for %node-name nodes, there should NOT be any users listed here', array('%node-name'=>$node_name));
            $grp .= ($nodelimit_anon == 1 ? t(' except possibly the anonymous user') : '');
            $grp .= '.</p>';
            // themed table
            $grp .= theme('table', $header, $rows);
          }
          else {
            // if no data, text informing user
            $grp .= t('There are currently NO users with an <strong>%node-name limit of %quantity</strong> indicated as exceeding their limit. ', array('%node-name'=>$node_name, '%quantity'=>$limit));
            $grp .= t('The current configuration setting and the %node-name node data are in agreement.', array('%node-name'=>$node_name));
            $grp .= '</p>';
          }
        }
        // wrap in fieldset tags
        // enclose in fieldset tags
        $output .= '<fieldset><legend>'. t('Users exceeding the current limit') .'</legend>'. $grp .'</fieldset>';
        
      break;
    }
          
    // set title
    drupal_set_title(t("settings for '%node-name' nodes", array('%node-name'=>$node_name)));
    // print page
    print theme('page', $output);
  }
  else {
    // uid is a ##
    // the output is instead a list of all posts of type for the user in a paged table
    // with an indication of any changes that should be made to have the node data agree with the limitation setting
    
    // get the user's name
    if ($uid > 0) {
      $sql = "SELECT u.name FROM {users} u WHERE u.uid=%d";
      $result = db_query($sql, $uid);
      if (db_num_rows($result) > 0) {
        $row = db_fetch_object($result);
        $user_name = $row->name;
      }
    }
    else {
      $user_name = 'anonymous';
    }
    
    // get users limit and count
    $user_limit = nodelimit_user_limit($uid, $type);
    $user_node_count = nodelimit_user_node_count($uid, $type);
    
    // enclose in fieldset tags
    $output .= '<fieldset><legend>'. t('%node-name nodes by %user-name', array('%node-name'=>$node_name, '%user-name'=>$user_name)) .'</legend>';
    
    // inform user what the list includes, and possible actions that need to be taken
    // defined by the mode
    $output .= '<p>'. t('The following %count %node-name nodes are authored by %user-name. ', array('%node-name'=>$node_name, '%count'=>$count, '%user-name'=>$user_name));
    switch ($nodelimit_mode) {
      case NODELIMIT_NONE:
        $output .= t('There is currently NO limit on the number of %node-name nodes a user can create, this node listing is provided for informational purposes, no action is necessary.', array('%node-name'=>$node_name));
      break;
      case NODELIMIT_SIMPLE:
        if ($user_node_count > $user_limit) {
          $output .= t("To agree with the currently configured limit, %overage of %user-name's %node-name nodes should be changed to a indicate a different user as the author.", array('%node-name'=>$node_name, '%overage'=>($user_node_count-$user_limit), '%user-name'=>$user_name));
        }
        else {
          $output .= t("%user-name's %node-name node count is within the current limit, no action is necessary.", array('%node-name'=>$node_name, '%user-name'=>$user_name));
        }
      break;
      case NODELIMIT_PERM:
        if ($count > $limit) {
          $output .= t("%user-name's permissions allow him/her to create (or author) %limit %node-name node(s).  To agree with this limit, %overage of %user-name's %node-name nodes should be changed to a indicate a different user as the author.", array('%node-name'=>$node_name, '%limit'=>($user_limit), '%overage'=>($user_node_count-$user_limit), '%user-name'=>$user_name));
        }
        else {
          $output .= t("%user-name's %node-name node count is within the user's allowed limit, no action is necessary.", array('%node-name'=>$node_name, '%user-name'=>$user_name));
        }
      break;
    }
    $output .= '</p>';
    
    // provide list of nodes in a themed table
    $user_node_list = nodelimit_user_node_list($uid, $type);
    $output .= $user_node_list;
    if (!$user_node_list) {
      $output .= '<p>'. t('No %node-name nodes by %user-name were found.', array('%node-name'=>$node_name, '%user-name'=>$user_name)) .'</p>';
    }
    $output .= '</fieldset>';
    
    // set title
    drupal_set_title(t("'%node-name' nodes by ". $user_name, array('%node-name'=>$node_name)));
    // print page
    print theme('page', $output);
    
  }
}

/**
 * Implementation of hook_nodeapi().
 */ 
function nodelimit_nodeapi(&$node, $op, $teaser, $page) { 
global $user;
  
  switch ($op) { 
    
    // settings
    case 'settings': 
      // in this implementation of the 'settings' option
      // the standard settings form fields are not provided here
      // instead, a short note is provided with a link to the settings form
      $settings = array(); 
      $node_name = node_invoke($node->type, 'node_name');
      $grp = 'The nodelimit module is currently enabled and allows setting a limit on the number of %node-name nodes your users can create. ';
      if (variable_get('nodelimit_mode_'. $node->type, 0) == 0) {
        $grp .= 'There is currently no limit set for %node-name nodes. ';
      }
      else {
        $grp .= 'A limit is currently set for %node-name nodes. ';
      }
      $grp .= 'For more information or to change the setting, please see %settings-link.';
      $grp = t($grp, array('%node-name'=>$node_name, '%settings-link'=>l('admin/settings/nodelimit', 'admin/settings/nodelimit')));
      $settings[] = '<strong>Node Limits</strong><br/>'. $grp;
      return $settings; 
    break;
    
    // validate
    case 'validate': 
      // this implementation of the 'validate' option does most of the work
      // does this node type have a limitiation set ???
      $nodelimit_mode = variable_get('nodelimit_mode_'. $node->type, 0);
      if ($nodelimit_mode > 0) {
        // a limit is in place for this node type
        // get the assoctiated values
        $nodelimit_quantity = variable_get('nodelimit_quantity_'. $node->type, 0);
        $nodelimit_anon = variable_get('nodelimit_anon_'. $node->type, 0);
        // get the node name
        $node_name = node_invoke($node->type, 'node_name');
        
        // does the user have administer nodes permission ?
        if (user_access('administer nodes')) {
          // yes, this user can create nodes for other users
          // get the users limit
          switch ($nodelimit_mode) {
            case NODELIMIT_SIMPLE:
              // this mode sets a single limit for all users
              $user_limit = $nodelimit_quantity;
            break;
            case NODELIMIT_PERM:
              // this mode sets multiple limits for users by permission
              // get users limit
              $user_limit = nodelimit_user_limit($node->uid, $node->type);
            break;
          }
          // is there a limit in place ???
          if ($user_limit > 0) {
            // create message to inform user 
            drupal_set_message(t("The '%node-name' post limit for the user '%user-name' is %user-limit.", array('%node-name'=>$node_name, '%user-limit'=>$user_limit, '%user-name'=>$node->name)));
            drupal_set_message(t("Your permissions allow you to author '%node-name' posts for other users.", array('%node-name'=>$node_name)));
            // sql string for check
            $sql = "SELECT n.nid FROM {node} n WHERE n.type='%s' AND n.uid=%d";
            if ($nodelimit_anon == 1) {
              // the anonymous user exception is in place
              // extend message accordingly
              drupal_set_message(t("You can set the author to blank if you need to create additional '%node-name' posts beyond the limit.", array('%node-name'=>$node_name)));
              // change sql string for check
              $sql = "SELECT n.nid FROM {node} n WHERE n.type='%s' AND n.uid=%d AND n.uid>0";
            }
            // check (using sql string from above) if user already has reached the limit of nodes of this type
            $result = db_query($sql, $node->type, $node->uid);
            if (db_num_rows($result) > $user_limit) {
              // the user has already reached the limit of nodes of this type ...
              // set form error and message to inform user
              form_set_error('name', t("The user '%user-name' has already reached the '%node-name' post limit of %user-limit, you must indicate a different user as the author of this post.", array('%user-name'=>$node->name, '%node-name'=>$node_name, '%user-limit'=>$user_limit)));
            }
          }
        }
        else {
          // No, this user can only create his/her nodes
          // is this an add or edit ??? (users that have exceeded their limit for some reason are still able to edit their existing posts)
          if (arg(1) == 'add') {
            // this is an add/create new node
            // get the users limit
            switch ($nodelimit_mode) {
              case NODELIMIT_SIMPLE:
                // this type sets a single limit for all users
                $user_limit = $nodelimit_quantity;
              break;
              case NODELIMIT_PERM:
                // this type sets limits by permissions, get users limit
                $user_limit = nodelimit_user_limit($user->uid, $node->type);
              break;
            }
            if ($user_limit > 0) {
              // the user has a limit 
              // sql string for limit check
              $sql = "SELECT n.nid FROM {node} n WHERE n.type='%s' AND n.uid=%d";
              if ($nodelimit_anon == 1) {
                // use different sql string for limit check
                $sql = "SELECT n.nid FROM {node} n WHERE n.type='%s' AND n.uid=%d AND n.uid>0";
              }
              // using sql string, check if user has already reached his/her limit for nodes of this type
              $result = db_query($sql, $node->type, $node->uid, $node->nid);
              if (db_num_rows($result) >= $user_limit) {
                // user has already reached his/her limit for nodes of this type ...
                if ($limit == 1) {
                  // set message to inform user
                  drupal_set_message(t("Your permissions allow you to create only one '%node-name' post.  You have been redirected to your '%node-name' post.", array('%node-name'=>$node_name)));
                  // redirect user to node edit page ...
                  $row = db_fetch_object($result);
                  $nid = $row->nid;
                  drupal_goto('node/'. $nid);
                }
                else {
                  // no message, just redirect user to his/her nodelimit page
                  drupal_goto('nodelimit/'. $user->uid .'/'. $node->type);
                }
              }
            }
          }
        }
      }
    break;
  }
}

/**
 * Provides the node count for a user
 * 
 * @param $uid 
 *   The id of an user
 * @param $type
 *   The node type
 *
 * @return 
 *   The number of nodes of the type indicated that the user has created (or authored)
 */ 
function nodelimit_user_node_count($uid, $type) { 
  $sql = "SELECT n.nid FROM {node} n WHERE n.type='%s' AND n.uid=%d";
  $result = db_query($sql, $type, $uid);
  return db_num_rows($result);
}

/**
 * Provides the node limit for a user
 *   It should be noted that limits apply to users with permission to create nodes of the indicated type
 *   A check for permission to create is not provided by this function, but is assumed
 *
 * @param $uid 
 *   The id of an user
 * @param $type
 *   The node type
 *
 * @return 
 *   The number of nodes of the type indicated that the user may create (or author)
 *   Returns 0 for unlimited
 */ 
function nodelimit_user_limit($uid, $type) {  
  global $user;
  $nodelimit_mode = variable_get('nodelimit_mode_'. $type, 0);
  $nodelimit_quantity = variable_get('nodelimit_quantity_'. $type, 0);
  $nodelimit_anon = variable_get('nodelimit_anon_'. $type, 0);
  switch ($nodelimit_mode) {
    case NODELIMIT_NONE:
      // always unlimited
      return 0;
    break;
    case NODELIMIT_SIMPLE:
      if ($uid == 0 && $nodelimit_anon == 1) {
        // anonymous user with anonymous exception
        return 0;
      }
      else {
        return $nodelimit_quantity;
      }
    break;
    case NODELIMIT_PERM:
      switch ($uid) {
        case 1:
          // admin user
          return 0;
        case 0:
          // anonymous user
          if ($nodelimit_anon == 1) {
            // with anonymous exception
            return 0;
          }
          // else fall thru
        default;
          // for all other users
          $node_name = node_invoke($node->type, 'node_name');
          $quantities = explode(',', $nodelimit_quantity);
          // need user account object 
          $account = $uid == $user->uid ? $user : user_load(array('uid' => $uid));
          // loop thru perms, find first match for user
          foreach ($quantities as $quantity) {
            if (user_access(t('%node-name limit %quantity', array('%node-name'=>$node_name, '%quantity'=>$quantity)), $account)) {
              return $quantity;
            }
          }
          // if no matches 
          return 0;
      }
    break;
    default;
      return 0;
  }
}

/**
 * Provides the node list for a user
 *
 * @param $uid 
 *   The id of an user
 * @param $type
 *   The node type
 *
 * @return 
 *   a themed, paged table listing a users nodes of type
 */ 
function nodelimit_user_node_list($uid, $type) {
  global $user;
  $output = '';
  // list nodes of the type for the user in a paged table
  // create header (for table sort)
  $header = array();
  $header[] = array('data' => '&nbsp;');
  $header[] = array('data' => '&nbsp;');
  $header[] = array('data' => t('title'), 'field' => 'title', 'sort' => 'ASC');
  $header[] = array('data' => t('created'), 'field' => 'created');
  $header[] = array('data' => t('changed'), 'field' => 'changed');
  // sql string
  $sql = "SELECT n.* FROM {node} n WHERE n.type='$type' AND n.uid=$uid";
  if (!($user->uid == $uid || user_access('administer content'))) {
    $sql .= " AND n.status=1";
  }    
  $sql .= ' '. tablesort_sql($header);
  $result = pager_query(db_rewrite_sql($sql), 20);
  $rows = array();
  // get data into rows
  // actions (view/edit), title, created, and changed
  // note: only showing changed if changed is more than 60 seconds after created.
  while ($row = db_fetch_object($result)) {
    $action_view = '&nbsp;';
    if (node_access('view', $row, $user->uid)) {
      $action_view = l(t('view'), 'node/'. $row->nid);
    }
    $action_edit = '&nbsp;';
    if (node_access('update', $row, $user->uid)) {
      $action_edit = l(t('edit'), 'node/'. $row->nid .'/edit');
    }
    $title = '<strong>'. $row->title .'</strong> '. theme('mark', node_mark($row->nid, $row->changed));
    $created = t('%time ago', array('%time' => format_interval(time() - $row->created)));
    $changed = ($row->created + 60) > $row->changed ? '&nbsp;' : t('%time ago', array('%time' => format_interval(time() - $row->changed)));
    $rows[] = array($action_view, $action_edit, $title, $created, $changed);
  }
  if (count($rows)) {
    // user account has nodes ...
    // add pager ...
    $pager = theme('pager', NULL, 20, 0);
    if (!empty($pager)) {
      $rows[] = array(array('data' => $pager, 'colspan' => count($header)));
    }    
    // display in themed table
    $output .= theme('table', $header, $rows);
  }
  return $output;
}

Comments

sangamreddi’s picture

Thanks for your hard work. I would like to know, whether this module works on role based, it means a authenticated memeber the node creation limit will be 5 nodes and premium members can create 50 nodes something like that.
I havent tested it. Could you explain me the major difference between your module and other node limit modules? Once again thanks.

Sunny                      
www.gleez.com | www.sandeepone.com

rjl’s picture

Regarding your second question, here is my take on the current node limiting modules.

noderestrict
Node types are either limited or not. There is one limit for all users for all limited node types. There is a permission that allows for unlimited node creation. This module has a very cool feature where you can input the path of a page that users should be redirected to if they attempt to create more nodes than the allowed limit. Those concerned with overhead, this module creates 3 total drupal variables. This module is not a project (yet) is very small, 50 lines, and very much a proof of concept idea. Limited help and code commenting

bio
Creates a new node type called 'bio.' Only bio nodes are limited, each user can create only one. This too has very little in terms of overhead. This module is a project, it is also very small, 120 lines, limited help and code commenting

usernodes
This one allows setting a different limit for each node type, the limit is applied to all users. It has an interesting option for adding links to nodes to view the users page, though I am not sure how this differs from the normal author link, but always nice to have several options. This module displays a harsh message if a user attempts to create more than the limit, but nothing more. There are also some options about if and where to display nodes and create content links in the user pages. This module creates potentially 7 drupal variables for each limited node type. This module is a project, also small at 240 lines, limited help and code commenting

bioesque
This module allows setting a limit of one for every node type with an override for the anonymous user. This module also allows for some placement of nodes in the user pages. It has a nice administration section for reviewing existing node data and users with too many nodes. When a user attempts to create a second node, the user is redirected to their first. This module creates one drupal variable for each limited node type and 2 variables at the module level. This module is also not a project. This module is 500 lines, though the first 100 lines are the help section and the code is pretty thoroughly commented.

nodelimit
The newest of the pack. Can set any limit on node types applied to all users OR can set multiple limits that can be assigned to users in different roles by permission. Has an anonymous user override. Options to display lists of a users nodes (and create content links) on the user account pages. If a user attempts to create additional nodes beyond the limit, user is redirected to a page showing a list of his/her current nodes. Nice administration section for review existing node data to see if users have exceeded limits. Nice help section and commented code. This module starts to get on the larger side at 1000 lines and creates 3 drupal variables for each limited node type. The code here was also written to easily allow someone to extend the options with new ideas without having to re-write the whole module, but instead to insert the relevent logic in the appropriate places.

rjl’s picture

let's say you have 4 roles
1-unauthenticated
2-authenticated
3-premium
4-admin

let's say you have 2 node types
story
page

Let's say page nodes are created by admin only, but everyone can view them. In this situation, the nodelimit module would NOT come into play.

Let's say story nodes are created by authenticated, premium, and admin. Authenticated users can create up to 5 stories, premium users can create up to 50 stories and admin users can create unlimited stories. This is a great use for nodelimit modules 'Multiple limits applied by permission' option. The Quantity value you need is '50,5' and Exception value can be 'no exception.' After you create these settings, go to your admin » access control page. We noted above that 3 roles can create story nodes, and those 3 roles should have 'create stories' permission. You should see 2 new permissions 'story limit 5' and 'story limit 50' listed under the 'nodelimit' heading. Assign 'story limit 5' to your authenticated users role and 'story limit 50' to your premium users role. Do not assign a limit to admin users, this allows them the 'unlimited' ability. Once you have assigned these roles, go back to the nodelimit admin pages and choose 'story' nodes. At the bottom of the settings form in a section labeled 'users exceeding the current limit' you should see 2 links 'limit 5' and 'limit 50.' Click on 'limit 5' and you will see a list of any authenticated users that have more than 5 story nodes, click on the 'view nodes' link next to a user and you will see a list of his/her story nodes. You can then make a descision about how you want to handle any over limit situation.

Hope that answered your question, and hopefully not too difficult.

sangamreddi’s picture

Very nice explanation, it covered all my queries. Thanks a lot. I'll try your module with in 2 days and let you know the same. I think all the features have been covered in your module. Thank you for contributing the code. It would be better to create a project for this. So that other developeres will extend teh functionality and track issues easily.

Sunny                      
www.gleez.com | www.sandeepone.com

parchita’s picture

where can I find the nodelimit module?

thanks

rjl’s picture

coupet’s picture

What about limit for modules that are not node enable such as comments, vote and invite?

Thanks, Darly

Apache is bandwidth limited, PHP is CPU limited, and MySQL is memory limited.

rjl’s picture

That is an intesting question...

I am assuming you mean limiting a user to making X number of comments. I don't really know the vote and invite modules very well so am guessing you mean limit the number of votes on a node and limit the number of invites a user can make?

This module is really about node limits, but as comments, votes and invites are extensions of nodes, there are definate possibilities. I will look at those modules and my code and see what could happen.

mwu’s picture

thank you for writing this. Am I right in thinking I cannot use this to sell classified ads?
For example, for every $30, someone gets to post a classified ad.

Sounds like your module counts the total number of posts in that node type, so I can't really say "plus one more."

Is this right?

Also, turns out I can't cut and paste code this long. It crashed my computer. (unless there's something about the clipboard that I need to set.)

rjl’s picture

No, this code can't be used to sell classified ads, but I thought the e-commerce module http://drupal.org/project/ecommerce allowed for that? though I haven't tried that module out, so am not speaking from experience.

And you are correct in the module counting the total number of posts of that type.

As for the copy and paste, don't know about that. Would be happy to email you the code, contact me me through my contact form if you do.

mwu’s picture

there is no module that exists that charges per node.
thanks for the offer to email me the code. one test module seems to have broken my sandbox, so I'll wait a bit before I test more code. thank you though.

tknospdr’s picture

You may have already found this but the LM PalPay module does exactly this. I have it set to sell classified ads at $1.00 each for specific roles on my site. Upgrading to a different role then allows for free classified ad placement.

Thanks,
David
http://www.floridapets.org

hardieas’s picture

Gives error
Call to undefined function: node_list() in /var/www/drupal-4.7.2-scratch/modules/nodelimit/nodelimit.module on line 170

Pity, could have used this ...

rjl’s picture

Sorry the code if for version 4.6 and won't work with version 4.7

I've been so busy lately that I just haven't had much time to look at the Form API for drupal 4.7

Anyone of course should feel free to upgrade the code to 4.7 if they like. I do intend to get to that, but probably not for a while unfortunately.

tknospdr’s picture

get around to updating this for 4.7 I've got an additional feature request. Can you set it so that the limit resets itself after x amount? So like every 10 days, or 2 weeks or 6 months.
That would be totally awesome.

Thanks,
David
http://www.floridapets.org

abramo’s picture

this terrific module should not be abandoned to oblivion - it would be a great pity to let it rust unattended. hope someone will attempt to update it to 5.x soon.

best,
abramo

rjl’s picture

I appreciate the compliment, I've yet unfortunately to find time to keep up with the code changes in Drupal - just keeping up with all the versions and new modules is a bit overwhelming. I love Drupal, so my hats off to all those who are making this software so incredible - version 5 is amazing. I do hope to get back on track in terms of coding this summer, sorry it can't be sooner. But as I've said before, anyone who can, should feel free to upgrade the code if they have time.