--- protected_node/protected_node.module.orig 2008-08-04 16:38:26.000000000 +0200 +++ protected_node/protected_node.module 2008-08-05 11:10:30.000000000 +0200 @@ -41,118 +41,386 @@ * Implementation of hook_form_alter() */ function protected_node_form_alter(&$form, &$form_state, $form_id) { - // check if this node type should be protected - $types_to_protect = variable_get('protected_node_nodetypes', array()); - if (!in_array($form['type']['#value'], $types_to_protect)) { - return; + // check if this node type should be protected + $types_to_protect = variable_get('protected_node_nodetypes', array()); + if (!in_array($form['type']['#value'], $types_to_protect)) { + return; } - - if ($form['#id'] == 'node-form' && (user_access('edit protected content') || user_access('edit protected '.$form['type']['#value']) )) - { - $form['protected_node'] = array( - '#type' => 'fieldset', - '#description' => t('Here if you check the checkbox and provide a password your newly created node will be password protected.'), - '#title' => t('Protected node'), - '#collapsible' => TRUE, - '#collapsed' => TRUE - ); - $form['protected_node']['is_protected'] = array( - '#type' => 'checkbox', - '#title' => t('Node is protected'), - '#description' => t('Check here if this content should be protected by a password.'), - '#default_value' => $form['#node']->is_protected, - ); - $form['protected_node']['password'] = array( - '#type' => 'password_confirm', - '#description' => t('Enter the node password here.'), - '#size' => 20, - ); - } + + if ($form['#id'] == 'node-form' && (user_access('edit protected content') || user_access('edit protected '.$form['type']['#value']) )) + { + drupal_add_js(drupal_get_path('module', 'protected_node') .'/protected_node.js'); + $form['protected_node'] = array( + '#type' => 'fieldset', + '#description' => t('Here if you check the checkbox and provide a password your newly created node will be password protected.'), + '#title' => t('Protected node'), + '#collapsible' => TRUE, + '#collapsed' => TRUE + ); + + if (protected_global_inherit_flag()) { + if (isset($form['#node']->protected_inherit)) { + $inherits = $form['#node']->protected_inherit; + } + else { + $inherits = protected_default_inherit_flag(); + } + + $form['protected_node']['protected_inherit'] = array( + '#type' => 'checkbox', + '#title' => t('Inherit protection from menu parent'), + '#description' => t('If the node has a menu item, it will inherit the password from the node of the menu parent item. If the node has no menu item, it will be unprotected. Uncheck to give the node its own password.'), + '#default_value' => $inherits, + ); + $form['protected_node']['was_protected_inherit'] = array( + '#type' => 'hidden', + '#default_value' => $form['#node']->was_protected_inherit, + ); + $form['protected_node']['protected_old_plid'] = array( + '#type' => 'hidden', + '#default_value' => $form['#node']->protected_old_plid, + ); + $form['protected_node']['protected_old_children'] = array( + '#type' => 'hidden', + '#default_value' => $form['#node']->protected_old_children, + ); + $form['protected_node']['protected_pass_inheritance'] = array( + '#type' => 'checkbox', + '#title' => t('Activate inheritance for menu descendants once.'), + '#description' => t('If the node has a menu item, activate inheritance for all descendants, no matter what settings they had before. If the node has (or inherits) a password, this will protect the whole menu subtree, or unprotect it otherwise.'), + '#default_value' => FALSE, + ); + + } + $form['protected_node']['is_protected'] = array( + '#type' => 'checkbox', + '#title' => t('Node is protected'), + '#description' => t('Check here if this content should be protected by a password.'), + '#default_value' => $form['#node']->is_protected, + ); + $form['protected_node']['was_protected'] = array( + '#type' => 'hidden', + '#default_value' => $form['#node']->was_protected, + ); + $form['protected_node']['password'] = array( + '#type' => 'password_confirm', + '#description' => t('Enter the node password here.'), + '#size' => 20, + ); + } } /** * Implementation of hook_nodeapi() */ function protected_node_nodeapi(&$node, $op, $arg = 0, $page = 0) { - global $user, $protected_node_salt, $base_root; - - $types_to_protect = variable_get('protected_node_nodetypes', array()); - if (!in_array($node->type, $types_to_protect)) { - return; + global $user, $protected_node_salt, $base_root; + + $types_to_protect = variable_get('protected_node_nodetypes', array()); + if (!in_array($node->type, $types_to_protect)) { + return; + } + + switch ($op) { + case 'load': + $protected = get_protected($node->nid); + $output['is_protected'] = is_protected($protected); + $output['was_protected'] = $output['is_protected']; + + if (protected_global_inherit_flag()) { + // Changing the inherit state can cause a change for a complete subtree, + // so we need the former state. If there is no DB entry yet, we use the + // global default, but set was_protected_inherit to FALSE so that we inherit + // if the global default flag was turned on since we were last updated. + if (!$protected) { + $_inherit = protected_default_inherit_flag(); + $output['was_protected_inherit'] = FALSE; + } + else { + $_inherit = protected_inherit($protected); + $output['was_protected_inherit'] = $_inherit; + } + $output['protected_inherit'] = $_inherit; + + // Deleting or moving a node can make its children inherit new stuff from the + // nodes former parent. Here is the only place we have information about our + // menu children for sure. In 'update' and 'delete' they can have changed already + // by the node and menu module ('submit' doesn't help for 'delete'). + $item = protected_get_menu_info($node->nid); + // If we have a menu, save our parent and (possibly) children. + if ($item) { + $output['protected_old_plid'] = $item['plid']; + if ($item['has_children']) { + $output['protected_old_children'] = implode(",", protected_get_children($item['mlid'])); + } + } + } + return $output; + break; + + case 'delete': + if ($protected = get_protected($node->nid)) { + delete_protected($node->nid); + // Our menu children move below our former menu parent. + // If we inherited ourself, we already passed these information to our children before. + // Only if we didn't inherit, we must pass our parents settings to our children. + if (protected_global_inherit_flag() && !protected_inherit($protected)) { + if ($node->protected_old_children) { + if (!($parent_pwd = get_protected_passwd(get_protected_by_mlid($node->protected_old_plid)))) { + $parent_pwd = ''; + } + protected_propagate_children_list(split(",", $node->protected_old_children), $parent_pwd); + } + } + } + break; + + case 'insert': + // $pwd will be defined if the protection box is checked *and* a passwort was entered. + $pwd = protected_enc_pwd($node); + + // If inherit is checked for a new node, we ignore possible user input for is_protected + // and password. Instead we check the parent if the node has a menu entry. + if (protected_global_inherit_flag()) { + if ($node->protected_inherit) { + // Will return FALSE if we have no menu entry or no parent with passwd. + $pwd = protected_get_parent_pwd($node); + } + // If no password found (neither fetched from the parent nor entered) + // store the empty string because we save the inherit flag anyway. + if (!is_string($pwd)) { + $pwd = ''; + } + } + if (is_string($pwd)) { + insert_protected($node->nid, $pwd, $node->protected_inherit); + } + break; + + case 'validate': + if ($node->is_protected && (!empty($node->password) || isset($_SESSION['_protected_node']['entered_password']))) { + if (!isset($_SESSION['_protected_node']['entered_password'])) { + $_SESSION['_protected_node']['entered_password'] = $node->password; + } + } + break; + + case 'update': + // Old behaviour if inheritance is disabled. More efficient to have separate code for this. + if (!protected_global_inherit_flag()) { + // Protected flag was switched off? + if ($node->was_protected && !$node->is_protected) { + delete_protected($node->nid); + } + if ($node->is_protected && ($pwd = protected_enc_pwd($node))) { + update_protected($node->nid, $pwd, FALSE); + } + break; + } + + // Inheritance + // ----------- + // In general, we inherit from our parent (if checked) and pass + // those information to those of our children that inherit from + // us. So we must consider all cases in which the relation between + // us and our parent or children changes. This depends on our menu + // settings which could change during an update, and on the status + // of the inherit and the protection checkboxes. + + // We must consider the following situations about our menu entry: + // 1. We have a menu entry and the same parent as before. + // 2. We have no menu entry and didn't have one before. + // 3. Our parent menu has changed. Our children move with us. + // 4. Our menu entry was deleted. Our former children move below our + // former parent. We saved those information in 'load'. + // 5. We got a menu entry and didn't have one before. + + if (is_numeric($node->protected_old_plid)) { + $oldparent = $node->protected_old_plid; + $had_menu = TRUE; + } + if (!empty($node->menu['link_title']) && !$node->menu['delete']) { + $newparent = $node->menu['plid']; + $has_menu = TRUE; + } + + // For 4, it's like in nodeapi('delete'), our children move one level up. + // Consider only if we didn't inherit so far (cf. 'delete'). + if ($had_menu && !$has_menu && $node->protected_old_children && !$node->was_protected_inherit) { + if (!($parent_pwd = get_protected_passwd(get_protected_by_mlid($oldparent)))) { + $parent_pwd = ''; + } + protected_propagate_children_list(split(",", $node->protected_old_children), $parent_pwd); + } + + // Now consider the node itself. + if ($node->is_protected) { + // If protected: Has a (new or changed) password been entered? + // $pwd is FALSE if not, or contains the encrypted string otherwise. + $pwd = protected_enc_pwd($node); + } + + // Inherit checkbox is off + // ----------------------- + // If our menu entry didn't change (case "1" from above) we distinguish on the + // state of the protection checkbox: + // If it was on and now + // 1.1 is turned off -> update db and propagate to our children. + // 1.2 is still on and password changed -> update db and propagate. + // 1.3 is still on and no password entered -> We know that a password exists. + // If inherit was turned off, update the db flag and keep the password. + // Unless "pass inheritance once", no need to pass to children, they + // still inherit the same from us. + // If it was off and now + // 1.4. is still off or was turned on but without password entered: + // If inherit was *turned off* -> update db entry, + // don't propagate to children (unless "pass inheritance once"). + // 1.5. is turned on with password entered -> update/insert and propagate. + // + // For case 2: We have no children and no parents. + // Same as 1, just that we don't propagate to children. + // + // For 3: If we don't inherit, it doesn't matter if the parent menu + // changed or not, so it's the same as for 1. + // + // For 4: Our parent menu was deleted. We have no parent and no children now, + // so it's the same code as for 2. + // + // For 5: If we don't inherit, a new menu entry is just like a changed menu entry, + // just that we know that we have no children. + // + // + if (!$node->protected_inherit) { + // 1.1. protected turned off? + if (!$node->is_protected && $node->was_protected) { + update_protected($node->nid, '', FALSE); + // Also delete protection for inheriting children. + // Exclude 2/4 & 5. No children if no or new menu. + if ($has_menu && $had_menu) { + protected_propagate_children($node->menu, '', $node->protected_pass_inheritance); } - - switch ($op) { - case 'load': - $output['is_protected'] = db_result(db_query('SELECT count(*) FROM {protected_nodes} WHERE nid = %d', $node->nid)) > 0; - $output['was_protected'] = $output['is_protected']; - return $output; - break; - case 'delete': - db_query('DELETE FROM {protected_nodes} WHERE nid = %d', $node->nid); - break; - case 'insert': - if ($node->is_protected && (!empty($node->password) || isset($_SESSION['_protected_node']['entered_password']))) { - $pwd = isset($_SESSION['_protected_node']['entered_password']) ? $_SESSION['_protected_node']['entered_password'] : $node->password; - unset($_SESSION['_protected_node']['entered_password']); - db_query('INSERT INTO {protected_nodes} (nid, passwd) VALUES (%d, \'%s\')', $node->nid, sha1($protected_node_salt.$pwd)); - } - break; - case 'validate': - if ($node->is_protected && (!empty($node->password) || isset($_SESSION['_protected_node']['entered_password']))) { - if (!isset($_SESSION['_protected_node']['entered_password'])) { - $_SESSION['_protected_node']['entered_password'] = $node->password; - } - } - break; - case 'update': - // Protected flag was switched off? - if ($node->was_protected && !$node->is_protected) { - db_query('DELETE FROM {protected_nodes} WHERE nid = %d', $node->nid); - } - - if ($node->is_protected && (!empty($node->password) || isset($_SESSION['_protected_node']['entered_password']))) { - if (db_result(db_query('SELECT count(*) FROM {protected_nodes} WHERE nid = %d', $node->nid)) > 0) { - $pwd = isset($_SESSION['_protected_node']['entered_password']) ? $_SESSION['_protected_node']['entered_password'] : $node->password; - unset($_SESSION['_protected_node']['entered_password']); - db_query('UPDATE {protected_nodes} SET passwd = \'%s\' WHERE nid = %d', sha1($protected_node_salt.$pwd), $node->nid); - } - else { - protected_node_nodeapi($node, 'insert', $arg, $page); - } - } - break; - case 'view': - if ($node->is_protected) { - // If we have been accessed from cron.php (f.e. search indexing) - if (variable_get('cron_semaphore', FALSE)) { - $node->title = ''; - $node->teaser = ''; - $node->body = ''; - $node->content = array(); - } - else { - if (!$user->uid && variable_get('cache', 0)) { - $GLOBALS['conf']['cache'] = FALSE; - } - - if ($node->uid !== $user->uid) { - // If node is protected and not teaser nor page view and not owner of node - if (!isset($_SESSION['_protected_node']['passwords'][$node->nid])) { - if (!$arg) { - $_SESSION['_protected_node']['current'] = $node->nid; - drupal_goto('protected-node', 'destination=' . $_GET['q']); - } - else { - $node->teaser = ''; - $node->body = ''; - $node->content = array(); - } - } - } - } - } - break; - } + } + // 1.2/1.5 + elseif ($node->is_protected && is_string($pwd)) { + update_protected($node->nid, $pwd, FALSE); + // Exclude 2/4 & 5. No children if no or new menu. + if ($has_menu && $had_menu) { + protected_propagate_children($node->menu, $pwd, $node->protected_pass_inheritance); + } + } + // 1.3/1.4. Inherit turned off? + elseif ($node->was_protected_inherit) { + // 1.4. + if (!$node->is_protected || ($node->is_protected && !$node->was_protected && !is_string($pwd))) { + update_protected($node->nid, '', FALSE); + // Only for 1. and 3., so check for menu. + if ($node->protected_pass_inheritance && $has_menu && $had_menu) { + protected_propagate_children($node->menu, '', $node->protected_pass_inheritance); + } + } + // 1.3 + else { + update_protected($node->nid, FALSE, FALSE); + // Only for 1. and 3., so check for menu. + if ($node->protected_pass_inheritance && $has_menu && $had_menu) { + // We can't be sure we have a password stored. + if (!($pwd = get_protected_passwd(get_protected($node->nid)))) { + $pwd = ''; + } + protected_propagate_children($node->menu, $pwd, $node->protected_pass_inheritance); + } + } + } + // Nothing has changed, just activated the "pass inheritance" box. + else { + if ($node->protected_pass_inheritance && $has_menu && $had_menu) { + // We can't be sure we have a password stored. + if (!($pwd = get_protected_passwd(get_protected($node->nid)))) { + $pwd = ''; + } + protected_propagate_children($node->menu, $pwd, $node->protected_pass_inheritance); + } + } + } + + // Inherit box is on. + // ------------------ + else { + // 1: + // 1.6 if inherit was checked before, nothing changes (possibly consider + // children if "pass inheritance once"). + // 1.7 if it was turned on, fetch the password from the parent, store it for us + // and propagate to our children (or delete ours and the children ones + // if the parent is not protected. + // + // 2: If inherit was checked before, nothing changes. If it was turned on, + // we inherit from a non-existing parent => delete protection. + // + // 3: In addition to 1.7 and 2. fetch and update even if inherit was already on + // before because we get new data from a new parent. + // + // 4: Our parent menu was deleted. We have no parent and no children now, + // so it's the same code as for 2. + // + // 5: New menu = changed menu, but no children. Same as 3, without child propagation. + + // (1.7 and 2/4) || 3(=changed menu entry) + if (!$node->was_protected_inherit + || ($has_menu && $had_menu && $oldparent !== $newparent)) { + // Flag used to avoid writing db for 1.6. + $normal_conditions = TRUE; + } + if ($normal_conditions || $node->protected_pass_inheritance) { + if (!$parent_pwd = protected_get_parent_pwd($node)) { + // Store empty password together with inherit flag. + $parent_pwd = ''; + } + if ($normal_conditions) { + update_protected($node->nid, $parent_pwd, TRUE); + } + // Exclude 2/4 & 5. No children if no or first-time menu. + if ($has_menu && $had_menu) { + protected_propagate_children($node->menu, $parent_pwd, $node->protected_pass_inheritance); + } + } + } + break; + + case 'view': + if ($node->is_protected) { + // If we have been accessed from cron.php (f.e. search indexing) + if (variable_get('cron_semaphore', FALSE)) { + $node->title = ''; + $node->teaser = ''; + $node->body = ''; + $node->content = array(); + } + else { + if (!$user->uid && variable_get('cache', 0)) { + $GLOBALS['conf']['cache'] = FALSE; + } + + if ($node->uid !== $user->uid) { + // If node is protected and not teaser nor page view and not owner of node, + // we can view it if its own password has been entered and stored. But if + // the node inherits the password, we can also view it if the password was + // entered for any of it parents. + if (!protected_passwd_cached($node)) { + if (!$arg) { + $_SESSION['_protected_node']['current'] = $node->nid; + drupal_goto('protected-node', 'destination=' . $_GET['q']); + } + else { + $node->teaser = ''; + $node->body = ''; + $node->content = array(); + } + } + } + } + } + break; + } } /** @@ -278,6 +546,368 @@ '#default_value' => variable_get('protected_node_info', ''), '#description' => t('You can use node type tokens from the token module if you have installed it previously.'), ); + + $description = t('If a node has a menu item, it can inherit the protection state and password from the node of the parent menu item. If you enable inheritance, you can define the default behaviour here, but also turn it on or off for every node in the node edit form. Inheritance can protect a whole menu subtree with the same password. When you change a passwort of a protected node, the whole menu subtree will inherit the new password unless you unchecked the inherit box for some nodes. '); + $description .= '

'. t('Warning: '); + $description .= t('Turning the inheritance flag off will remove all saved inheritance flags from the database.'); + + $form['protected_node_inheritance'] = array( + '#type' => 'checkboxes', + '#title' => t('Inheritance settings'), + '#options' => array( + 'enable' => t('Enable inheritance'), + 'default' => t('Use inheritance by default'), + ), + '#default_value' => variable_get('protected_node_inheritance', array()), + '#description' => $description, + ); + + // Store the old inheritance value. When turned off => delete flags. + $form['protected_node_was_inheritance'] = array( + '#type' => 'hidden', + '#default_value' => protected_global_inherit_flag(), + ); + + + // We need to hook into the submit form to delete inherit flags. + $myform = system_settings_form($form); + // ADD our submit form, don't replace. + $myform['#submit'][] = 'protected_node_admin_settings_submit'; + return $myform; +} + +/** + * Delete all inheritance flag when inheritance is disabled. If node + * node protections were changed after inheritance was turned off, + * the state would be too disturbing when inheritance was turned on + * again. E.g. parent nodes could have new passwords and we couldn't + * realize that when updating children nodes without comparing against + * the parent settings every time. + * If we ever change this, we must implement a check against the parent + * password for e.g. 1.6 in nodeapi 'update'. + */ +function protected_node_admin_settings_submit($form, &$form_state) { + if (!$form_state['values']['protected_node_inheritance']['enable'] && + $form_state['values']['protected_node_was_inheritance']) { + + // Without inheritance, we can delete all entries that don't have a password. + db_query("DELETE from {protected_nodes} where passwd = ''"); + + // For the remaining, disabled inheritance. + db_query("UPDATE {protected_nodes} SET inherit = 0"); + + drupal_set_message('Inheritance flags were removed for all nodes.'); + } +} + + +/** + * Fetch the global inheritance settings. + */ +function protected_global_inherit_flag() { + return in_array('enable', variable_get('protected_node_inheritance', array())); + +} +function protected_default_inherit_flag() { + return in_array('default', variable_get('protected_node_inheritance', array())); +} + + +/********************* + * Database helpers * + *********************/ + + +/** + * Return all protection information for a node. Might return FALSE. + */ +function get_protected($nid) { + return db_fetch_object(db_query('SELECT * FROM {protected_nodes} WHERE nid = %d', $nid)); +} + + +/** + * Remove an entry from the protected_nodes table. + */ +function delete_protected($nid) { + db_query('DELETE FROM {protected_nodes} WHERE nid = %d', $nid); +} + +/** + * Add a protection entry. + */ +function insert_protected($nid, $pwd, $inherit) { + db_query('INSERT INTO {protected_nodes} (nid, passwd, inherit) VALUES (%d, \'%s\', %d)', $nid, $pwd, $inherit); +} + + +/** + * Update protected node entry. + */ +function update_protected($nid, $pwd, $inherit) { + if ($protected = get_protected($nid)) { + // If we changed only the inherit flag, we didn't pass a password. + if (!is_string($pwd)) { + $pwd = $protected->passwd; + }; + db_query('UPDATE {protected_nodes} SET passwd = \'%s\', inherit = %d WHERE nid = %d', $pwd, $inherit, $nid); + } + else { + insert_protected($nid, $pwd, $inherit); + } +} + + + +/********************************** + * Protection properties helpers * + **********************************/ + +/** + * Return passwd string for a protected node. Parameter might be FALSE. + */ +function get_protected_passwd($protected) { + if ($protected) { + return $protected->passwd; + } + return FALSE; +} + +/** + * Check if a node is protected. Parameter might be FALSE. +*/ +function is_protected($protected) { + return ($protected && !empty($protected->passwd)); +} + +/** + * Check if a node inherits from the menu parent. Parameter might be FALSE. +*/ +function protected_inherit($protected) { + if ($protected) { + return $protected->inherit; + } + return FALSE; +} + +/** + * Test if a password was entered and return it encrypted. If the protected box + * is not checked, ignore the password. + */ +function protected_enc_pwd($node) { + if (isset($_SESSION['_protected_node']['entered_password'])){ + $pwd = $_SESSION['_protected_node']['entered_password']; + unset($_SESSION['_protected_node']['entered_password']); + } + else { + if ($node->is_protected && !empty($node->password)) { + $pwd = $node->password; + } + } + if (is_string($pwd)) { + return sha1($protected_node_salt.$pwd); + } + return FALSE; +} + + + +/***************************** + * Menu inheritance helpers * + *****************************/ + +/** + * Mini helper: get nid from 'node/%d' link_path. + */ +function get_nid_from_link_path($link_path) { + $nid = substr($link_path, 5); + if (is_numeric($nid)) { + return $nid; + } + return FALSE; +} + + +/** + * Return the parent menu item of a node. + * Note: $smallnode just needs ->nid and ->menu, not a full node object. + */ +function protected_get_menu_parent($smallnode) { + // If we come from an insert or update, $smallnode->menu is never empty. + // Otherwise we might have to load the menu. + if (!$smallnode->menu) { + $smallnode->menu = protected_get_menu_info($smallnode->nid); + } + if ($smallnode->menu) { + if ($smallnode->menu['plid'] == 0) { + return FALSE; + } + else { + return protected_get_menu_info($smallnode->menu['plid'], 'mlid'); + } + } + return FALSE; +} + + +/** + * Check if a menu parent item points to a protected node and return its password. + */ +function protected_get_parent_pwd($node) { + if ($menu_item = protected_get_menu_parent($node)) { + if ($nid = get_nid_from_link_path($menu_item['link_path'])) { + return get_protected_passwd(get_protected($nid)); + } + } + return FALSE; +} + + +/** + * Check if we stored the password for a node or any of its parent we inherited + * the password from in the session. The recursion will abort if we find + * a node with a cached password, the top menu, a node without a menu or + * a node that does not inherit. + * Note: $smallnode just needs ->nid and ->menu, not a full node object + */ +function protected_passwd_cached($smallnode) { + // When doing recursion, we might end up with a not protected node. + if ($protected = get_protected($smallnode->nid)) { + if (isset($_SESSION['_protected_node']['passwords'][$smallnode->nid])) { + return TRUE; + }; + } + else { + // Node not protected. + return FALSE; + } + + // Node is protected. Check for inheritance. + if (protected_global_inherit_flag() && protected_inherit($protected)) { + if ($menu_item = protected_get_menu_parent($smallnode)) { + if ($nid = get_nid_from_link_path($menu_item['link_path'])) { + // Avoid the node_load just to pass a node with useless information. + $parent_node = new stdClass(); + $parent_node->menu = $menu_item; + $parent_node->nid = $nid; + + // If we found the password for the parent, store it for us, too, so the + // next check will work without recursion. + if (protected_passwd_cached($parent_node)) { + $_SESSION['_protected_node']['passwords'][$smallnode->nid] = TRUE; + return TRUE; + } + } + } + } + // Recursion end without password found. + return FALSE; +} + +/** + * Fetch menu entries for a nid or mlid. + * + * Return mlid, plid, has_children-flag, link_path of a menu entry. + * That's all information we need from a menu. We use this because + * we cannot call menu_link_load from nodeapi 'load' and also to + * to avoid all menu_link_load calls, as they cause node_load. + * That's too expensive, we can traverse a menu tree just with + * some menu information and the protected_node database. + * + * Return FALSE if node doesn't have a menu or is not a 'node/%d' path, e.g. + * someone linked a system page into a user-built menu. + */ +function protected_get_menu_info($id, $key = 'nid') { + // Follow the algorithm from menu.module. There might be more than one menu entry + // for a node, so try default menu first, then all menus. + $menu_name = variable_get('menu_default_node_menu', 'primary-links'); - return system_settings_form($form); -} \ No newline at end of file + if ($key == 'nid') { + // Default menu first. + $item = db_fetch_array(db_query_range("SELECT mlid,plid,has_children,link_path FROM {menu_links} WHERE link_path = 'node/%d' AND menu_name = '%s' AND module = 'menu' ORDER BY mlid ASC", $id, $menu_name, 0, 1)); + // If not found, try all menus. + if (!$item) { + $item = db_fetch_array(db_query_range("SELECT mlid,plid,has_children,link_path FROM {menu_links} WHERE link_path = 'node/%d' AND module = 'menu' ORDER BY mlid ASC", $id, 0, 1)); + } + } + // Fetching by mlid. + else { + $item = db_fetch_array(db_query_range("SELECT mlid,plid,has_children,link_path FROM {menu_links} WHERE mlid = %d AND menu_name = '%s' AND module = 'menu' ORDER BY mlid ASC", $id, $menu_name, 0, 1)); + if (!$item) { + $item = db_fetch_array(db_query_range("SELECT mlid,plid,has_children,link_path FROM {menu_links} WHERE mlid = %d AND module = 'menu' ORDER BY mlid ASC", $id, 0, 1)); + } + } + return $item; +} + + +/** + * For a given mlid, fetch the protection entry for the node this menu link points to. + */ +function get_protected_by_mlid($mlid) { + if (is_numeric($mlid) && $mlid != 0 && $menu_item = protected_get_menu_info($mlid, 'mlid')) { + if ($nid = get_nid_from_link_path($menu_item['link_path'])) { + return get_protected($nid); + } + } + return FALSE; +} + + +/** + * Return mlids of all children of a menu item. + */ +function protected_get_children($mlid) { + $children = array(); + $result = db_query("SELECT mlid FROM {menu_links} WHERE plid = %d", $mlid); + while ($m = db_result($result)) { + $children[] = $m; + } + return $children; +} + + +/** + * Walk through a menu tree and change the password for all children of a menu item. + * We can do this without loading the nodes. + */ +function protected_propagate_children($menu, $pwd, $pass_inheritance = FALSE){ + if ($menu['has_children']) { + $children = protected_get_children($menu['mlid']); + protected_propagate_children_list($children, $pwd, $pass_inheritance); + } +} + +/** + * Separate function for array of children because we call this once + * when deleting/changing menus. + * If we are told to pass inheritance, we even change non-inheriting + * children to inherit our password. + */ +function protected_propagate_children_list($children, $pwd, $pass_inheritance = FALSE){ + foreach ($children as $mlid) { + $menu_item = protected_get_menu_info($mlid, 'mlid'); + if ($nid = get_nid_from_link_path($menu_item['link_path'])) { + // Determine if the child inherits. + if ($pass_inheritance) { + $inherits = TRUE; + } + else { + if ($protected = get_protected($nid)) { + // We have an entry in the database. + $inherits = protected_inherit($protected); + } + // Otherwise check the global default. + else { + $inherits = protected_default_inherit_flag(); + } + } + + if ($inherits) { + update_protected($nid, $pwd, $inherits); + // Start recursion. + protected_propagate_children($menu_item, $pwd, $pass_inheritance); + } + } + } +} --- protected_node/protected_node.js.orig 2008-08-04 09:21:40.000000000 +0200 +++ protected_node/protected_node.js 2008-07-23 17:11:42.000000000 +0200 @@ -0,0 +1,25 @@ +// $Id: pathauto.js,v 1.4 2008/03/06 02:56:05 greggles Exp $ +if (Drupal.jsEnabled) { + $(document).ready(function() { + if ($("#edit-protected-inherit").size() && $("#edit-protected-inherit").attr("checked")) { + // Initialize. + $("#edit-is-protected").attr("disabled","disabled"); + $("#edit-password-pass1").attr("disabled","disabled"); + $("#edit-password-pass2").attr("disabled","disabled"); + } + $("#edit-protected-inherit").bind("click", function() { + if ($("#edit-protected-inherit").attr("checked")) { + // Inherit checked. Disabled protect box and password fields. + $("#edit-is-protected").attr("disabled","disabled"); + $("#edit-password-pass1").attr("disabled","disabled"); + $("#edit-password-pass2").attr("disabled","disabled"); + } + else { + // Inherit unchecked. Disabled protect box and password fields. + $("#edit-is-protected").removeAttr("disabled"); + $("#edit-password-pass1").removeAttr("disabled"); + $("#edit-password-pass2").removeAttr("disabled"); + } + }); + }); +} --- protected_node/protected_node.install.orig 2008-08-04 16:38:37.000000000 +0200 +++ protected_node/protected_node.install 2008-08-04 16:41:10.000000000 +0200 @@ -33,6 +33,11 @@ 'length' => 40, 'not null' => TRUE, 'default' => ''), + 'inherit' => array( + 'description' => t('Boolean indicating whether the node inherits protection from menu parent item.'), + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0), ), 'indexes' => array('protected_passwd' => array('passwd')), 'primary key' => array('nid'), @@ -55,3 +60,21 @@ function protected_node_uninstall() { drupal_uninstall_schema('protected_node'); } + + +/** + * Add column for inheritance flag. + */ +function protected_node_update_1() { + $ret = array(); + db_add_field($ret, 'protected_nodes', 'inherit', + array( + 'description' => t('Boolean indicating whether the node inherits protection from menu parent item.'), + 'type' => 'int', + 'not null' => TRUE, + 'default' => 0, + ) + ); + + return $ret; +}