Under "admin/people/permissions" there is a permission that allows users to administer all the content types.


I want to create a role that can only administer an "Article" content type. How can I achieve this?

  • Are you interested in existing modules, or would you be interested in writing your own code for handling this?
    – apaderno
    Commented Feb 16, 2012 at 13:32
  • @kiamlaluno I prefer writing own code for handling this.
    – gilzero
    Commented Feb 16, 2012 at 23:28

4 Answers 4


The menu callback for "admin/structure/types/manage/%node_type" is defined in node_menu() using the following array item:

  $items['admin/structure/types/manage/%node_type'] = array(
    'title' => 'Edit content type', 
    'title callback' => 'node_type_page_title', 
    'title arguments' => array(4), 
    'page callback' => 'drupal_get_form', 
    'page arguments' => array('node_type_form', 4), 
    'access arguments' => array('administer content types'), 
    'file' => 'content_types.inc',

There are other two menu items that follow it, but they depend from that menu item for the access callback.

  $items['admin/structure/types/manage/%node_type/edit'] = array(
    'title' => 'Edit', 
  $items['admin/structure/types/manage/%node_type/delete'] = array(
    'title' => 'Delete', 
    'page arguments' => array('node_type_delete_confirm', 4), 
    'access arguments' => array('administer content types'), 
    'file' => 'content_types.inc',

Once you change the access callback for the first menu item, the other two will use the same access callback.

The first step, for the custom module I would implement to achieve what you want is to implement hook_permission(). node_permission() is useful as guideline to write the code you need.

function mymodule_permission() {
  $perms = array();

  foreach (node_type_get_types() as $type => $info) {
    $perms["administer $type content type"] = array(
      'title' => t('%type_name: administer content type', array('%type_name' => $info->name)),

  return $perms;

The foreach() part could be rewritten as the following code:

  foreach (node_permissions_get_configured_types() as $type) {
    $info = node_type_get_type($type);

    $perms["administer $type content type"] = array(
      'title' => t('%type_name: administer content type', array('%type_name' => $info->name)),

The difference is that node_permissions_get_configured_types() is using some Drupal variables that are set in a settings page. The function is used also from the Node module when creating the list of the permissions associated to a node of a content type. The documentation for the function reports the following text:

To exclude a specific node from getting permissions defined for it, set the "node_permissions_$type" variable to 0. Core does not provide an interface for doing so; however, contrib modules may exclude their own nodes in hook_install(). Alternatively, contrib modules may configure all node types at once, or decide to apply some other hook_node_access() implementation to some or all node types.

The second step is to alter the menu item already defined by the Node module.

function mymodule_menu_alter(&$items) {
  if (isset($items['admin/structure/types/manage/%node_type']) {
    $items['admin/structure/types/manage/%node_type']['access callback'] = '_mymodule_content_type_access';    
    $items['admin/structure/types/manage/%node_type']['access arguments'] = array(4);

_mymodule_content_type_access() should contain code similar to the following one:

function _mymodule_content_type_access($type) {
  return user_access("administer {$type->type} content type");
  • +1 beat me to it by 4 minutes :) I'll leave mine up as a self-contained code example as it might come in handy for someone
    – Clive
    Commented Feb 18, 2012 at 16:57

The easiest way I can think would be to provide a new separate permission for each available content type which you can assign to arbitrary roles, then override the access callback for the content type edit form to use this new permission:

// Implements hook_permission()
function MYMODULE_permission() {
  $permissions = array();
  // Build up a permission for each content type
  foreach (node_type_get_types() as $type) {
    // Get the permission key
    $permission_string = _MYMODULE_permission_string($type->type);

    // Add the new permission
    $permissions[$permission_string] = array(
      'title' => 'Administer ' . $type->name . ' Content Type',
      'restrict access' => TRUE

  return $permissions;

// Implements hook_menu_alter()
function MYMODULE_menu_alter(&$items) {
  // Override the access callback
  $items['admin/structure/types/manage/%node_type']['access callback'] = 'MYMODULE_content_type_access';

  // Override the access arguments so we receive the content type object
  // rather than the standard permission name.
  $items['admin/structure/types/manage/%node_type']['access arguments'] = array(4);

// Access callback function
function MYMODULE_content_type_access($content_type, $account = NULL) {
  // If no account object is provided use the logged in user
  if (!$account) {
    global $user;
    $account = $user;

  // If the user has access to edit all content types then we should return
  // TRUE.
  if (user_access('administer content types', $account)) {
    return TRUE;

  // Otherwise create a permission string based on the provided content type.
  $permission_string = _MYMODULE_permission_string($content_type->type);

  // Return access based on the permission string
  return user_access($permission_string, $account);

// Helper function to build up a standardised permission string
function _MYMODULE_permission_string($type_name) {
  return 'administer ' . $type_name . ' content type';
  • Worked really great, except I had to override $items['admin/structure/types']['access callback'] as well to check if the user had access to any of the content types before they could reach that page. I also had to override the table that is rendered to check for access to each content types. I need to see if there's a way to modify that table without overriding the page callback. If I get a chance, I'd love to roll this into a module at some point :-p
    – thirdender
    Commented Apr 2, 2014 at 23:12
  • When using in a drupal instance based on an installation profile, I had to implements hook_module_implements_alter() (see docs.getdkan.com/dkan-documentation/dkan-developers/…).
    – Ursula
    Commented Jun 13, 2014 at 13:01

You also have, on permissions, for each role:

  • Article: Create new content
  • Article: Edit own content
  • Article: Edit any content
  • Article: Delete own content
  • Article: Delete any content.

What else do you need?

  • Users with the permission to administer content types change the settings for the content type; the users who can edit any content are the user with the permission to administer content. Being able to delete any article doesn't mean being able to change the settings for that content type, which are shown on admin/structure/types/manage/article.
    – apaderno
    Commented Feb 16, 2012 at 13:28
  • 3
    'Edit own content' is different with 'edit content type'
    – gilzero
    Commented Feb 16, 2012 at 23:29

Workbench module can also help here. Different roles can have access to steps in Workbench, ie you can have different roles access different steps in workbench.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.