Index: content_access.admin.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/Attic/content_access.admin.inc,v retrieving revision 1.1.2.24 diff -u -p -r1.1.2.24 content_access.admin.inc --- content_access.admin.inc 19 Jul 2010 11:40:11 -0000 1.1.2.24 +++ content_access.admin.inc 11 Dec 2010 00:46:22 -0000 @@ -13,14 +13,14 @@ define('CONTENT_ACCESS_MASS_UPDATE_THRES /** * Per node settings page. */ -function content_access_page(&$form_state, $node) { - drupal_set_title(t('Access control for %title', array('%title' => $node->title))); +function content_access_page($form, &$form_state, $node) { + drupal_set_title(t("Access control for !node_title", array('!node_title' => $node->title))); foreach (_content_access_get_operations() as $op) { $defaults[$op] = content_access_per_node_setting($op, $node); } - $form = content_access_role_based_form($defaults); + $form = content_access_role_based_form( $form, $defaults); // Add a after_build handler that disables checkboxes, which are enforced by permissions. $form['per_role']['#after_build'] = array('content_access_force_permissions'); @@ -35,9 +35,9 @@ function content_access_page(&$form_stat ); foreach (array('view', 'update', 'delete') as $op) { $acl_id = content_access_get_acl_id($node, $op); - acl_node_add_acl($node->nid, $acl_id, $op == 'view', $op == 'update', $op == 'delete', content_access_get_settings('priority', $node->type)); + acl_node_add_acl($node->nid, $acl_id, (int)($op == 'view'), (int)($op == 'update'), (int)($op == 'delete'), content_access_get_settings('priority', $node->type)); $form['acl'][$op] = acl_edit_form($acl_id, t('Grant !op access', array('!op' => $op))); - $form['acl'][$op]['#collapsed'] = !isset($_POST['acl_'. $acl_id]) && !unserialize($form['acl'][$op]['user_list']['#default_value']); + $form['acl'][$op]['#collapsed'] = !isset($_POST['acl_' . $acl_id]) && !unserialize($form['acl'][$op]['user_list']['#default_value']); } } @@ -58,7 +58,7 @@ function content_access_page(&$form_stat if (!$node->status) { drupal_set_message(t("Warning: Your content is not published, so this settings are not taken into account as long as the content remains unpublished."), 'error'); } - + return $form; } @@ -70,6 +70,7 @@ function content_access_page_submit($for // Set the settings so that further calls will return this settings. $settings[$op] = array_keys(array_filter($form_state['values'][$op])); } + // Save per-node settings. content_access_save_per_node_settings($node, $settings); @@ -96,27 +97,29 @@ function content_access_page_reset($form /** * Per content type administration page form. */ -function content_access_admin_settings(&$form_state, $type) { - $form_state['type'] = $type; +function content_access_admin_settings( $form, &$form_state, $type ) { + $type_name = $type->type; + + $form_state['type'] = $type_name; // Add role based per content type settings $defaults = array(); foreach (_content_access_get_operations() as $op) { - $defaults[$op] = content_access_get_settings($op, $type); + $defaults[$op] = content_access_get_settings($op, $type_name); } - $form = content_access_role_based_form($defaults); + $form = content_access_role_based_form( $form, $defaults); // Per node: $form['node'] = array( '#type' => 'fieldset', '#title' => t('Per content node access control settings'), '#collapsible' => TRUE, - '#description' => t('Optionally you can enable per content node access control settings. If enabled, a new tab for the content access settings appears when viewing content. You have to configure permission to access these settings at the !permissions page.', array('!permissions' => l(t('permissions'), 'admin/user/permissions'))), + '#description' => t('Optionally you can enable per content node access control settings. If enabled, a new tab for the content access settings appears when viewing content. You have to configure permission to access these settings at the !permissions page.', array('!permissions' => l(t('permissions'), 'admin/people/permissions'))), ); $form['node']['per_node'] = array( '#type' => 'checkbox', '#title' => t('Enable per content node access control settings'), - '#default_value' => content_access_get_settings('per_node', $type), + '#default_value' => content_access_get_settings('per_node', $type_name), ); $form['advanced'] = array( @@ -128,7 +131,7 @@ function content_access_admin_settings(& $form['advanced']['priority'] = array( '#type' => 'weight', '#title' => t('Give content node grants priority'), - '#default_value' => content_access_get_settings('priority', $type), + '#default_value' => content_access_get_settings('priority', $type_name), '#description' => t('If you are only using this access control module, you can safely ignore this. If you are using multiple access control modules you can adjust the priority of this module.'), ); $form['submit'] = array( @@ -142,13 +145,23 @@ function content_access_admin_settings(& function content_access_admin_settings_submit($form, &$form_state) { // Where possible let the drupal permissions system handle access control. $permissions = content_access_get_permissions_by_role(); + + $roles = user_roles(); + foreach (array('update', 'update_own', 'delete', 'delete_own') as $op) { foreach ($form_state['values'][$op] as $rid => $value) { - $permissions[$rid][ content_access_get_permission_by_op($op, $form_state['type']) ] = $value; + $permission = content_access_get_permission_by_op($op, $form_state['type']); + if ( $value ) { + $permissions[$rid][] = $permission; + } + else { + $permissions[$rid] = array_diff( $permissions[$rid], array( $permission )); + } } // Don't save the setting, so its default value (get permission) is applied always. unset($form_state['values'][$op]); } + content_access_save_permissions($permissions); // Update content access settings @@ -176,7 +189,9 @@ function content_access_admin_settings_s } if (content_access_mass_update(array($type))) { - drupal_set_message(t('Permissions have been successfully rebuilt for the content type @types.', array('@types' => node_get_types('name', $type)))); + drupal_set_message( + t('Permissions have been successfully rebuilt for the content type @types.', + array('@types' => node_type_get_name( $type)))); } } @@ -191,14 +206,19 @@ function content_access_admin_settings_s * Whether the operation has been processed successfully (TRUE) or postponed (FALSE). */ function content_access_mass_update($types) { - $count = db_result(db_query("SELECT COUNT(DISTINCT nid) FROM {node} WHERE type IN (". db_placeholders($types, 'text') .")", $types)); + $q = db_select( "node", 'n' ) + ->fields( 'n', array( 'nid' )) + ->condition( 'n.type', $types, 'IN' ); + + $count = $q->countQuery()->execute()->fetchField(); + node_access_needs_rebuild(TRUE); // If there not too much nodes affected, try to do it. - if ($count <= CONTENT_ACCESS_MASS_UPDATE_THRESHOLD) { - $result = db_query("SELECT nid FROM {node} WHERE type IN (". db_placeholders($types, 'text') .")", $types); - while ($node = db_fetch_object($result)) { - node_access_acquire_grants(node_load($node->nid)); + if ( $count <= CONTENT_ACCESS_MASS_UPDATE_THRESHOLD) { + $records = $q->execute(); + foreach ( $records as $node ) { + node_access_acquire_grants(node_load($node->nid)); } cache_clear_all(); @@ -212,12 +232,18 @@ function content_access_mass_update($typ * Gets the permissions for the role of the given id. */ function content_access_get_permissions_by_role() { - $result = db_query('SELECT r.rid, p.perm FROM {role} r LEFT JOIN {permission} p ON r.rid = p.rid'); $permissions = array(); - while ($role = db_fetch_object($result)) { - $permissions[$role->rid] = array_filter(drupal_map_assoc(explode(', ', $role->perm))); + $result = db_query('SELECT r.rid, p.permission FROM {role} r LEFT JOIN {role_permission} p ON r.rid = p.rid'); + foreach ( $result as $rolepermission ) { + if ( ! isset( $permissions[ $rolepermission->rid ] )) { + $permissions[ $rolepermission->rid ] = array(); + } + + $permissions[ $rolepermission->rid ][] = $rolepermission->permission; + } - return $permissions; + + return $permissions; } /** @@ -225,10 +251,17 @@ function content_access_get_permissions_ */ function content_access_save_permissions($permissions) { foreach ($permissions as $rid => $perms) { - $perms = array_filter($perms); - db_query('DELETE FROM {permission} WHERE rid = %d', $rid); - if (count($perms)) { - db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $rid, implode(', ', array_keys($perms))); + $perms = array_unique( array_filter($perms)); + db_query('DELETE FROM {role_permission} WHERE rid = :rid', array( 'rid' => $rid )); + foreach ( $perms as $permission ) { + if ( ! is_numeric( $permission )) { // Not sure if it is ever could be true now. Used to happen. + db_insert( 'role_permission' ) + ->fields( array( + 'rid' => $rid, + 'permission' => $permission + )) + ->execute(); + } } } // Make sure new permissions are applied immediately. @@ -241,7 +274,7 @@ function content_access_save_permissions * @param $defaults * Array of defaults for all operations. */ -function content_access_role_based_form($defaults = array()) { +function content_access_role_based_form($form, $defaults = array()) { // Make sure defaults are set properly foreach (_content_access_get_operations() as $op) { @@ -254,8 +287,8 @@ function content_access_role_based_form( '#type' => 'fieldset', '#title' => t('Role based access control settings'), '#collapsible' => TRUE, - '#description' => t('Note that users need at least the %access_content permission to be able to deal in any way with content.', array('%access_content' => t('access content'))). - ' '. t('Furthermore note that content which is not @published is treated in a different way by drupal: It can be viewed only by its author or users with the %administer_nodes permission.', array('@published' => t('published'), '%administer_nodes' => t('administer nodes'))), + '#description' => t('Note that users need at least the %access_content permission to be able to deal in any way with content.', array('%access_content' => t('access content'))) . + ' ' . t('Furthermore note that content which is not @published is treated in a different way by drupal: It can be viewed only by its author or users with the %administer_nodes permission.', array('@published' => t('published'), '%administer_nodes' => t('administer nodes'))), ); drupal_add_css(drupal_get_path('module', 'content_access') . '/content_access.css'); $form['per_role']['view'] = array('#type' => 'checkboxes', @@ -264,7 +297,7 @@ function content_access_role_based_form( '#options' => $roles, '#title' => t('View any content'), '#default_value' => $defaults['view'], - '#process' => array('expand_checkboxes', 'content_access_disable_checkboxes'), + '#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'), ); $form['per_role']['update'] = array('#type' => 'checkboxes', '#prefix' => '
', @@ -272,7 +305,7 @@ function content_access_role_based_form( '#options' => $roles, '#title' => t('Edit any content'), '#default_value' => $defaults['update'], - '#process' => array('expand_checkboxes', 'content_access_disable_checkboxes'), + '#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'), ); $form['per_role']['delete'] = array('#type' => 'checkboxes', '#prefix' => '
', @@ -280,18 +313,15 @@ function content_access_role_based_form( '#options' => $roles, '#title' => t('Delete any content'), '#default_value' => $defaults['delete'], - '#process' => array('expand_checkboxes', 'content_access_disable_checkboxes'), - ); - $form['per_role']['clearer'] = array( - '#value' => '
', + '#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'), ); $form['per_role']['view_own'] = array('#type' => 'checkboxes', - '#prefix' => '
', + '#prefix' => '
', '#suffix' => '
', '#options' => $roles, '#title' => t('View own content'), '#default_value' => $defaults['view_own'], - '#process' => array('expand_checkboxes', 'content_access_disable_checkboxes'), + '#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'), ); $form['per_role']['update_own'] = array('#type' => 'checkboxes', '#prefix' => '
', @@ -299,7 +329,7 @@ function content_access_role_based_form( '#options' => $roles, '#title' => t('Edit own content'), '#default_value' => $defaults['update_own'], - '#process' => array('expand_checkboxes', 'content_access_disable_checkboxes'), + '#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'), ); $form['per_role']['delete_own'] = array('#type' => 'checkboxes', '#prefix' => '
', @@ -307,7 +337,7 @@ function content_access_role_based_form( '#options' => $roles, '#title' => t('Delete own content'), '#default_value' => $defaults['delete_own'], - '#process' => array('expand_checkboxes', 'content_access_disable_checkboxes'), + '#process' => array('form_process_checkboxes', 'content_access_disable_checkboxes'), ); $form['per_role']['clearer'] = array( '#value' => '
', @@ -326,14 +356,14 @@ function content_access_disable_checkbox if (!in_array($key, $access_roles) && !($key != DRUPAL_ANONYMOUS_RID && in_array(DRUPAL_AUTHENTICATED_RID, $access_roles))) { $element[$key]['#disabled'] = TRUE; $element[$key]['#default_value'] = FALSE; - $element[$key]['#prefix'] = ' t('access content'))) .'">'; + $element[$key]['#prefix'] = ' t('access content'))) . '">'; $element[$key]['#suffix'] = ""; } - else if (in_array($key, $admin_roles) || ($key != DRUPAL_ANONYMOUS_RID && in_array(DRUPAL_AUTHENTICATED_RID, $admin_roles))) { + elseif (in_array($key, $admin_roles) || ($key != DRUPAL_ANONYMOUS_RID && in_array(DRUPAL_AUTHENTICATED_RID, $admin_roles))) { // Fix the checkbox to be enabled for users with administer node privileges $element[$key]['#disabled'] = TRUE; $element[$key]['#default_value'] = TRUE; - $element[$key]['#prefix'] = ' t('administer nodes'))) .'">'; + $element[$key]['#prefix'] = ' t('administer nodes'))) . '">'; $element[$key]['#suffix'] = ""; } } @@ -345,20 +375,19 @@ function content_access_disable_checkbox * Formapi #after_build callback, that disables checkboxes for roles without access to content. */ function content_access_force_permissions($element, &$form_state) { + $type = isset( $form_state['node'] ) ? $form_state['node']->type : $form_state['type'] ; foreach (array('update', 'update_own', 'delete', 'delete_own') as $op) { - foreach (content_access_get_settings($op, $form_state['node']->type) as $rid) { + foreach (content_access_get_settings($op, $type) as $rid) { $element[$op][$rid]['#disabled'] = TRUE; $element[$op][$rid]['#attributes']['disabled'] = 'disabled'; $element[$op][$rid]['#value'] = TRUE; - $element[$op][$rid]['#prefix'] = ''; + $element[$op][$rid]['#prefix'] = ''; $element[$op][$rid]['#suffix'] = ""; } } return $element; } - - /** * Submit callback for the user permissions form. * Trigger changes to node permissions to rebuild our grants. @@ -395,8 +424,8 @@ function _content_access_get_node_permis * Detaches all our ACLs for the nodes of the given type. */ function _content_access_remove_acls($type) { - $result = db_query("SELECT n.nid FROM {node} n WHERE type = '%s'", $type); - while ($node = db_fetch_object($result)) { + $result = db_query("SELECT n.nid FROM {node} n WHERE type = ':type'", array( 'type' => $type)); + foreach ( $result as $node ) { acl_node_clear_acls($node->nid, 'content_access'); } } Index: content_access.css =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/Attic/content_access.css,v retrieving revision 1.1 diff -u -p -r1.1 content_access.css --- content_access.css 17 May 2007 20:32:07 -0000 1.1 +++ content_access.css 11 Dec 2010 00:46:22 -0000 @@ -5,3 +5,7 @@ float: left; overflow: hide; } + +.content_access-div.new-row { + clear: left; +} Index: content_access.info =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/Attic/content_access.info,v retrieving revision 1.1.4.1 diff -u -p -r1.1.4.1 content_access.info --- content_access.info 30 Jun 2008 08:56:27 -0000 1.1.4.1 +++ content_access.info 11 Dec 2010 00:46:22 -0000 @@ -2,4 +2,9 @@ name = Content Access description = Provides flexible content access control package = Access control -core = "6.x" \ No newline at end of file +core = "7.x" + +files[] = content_access.module +files[] = content_access.install +files[] = content_access.admin.inc +files[] = content_access.rules.inc Index: content_access.install =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/Attic/content_access.install,v retrieving revision 1.1.4.3 diff -u -p -r1.1.4.3 content_access.install --- content_access.install 28 Oct 2008 13:36:17 -0000 1.1.4.3 +++ content_access.install 11 Dec 2010 00:46:22 -0000 @@ -2,21 +2,9 @@ // $Id: content_access.install,v 1.1.4.3 2008/10/28 13:36:17 fago Exp $ /** - * Implementation of hook_install(). + * @file + * Install, update and uninstall functions for the content_access module. */ -function content_access_install() { - // Create tables. - drupal_install_schema('content_access'); -} - -/** - * Implementation of hook_uninstall(). - */ -function content_access_uninstall() { - variable_del('content_access_settings'); - // Remove tables. - drupal_uninstall_schema('content_access'); -} /** * Implementation of hook_schema(). @@ -60,7 +48,7 @@ function content_access_update_6001() { if (is_numeric($rid)) { $permissions[$rid][ content_access_get_permission_by_op($op, $type) ] = TRUE; } - else if ($rid == 'author') { + elseif ($rid == 'author') { // CA 5.x let authors access, but only if they were authenticated. So we set the d6 permissions like this. $permissions[DRUPAL_AUTHENTICATED_RID][ content_access_get_permission_by_op($op . '_own', $type) ] = TRUE; } Index: content_access.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/Attic/content_access.module,v retrieving revision 1.1.2.9.2.22 diff -u -p -r1.1.2.9.2.22 content_access.module --- content_access.module 2 Sep 2010 13:13:33 -0000 1.1.2.9.2.22 +++ content_access.module 11 Dec 2010 00:46:22 -0000 @@ -13,30 +13,29 @@ function content_access_menu() { $items = array(); $items['node/%node/access'] = array( - 'title' => 'Access control', + 'title' => 'Access Control', 'page callback' => 'drupal_get_form', 'page arguments' => array('content_access_page', 1), 'access callback' => 'content_access_node_page_access', 'access arguments' => array(1), 'file' => 'content_access.admin.inc', + 'theme callback' => '_node_custom_theme', 'weight' => 3, 'type' => MENU_LOCAL_TASK ); - foreach (node_get_types('types', NULL, TRUE) as $type) { - $type_url_str = str_replace('_', '-', $type->type); - $items['admin/content/node-type/'. $type_url_str .'/access'] = array( - 'title' => 'Access control', - 'description' => 'Configure content access control.', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('content_access_admin_settings', $type->type), - 'access callback' => 'content_access_admin_settings_access', - 'access arguments' => array(), - 'type' => MENU_LOCAL_TASK, - 'file' => 'content_access.admin.inc', - 'weight' => 1, - ); - } + $items['admin/structure/types/manage/%node_type/access'] = array( + 'title' => 'Access Control', + 'description' => 'Configure content access control.', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('content_access_admin_settings', 4 ), + 'access callback' => 'content_access_admin_settings_access', + 'access arguments' => array(), + 'type' => MENU_LOCAL_TASK, + 'file' => 'content_access.admin.inc', + 'theme callback' => '_node_custom_theme', + 'weight' => 1, + ); return $items; } @@ -52,13 +51,24 @@ function content_access_init() { if (variable_get('node_admin_theme', '0') && arg(0) == 'node' && arg(2) == 'access') { global $custom_theme; $custom_theme = variable_get('admin_theme', '0'); - drupal_add_css(drupal_get_path('module', 'system') .'/admin.css', 'module'); + drupal_add_css(drupal_get_path('module', 'system') . '/admin.css', 'module'); } } +/** + * Implements hook_admin_paths(). + */ +function content_access_admin_paths() { + $paths = array( + 'node/*/access' => TRUE, + ); + return $paths; +} + function content_access_node_page_access($node) { global $user; - return content_access_get_settings('per_node', $node->type) && user_access('grant content access') || content_access_get_settings('per_node', $node->type) && (user_access('grant own content access') && ($user->uid == $node->uid)); + return content_access_get_settings('per_node', $node->type) && user_access('grant content access') || + content_access_get_settings('per_node', $node->type) && (user_access('grant own content access') && ($user->uid == $node->uid)); } function content_access_admin_settings_access() { @@ -68,8 +78,11 @@ function content_access_admin_settings_a /** * Implementation of hook_perm(). */ -function content_access_perm() { - return array('grant content access', 'grant own content access'); +function content_access_permission() { + return array( + 'grant content access' => array( 'title' => 'Grant Content Access', 'description' => 'View and modify content access for any nodes' ), + 'grant own content access' => array( 'title' => 'Grant Own Content Access', 'description' => 'View and modify content access for own nodes' ) + ); } /** @@ -95,19 +108,21 @@ function content_access_node_access_reco $grants = array(); foreach (array('view', 'update', 'delete') as $op) { foreach (content_access_get_rids_per_node_op($op, $node) as $rid) { - $grants[$rid]['grant_'. $op] = 1; + $grants[$rid]['grant_' . $op] = 1; } } foreach ($grants as $rid => $grant) { $grants[$rid] = content_access_proccess_grant($grant, $rid, $node); } + // Care for the author grant. $grant = array(); foreach (array('view', 'update', 'delete') as $op) { // Get all roles that have access to use $op on this node. $any_roles = drupal_map_assoc(content_access_per_node_setting($op, $node)); + $any_roles = $any_roles ? $any_roles : array(); $any_roles += ($op != 'view') ? content_access_get_settings($op, $node->type) : array(); - $grant['grant_'. $op] = content_access_own_op($node, $any_roles, content_access_get_rids_per_node_op($op .'_own', $node)); + $grant['grant_' . $op] = content_access_own_op($node, $any_roles, content_access_get_rids_per_node_op($op . '_own', $node)); } if (array_filter($grant)) { @@ -127,6 +142,7 @@ function content_access_node_access_reco else { content_access_optimize_grants($grants, $node); } + return $grants; } @@ -183,6 +199,7 @@ function content_access_get_settings($re } if (!isset($type)) { $settings = content_access_get_settings(); + return isset($settings[$return]) ? $settings[$return] : array(); } return array(); @@ -216,12 +233,14 @@ function content_access_available_settin * Defines default values for settings. */ function content_access_get_setting_defaults($setting, $type) { + global $user; + switch ($setting) { default: return array(); case 'view': case 'view_own': - return array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID); + return content_access_get_permission_access(content_access_get_permission_by_op($setting, $type)); case 'update': case 'update_own': case 'delete': @@ -229,6 +248,8 @@ function content_access_get_setting_defa return content_access_get_permission_access(content_access_get_permission_by_op($setting, $type)); case 'priority': return 0; + case 'per_node' : + return array(); } } @@ -254,14 +275,18 @@ function content_access_get_permission_b switch ($op) { default: return FALSE; + case 'view': + return 'view any ' . $type . ' content'; + case 'view_own': + return 'view own ' . $type . ' content'; case 'update': - return 'edit any '. $type .' content'; + return 'edit any ' . $type . ' content'; case 'update_own': - return 'edit own '. $type .' content'; + return 'edit own ' . $type . ' content'; case 'delete': - return 'delete any '. $type .' content'; + return 'delete any ' . $type . ' content'; case 'delete_own': - return 'delete own '. $type .' content'; + return 'delete own ' . $type . ' content'; } } @@ -317,8 +342,9 @@ function content_access_own_op($node, $a if (!isset($roles[$node->uid])) { $roles[$node->uid] = $node->uid ? array(DRUPAL_AUTHENTICATED_RID) : array(DRUPAL_ANONYMOUS_RID); - $result = db_query('SELECT rid FROM {users_roles} WHERE uid = %d', $node->uid); - while ($role = db_fetch_object($result)) { + $result = db_query('SELECT rid FROM {users_roles} WHERE uid = ?', array($node->uid)); +// while ($role = db_fetch_object($result)) { + foreach ( $result as $role ) { $roles[$node->uid][] = $role->rid; } } @@ -343,9 +369,12 @@ function content_access_own_op($node, $a */ function content_access_get_rids_per_node_op($op, $node) { $rids = content_access_per_node_setting($op, $node); + if ($permission = content_access_get_permission_by_op($op, $node->type)) { $perm_roles = content_access_get_permission_access($permission); + $rids = array_diff($rids, $perm_roles); + if (in_array(DRUPAL_AUTHENTICATED_RID, $perm_roles)) { return in_array(DRUPAL_ANONYMOUS_RID, $rids) ? array(DRUPAL_ANONYMOUS_RID, DRUPAL_AUTHENTICATED_RID) : array(DRUPAL_AUTHENTICATED_RID); } @@ -386,10 +415,23 @@ function content_access_per_node_setting * Saves custom per node settings in the own content_access table. */ function content_access_save_per_node_settings($node, $settings) { - db_query("UPDATE {content_access} SET settings = '%s' WHERE nid = %d", serialize($settings), $node->nid); - if (!db_affected_rows()) { - db_query("INSERT INTO {content_access} (nid, settings) VALUES(%d, '%s')", $node->nid, serialize($settings)); - } +// db_query("UPDATE {content_access} SET settings = '?' WHERE nid = ?", array(serialize($settings), $node->nid)); + + + $result = FALSE; + foreach ( db_query('select nid as count from {content_access} where nid = :nid ', array( 'nid' => $node->nid )) as $record ) { + $result = TRUE; + db_update( 'content_access' ) + ->condition( 'nid', $node->nid ) + ->fields(array( 'settings' => serialize($settings))) + ->execute(); + } + + if ($result === FALSE ) { + db_insert( 'content_access' ) + ->fields( array( 'nid' => $node->nid, 'settings' => serialize( $settings ))) + ->execute(); + } // Make content_access_per_node_setting() use the new settings content_access_per_node_setting(NULL, $node, $settings); } @@ -398,7 +440,7 @@ function content_access_save_per_node_se * Deletes all custom per node settings, so that content type defaults are used again. */ function content_access_delete_per_node_settings($node) { - db_query("DELETE FROM {content_access} WHERE nid = %d", $node->nid); + db_query("DELETE FROM {content_access} WHERE nid = :nid", array( 'nid' => $node->nid)); // Clear the cache. content_access_per_node_setting(NULL, $node, FALSE); // Delete possible acl settings @@ -414,10 +456,10 @@ function content_access_delete_per_node_ * Gets the content access acl id of the node. */ function content_access_get_acl_id($node, $op) { - $acl_id = acl_get_id_by_name('content_access', $op .'_'. $node->nid); + $acl_id = acl_get_id_by_name('content_access', $op . '_' . $node->nid); if (!$acl_id) { // Create one: - $acl_id = acl_create_new_acl('content_access', $op .'_'. $node->nid); + $acl_id = acl_create_new_acl('content_access', $op . '_' . $node->nid); } return $acl_id; } @@ -430,11 +472,13 @@ function content_access_get_acl_id($node * it will return an empty array. */ function content_access_get_per_node_settings($node) { - $settings = db_result(db_query("SELECT settings FROM {content_access} WHERE nid = %d", $node->nid)); - if (!$settings) { - return array(); +foreach( db_query("SELECT settings FROM {content_access} WHERE nid = ?", array( $node->nid)) as $record ) { + $settings = $record->settings; + if (!$settings) { + return array(); + } + return unserialize($settings); } - return unserialize($settings); } /** @@ -447,7 +491,7 @@ function content_access_optimize_grants( $rids = array('view' => array(), 'update' => array(), 'delete' => array()); foreach ($grants as $key => $grant) { foreach (array('view', 'update', 'delete') as $op) { - if (is_numeric($key) && !empty($grant['grant_'. $op])) { + if (is_numeric($key) && !empty($grant['grant_' . $op])) { $rids[$op][] = $key; } } @@ -472,7 +516,7 @@ function content_access_optimize_grants( continue; } foreach (array('view', 'update', 'delete') as $op) { - if ($grant['grant_'. $op] && in_array($key, $rids[$op])) { + if ($grant['grant_' . $op] && in_array($key, $rids[$op])) { //it's still here, so we can't remove this grant continue 2; } @@ -486,7 +530,15 @@ function content_access_optimize_grants( * Implementation of hook_node_type(): * Update settings on node type name change. */ + function content_access_node_type_delete( $info) { + content_access_node_type( 'delete', $info ); + } + function content_access_node_type_update( $info) { + content_access_node_type( 'update', $info ); + } + function content_access_node_type($op, $info) { + switch ($op) { case 'delete': $settings = content_access_get_settings(); @@ -543,4 +595,4 @@ function content_access_form_alter(&$for */ function _content_access_get_operations() { return array('view', 'view_own', 'update', 'update_own', 'delete', 'delete_own'); -} \ No newline at end of file +} Index: content_access.rules.inc =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/Attic/content_access.rules.inc,v retrieving revision 1.1.2.6 diff -u -p -r1.1.2.6 content_access.rules.inc --- content_access.rules.inc 31 Jul 2009 09:09:46 -0000 1.1.2.6 +++ content_access.rules.inc 11 Dec 2010 00:46:22 -0000 @@ -1,4 +1,5 @@ array( - 'label' => t('Grant content permissions by role'), - 'arguments' => array( + 'label' => t('Grant content permissions by role'), + 'parameter' => array( 'node' => array('type' => 'node', 'label' => t('Content')), ), - 'module' => 'Content access', + 'group' => 'Content access', + 'base' => 'content_access_action_grant_node_permissions' ), 'content_access_action_revoke_node_permissions' => array( 'label' => t('Revoke content permissions by role'), - 'arguments' => array( + 'parameter' => array( 'node' => array('type' => 'node', 'label' => t('Content')), ), - 'module' => 'Content access', + 'group' => 'Content access', + 'base' => 'content_access_action_revoke_node_permissions' ), 'content_access_action_reset_node_permissions' => array( 'label' => t('Reset content permissions'), - 'arguments' => array( + 'parameter' => array( 'node' => array('type' => 'node', 'label' => t('Content')), ), - 'module' => 'Content access', + 'group' => 'Content access', + 'base' => 'content_access_action_reset_node_permissions' ), ); if (module_exists('acl')) { $items += array( 'content_access_action_acl_grant_access' => array( 'label' => t('Grant access for a user'), - 'arguments' => array( + 'parameter' => array( 'node' => array('type' => 'node', 'label' => t('Content')), 'user' => array('type' => 'user', 'label' => t('User')), ), - 'module' => 'Content access', + 'group' => 'Content access', + 'base' => 'content_access_action_acl_grant_access' ), 'content_access_action_acl_revoke_access' => array( 'label' => t('Revoke access for a user'), - 'arguments' => array( + 'parameter' => array( 'node' => array('type' => 'node', 'label' => t('Content')), 'user' => array('type' => 'user', 'label' => t('User')), ), - 'module' => 'Content access', + 'group' => 'Content access', + 'base' => 'content_access_action_acl_revoke_access' ), ); } @@ -222,11 +228,11 @@ function content_access_rules_condition_ return array( 'content_access_condition_check_permissions' => array( 'label' => t('Check role based settings'), - 'arguments' => array( + 'parameter' => array( 'node' => array('type' => 'node', 'label' => t('Content')), ), 'help' => t('Evaluates to TRUE, if content access allows all selected operations for the given roles.'), - 'module' => 'Content access', + 'group' => 'Content access', ), ); } Index: tests/content_access.test =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/tests/Attic/content_access.test,v retrieving revision 1.1.4.2 diff -u -p -r1.1.4.2 content_access.test --- tests/content_access.test 2 Jan 2009 15:01:01 -0000 1.1.4.2 +++ tests/content_access.test 11 Dec 2010 00:46:22 -0000 @@ -6,7 +6,7 @@ * Automatd SimpleTest Case for content access module */ -require_once(drupal_get_path('module', 'content_access') .'/tests/content_access_test_help.php'); +require_once(drupal_get_path('module', 'content_access') . '/tests/content_access_test_help.php'); class ContentAccessModuleTestCase extends ContentAccessTestCase { @@ -43,12 +43,12 @@ class ContentAccessModuleTestCase extend // Logout admin and try to access the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node is not viewable'); // Login test user, view node, access must be denied $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node is not viewable'); // Login admin and grant access for viewing to the test user @@ -58,12 +58,12 @@ class ContentAccessModuleTestCase extend // Logout admin and try to access the node anonymously // access must be denied again $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node is not viewable'); // Login test user, view node, access must be granted $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertNoText(t('Access denied'), 'node is viewable'); // Login admin and enable per node access @@ -75,18 +75,18 @@ class ContentAccessModuleTestCase extend // Logout admin and try to access both nodes anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node1 is not viewable'); - $this->drupalGet('node/'. $this->node2->nid); + $this->drupalGet('node/' . $this->node2->nid); $this->assertText(t('Access denied'), 'node2 is not viewable'); // Login test user, view node1, access must be granted $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertNoText(t('Access denied'), 'node1 is viewable'); // View node2, access must be denied - $this->drupalGet('node/'. $this->node2->nid); + $this->drupalGet('node/' . $this->node2->nid); $this->assertText(t('Access denied'), 'node2 is not viewable'); // Login admin, swap permissions between content type and node2 @@ -100,18 +100,18 @@ class ContentAccessModuleTestCase extend // Logout admin and try to access both nodes anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node1 is not viewable'); - $this->drupalGet('node/'. $this->node2->nid); + $this->drupalGet('node/' . $this->node2->nid); $this->assertText(t('Access denied'), 'node2 is not viewable'); // Login test user, view node1, access must be denied $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node1 is not viewable'); // View node2, access must be granted - $this->drupalGet('node/'. $this->node2->nid); + $this->drupalGet('node/' . $this->node2->nid); $this->assertNoText(t('Access denied'), 'node2 is viewable'); } @@ -122,12 +122,12 @@ class ContentAccessModuleTestCase extend // Logout admin and try to edit the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertText(t('Access denied'), 'edit access denied for anonymous'); // Login test user, edit node, access must be denied $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertText(t('Access denied'), 'edit access denied for test user'); // Login admin and grant access for editing to the test user @@ -137,12 +137,12 @@ class ContentAccessModuleTestCase extend // Logout admin and try to edit the node anonymously // access must be denied again $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertText(t('Access denied'), 'edit access denied for anonymous'); // Login test user, edit node, access must be granted $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertNoText(t('Access denied'), 'node1 is editable'); // Login admin and enable per node access @@ -157,18 +157,18 @@ class ContentAccessModuleTestCase extend // Logout admin and try to edit both nodes anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertText(t('Access denied'), 'node1 is not editable'); - $this->drupalGet('node/'. $this->node2->nid .'/edit'); + $this->drupalGet('node/' . $this->node2->nid . '/edit'); $this->assertText(t('Access denied'), 'node2 is not editable'); // Login test user, edit node1, access must be granted $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertNoText(t('Access denied'), 'node1 is editable'); // Edit node2, access must be denied - $this->drupalGet('node/'. $this->node2->nid .'/edit'); + $this->drupalGet('node/' . $this->node2->nid . '/edit'); $this->assertText(t('Access denied'), 'node2 is not editable'); // Login admin, swap permissions between node1 and node2 @@ -181,18 +181,18 @@ class ContentAccessModuleTestCase extend // Logout admin and try to edit both nodes anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertText(t('Access denied'), 'node1 is not editable'); - $this->drupalGet('node/'. $this->node2->nid .'/edit'); + $this->drupalGet('node/' . $this->node2->nid . '/edit'); $this->assertText(t('Access denied'), 'node2 is not editable'); // Login test user, edit node1, access must be denied $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid .'/edit'); + $this->drupalGet('node/' . $this->node1->nid . '/edit'); $this->assertText(t('Access denied'), 'node1 is not editable'); // Edit node2, access must be granted - $this->drupalGet('node/'. $this->node2->nid .'/edit'); + $this->drupalGet('node/' . $this->node2->nid . '/edit'); $this->assertNoText(t('Access denied'), 'node2 is editable'); } @@ -203,12 +203,12 @@ class ContentAccessModuleTestCase extend // Logout admin and try to delete the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/delete'); + $this->drupalGet('node/' . $this->node1->nid . '/delete'); $this->assertText(t('Access denied'), 'delete access denied for anonymous'); // Login test user, delete node, access must be denied $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid .'/delete'); + $this->drupalGet('node/' . $this->node1->nid . '/delete'); $this->assertText(t('Access denied'), 'delete access denied for test user'); // Login admin and grant access for deleting to the test user @@ -219,12 +219,12 @@ class ContentAccessModuleTestCase extend // Logout admin and try to edit the node anonymously // access must be denied again $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/delete'); + $this->drupalGet('node/' . $this->node1->nid . '/delete'); $this->assertText(t('Access denied'), 'delete access denied for anonymous'); // Login test user, delete node, access must be granted $this->drupalLogin($this->test_user); - $this->drupalPost('node/'. $this->node1->nid .'/delete', array(), 'Delete'); + $this->drupalPost('node/' . $this->node1->nid . '/delete', array(), 'Delete'); $this->assertRaw(t('%node has been deleted', array('%node' => $this->node1->title)), 'Test node was deleted successfully by test user'); // Login admin and recreate test node1 @@ -242,18 +242,18 @@ class ContentAccessModuleTestCase extend // Logout admin and try to delete both nodes anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/delete'); + $this->drupalGet('node/' . $this->node1->nid . '/delete'); $this->assertText(t('Access denied'), 'node1 is not deletable'); - $this->drupalGet('node/'. $this->node2->nid .'/delete'); + $this->drupalGet('node/' . $this->node2->nid . '/delete'); $this->assertText(t('Access denied'), 'node2 is not deletable'); // Login test user, delete node1, access must be granted $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid .'/delete'); + $this->drupalGet('node/' . $this->node1->nid . '/delete'); $this->assertNoText(t('Access denied'), 'node1 is deletable'); // Delete node2, access must be denied - $this->drupalGet('node/'. $this->node2->nid .'/delete'); + $this->drupalGet('node/' . $this->node2->nid . '/delete'); $this->assertText(t('Access denied'), 'node2 is not deletable'); // Login admin, swap permissions between node1 and node2 @@ -266,18 +266,18 @@ class ContentAccessModuleTestCase extend // Logout admin and try to delete both nodes anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid .'/delete'); + $this->drupalGet('node/' . $this->node1->nid . '/delete'); $this->assertText(t('Access denied'), 'node1 is not deletable'); - $this->drupalGet('node/'. $this->node2->nid .'/delete'); + $this->drupalGet('node/' . $this->node2->nid . '/delete'); $this->assertText(t('Access denied'), 'node2 is not deletable'); // Login test user, delete node1, access must be denied $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node1->nid .'/delete'); + $this->drupalGet('node/' . $this->node1->nid . '/delete'); $this->assertText(t('Access denied'), 'node1 is not deletable'); // Delete node2, access must be granted - $this->drupalGet('node/'. $this->node2->nid .'/delete'); + $this->drupalGet('node/' . $this->node2->nid . '/delete'); $this->assertNoText(t('Access denied'), 'node2 is deletable'); } @@ -311,27 +311,27 @@ class ContentAccessModuleTestCase extend // Logout admin and try to access both nodes anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node1 is not viewable'); - $this->drupalGet('node/'. $this->node2->nid); + $this->drupalGet('node/' . $this->node2->nid); $this->assertText(t('Access denied'), 'node2 is not viewable'); // Login test user 1, view node1, access must be granted $this->drupalLogin($test_user1); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertNoText(t('Access denied'), 'node1 is viewable'); // View node2, access must be denied - $this->drupalGet('node/'. $this->node2->nid); + $this->drupalGet('node/' . $this->node2->nid); $this->assertText(t('Access denied'), 'node2 is not viewable'); // Login test user 2, view node1, access must be denied $this->drupalLogin($test_user2); - $this->drupalGet('node/'. $this->node1->nid); + $this->drupalGet('node/' . $this->node1->nid); $this->assertText(t('Access denied'), 'node1 is not viewable'); // View node2, access must be granted - $this->drupalGet('node/'. $this->node2->nid); + $this->drupalGet('node/' . $this->node2->nid); $this->assertNoText(t('Access denied'), 'node2 is viewable'); } Index: tests/content_access_acl.test =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/content_access/tests/Attic/content_access_acl.test,v retrieving revision 1.1.4.2 diff -u -p -r1.1.4.2 content_access_acl.test --- tests/content_access_acl.test 2 Jan 2009 15:01:01 -0000 1.1.4.2 +++ tests/content_access_acl.test 11 Dec 2010 00:46:23 -0000 @@ -6,7 +6,7 @@ * Automatd SimpleTest Case for using content access module with acl module */ -require_once(drupal_get_path('module', 'content_access') .'/tests/content_access_test_help.php'); +require_once(drupal_get_path('module', 'content_access') . '/tests/content_access_test_help.php'); class ContentAccessACLTestCase extends ContentAccessTestCase { @@ -58,17 +58,17 @@ class ContentAccessACLTestCase extends C $edit = array( 'acl[view][add]' => $this->test_user->name, ); - $this->drupalPost('node/'. $this->node->nid .'/access', $edit, t('Add User')); + $this->drupalPost('node/' . $this->node->nid . '/access', $edit, t('Add User')); $this->drupalPost(NULL, array(), t('Submit')); // Logout admin, try to access the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node->nid); + $this->drupalGet('node/' . $this->node->nid); $this->assertText(t('Access denied'), 'node is not viewable'); // Login test user, view access should be allowed now $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node->nid); + $this->drupalGet('node/' . $this->node->nid); $this->assertNoText(t('Access denied'), 'node is viewable'); // Login admin and disable per node access @@ -77,12 +77,12 @@ class ContentAccessACLTestCase extends C // Logout admin, try to access the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node->nid); + $this->drupalGet('node/' . $this->node->nid); $this->assertText(t('Access denied'), 'node is not viewable'); // Login test user, view access should be denied now $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node->nid); + $this->drupalGet('node/' . $this->node->nid); $this->assertText(t('Access denied'), 'node is not viewable'); } @@ -104,17 +104,17 @@ class ContentAccessACLTestCase extends C $edit = array( 'acl[update][add]' => $this->test_user->name, ); - $this->drupalPost('node/'. $this->node->nid .'/access', $edit, t('Add User')); + $this->drupalPost('node/' . $this->node->nid . '/access', $edit, t('Add User')); $this->drupalPost(NULL, array(), t('Submit')); // Logout admin, try to edit the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node->nid .'/edit'); + $this->drupalGet('node/' . $this->node->nid . '/edit'); $this->assertText(t('Access denied'), 'node is not editable'); // Login test user, edit access should be allowed now $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node->nid .'/edit'); + $this->drupalGet('node/' . $this->node->nid . '/edit'); $this->assertNoText(t('Access denied'), 'node is editable'); // Login admin and disable per node access @@ -123,12 +123,12 @@ class ContentAccessACLTestCase extends C // Logout admin, try to edit the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node->nid .'/edit'); + $this->drupalGet('node/' . $this->node->nid . '/edit'); $this->assertText(t('Access denied'), 'node is not editable'); // Login test user, edit access should be denied now $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node->nid .'/edit'); + $this->drupalGet('node/' . $this->node->nid . '/edit'); $this->assertText(t('Access denied'), 'node is not editable'); } @@ -150,17 +150,17 @@ class ContentAccessACLTestCase extends C $edit = array( 'acl[delete][add]' => $this->test_user->name, ); - $this->drupalPost('node/'. $this->node->nid .'/access', $edit, t('Add User')); + $this->drupalPost('node/' . $this->node->nid . '/access', $edit, t('Add User')); $this->drupalPost(NULL, array(), t('Submit')); // Logout admin, try to delete the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node->nid .'/delete'); + $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertText(t('Access denied'), 'node is not deletable'); // Login test user, delete access should be allowed now $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node->nid .'/delete'); + $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertNoText(t('Access denied'), 'node is deletable'); // Login admin and disable per node access @@ -169,12 +169,12 @@ class ContentAccessACLTestCase extends C // Logout admin, try to delete the node anonymously $this->drupalLogout(); - $this->drupalGet('node/'. $this->node->nid .'/delete'); + $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertText(t('Access denied'), 'node is not deletable'); // Login test user, delete access should be denied now $this->drupalLogin($this->test_user); - $this->drupalGet('node/'. $this->node->nid .'/delete'); + $this->drupalGet('node/' . $this->node->nid . '/delete'); $this->assertText(t('Access denied'), 'node is not deletable'); }