protected_node.module in Protected Node 7
Same filename and directory in other branches
Protected Node module.
File
protected_node.moduleView source
<?php
/**
* @file
* Protected Node module.
*/
/**
* Per node password.
*
* The password is required on all nodes unless the node type defines
* a default password.
*
* Defined in: protected_node_use_global_password.
*/
define('PROTECTED_NODE_PER_NODE_PASSWORD', 0);
/**
* Per node or global password.
*
* The password is not required. The system uses the global password
* if the node does not define a password.
*
* Defined in: protected_node_use_global_password.
*/
define('PROTECTED_NODE_PER_NODE_AND_GLOBAL_PASSWORD', 1);
/**
* Global password only.
*
* Use the global password only. Ignore the node specific password
* and don't ask for one when editing the node.
*
* Defined in: protected_node_use_global_password.
*/
define('PROTECTED_NODE_GLOBAL_PASSWORD', 2);
/**
* Never protect these types of nodes.
*
* Defined in: protected_node_protection_<node type name>.
*/
define('PROTECTED_NODE_PROTECTION_NEVER', 0);
/**
* The author can choose whether the node is protected or not.
*
* By default, the node is not protected.
*
* Defined in: protected_node_protection_<node type name>.
*/
define('PROTECTED_NODE_PROTECTION_PROTECTABLE', 1);
/**
* The author can choose whether the node is protected or not.
*
* By default, the node is protected.
*
* Defined in: protected_node_protection_<node type name>.
*/
define('PROTECTED_NODE_PROTECTION_PROTECTED', 2);
/**
* The nodes of this type will always be protected.
*
* Defined in: protected_node_protection_<node type name>.
*/
define('PROTECTED_NODE_PROTECTION_ALWAYS', 3);
/**
* Implements hook_help().
*/
function protected_node_help($path, $arg) {
switch ($path) {
case 'admin/modules#description':
return t('With this module anybody who has edit protected node permission can password protect his or her own node.');
}
}
/**
* Implements hook_permission().
*/
function protected_node_permission() {
$permissions = array(
'access protected node overview page' => array(
'title' => t('Access protected node overview page'),
),
'access protected node password form' => array(
'title' => t('Access protected node password form'),
'description' => t('Access protected node password form page. Without this permission user will be denied access completely.'),
),
'edit any protected node password' => array(
'title' => t('Edit any protected node password'),
'description' => t('Edit the password of any protected node. Grants access to the password fieldset in the node form.'),
),
'view protected content' => array(
'title' => t('View protected content (bypass password)'),
'description' => t('Allow to view any protected node by bypassing the password protection.'),
),
'edit protected content' => array(
'title' => t('Edit protected content (bypass password)'),
'description' => t('Allow to edit any protected node by bypassing the password protection. Do not allow to edit the password. Note: the user will also be able to view the content without entering the password.'),
),
);
foreach (node_type_get_types() as $key => $type) {
$permissions['edit ' . $key . ' password'] = array(
'title' => t('Edit %type_name password', array(
'%type_name' => $key,
)),
'description' => t('Edit password for %type_name nodes', array(
'%type_name' => $key,
)),
);
}
return $permissions;
}
/**
* Implements hook_menu().
*/
function protected_node_menu() {
module_load_include('settings.inc', 'protected_node');
return protected_node_menu_array();
}
/**
* Callback function to determine who can enter a password.
*/
function protected_node_access_callback() {
global $user;
// Super user?
if ($user->uid == 1) {
return TRUE;
}
if (!user_access('access protected node password form')) {
return FALSE;
}
// Is $nid properly defined?
if (empty($_GET['protected_page']) || !is_numeric($_GET['protected_page'])) {
return FALSE;
}
// Valid node?
$node = node_load($_GET['protected_page']);
if (!$node) {
return FALSE;
}
// Editing/deleting? user has edit right?
if (substr($_GET['destination'], 0, 5) == 'node/') {
if (substr($_GET['destination'], -5) == '/edit') {
if (!node_access('update', $node)) {
return FALSE;
}
}
elseif (substr($_GET['destination'], -7) == '/delete') {
if (!node_access('delete', $node)) {
return FALSE;
}
}
}
return TRUE;
}
/**
* Implements hook_init().
*/
function protected_node_init() {
// Let Drush bypass password protection.
if (function_exists('drush_main')) {
return;
}
// Are we about to display a node?
// Can user see all nodes anyway?
if (user_access('edit protected content')) {
return;
}
if (variable_get('protected_node_use_global_password', PROTECTED_NODE_PER_NODE_PASSWORD) == PROTECTED_NODE_GLOBAL_PASSWORD && isset($_SESSION['has_entered_global_password'])) {
return;
}
$nid = FALSE;
$param2 = arg(2);
if (arg(0) == 'node' && is_numeric(arg(1))) {
if ($param2 === NULL) {
$nid = protected_node_is_locked(arg(1), 'view');
if ($nid === -1) {
return;
}
}
elseif ($param2 == 'edit' || $param2 == 'delete') {
$nid = protected_node_is_locked(arg(1), $param2);
}
else {
// Any other sub-path.
$nid = protected_node_is_locked(arg(1), 'view');
}
if ($nid === TRUE || $nid === -1) {
drupal_access_denied();
exit;
}
}
elseif (arg(0) == 'system' && arg(1) == 'files') {
$requested_url = drupal_parse_url(request_uri());
$path = urldecode(str_replace('system/files', '', $requested_url['path']));
if (!empty($path)) {
$nid = protected_node_and_attachment($path);
}
}
if ($nid) {
$query = drupal_get_destination();
if (!empty($_SERVER['HTTP_REFERER'])) {
$query['back'] = urlencode($_SERVER['HTTP_REFERER']);
}
$query['protected_page'] = $nid;
drupal_goto('protected-node', array(
'query' => $query,
));
}
}
/**
* Check whether a node is protected and a password is required.
*
* @param int $nid
* The node identifier.
* @param string $op
* Operation: 'access', 'view', 'edit', or 'delete'.
*
* @return false
* if the node is not protected for the current user.
* Return TRUE if it is protected and cannot be viewed by the current user.
* Return $nid if the user has a chance to unlock this protected node by
* entering the password.
* Return -1 if the user is trying to view the node and has both access to
* view nodes of that type and the 'view protected content' permission.
*/
function protected_node_is_locked($nid, $op = 'access') {
global $user;
// Get the node.
$node = node_load($nid);
// Is the node protected?
if (!isset($node->protected_node_is_protected) || !$node->protected_node_is_protected) {
return FALSE;
}
// Anonymous user?
if (!$user->uid) {
// Do not cache anything for anonymous users as that could make
// the content of the page available to people who never enter
// the password (especially with aggressive caching.).
if (variable_get('cache', 1)) {
// Prevent caching (do NOT use variable_set() since this is temporary
// for this session.).
$GLOBALS['conf']['cache'] = 0;
}
}
else {
// Author looking at his work (if not anonymous)?
if ($node->uid === $user->uid) {
return FALSE;
}
}
// User cannot access any protected node. This check avoids the rather
// useless drupal_goto() and thus does not change the URL on the user.
if (!user_access('access protected node password form')) {
return TRUE;
}
// If the user is only trying to view this node, accept.
if ($op == 'view') {
if (user_access('view protected content') && node_access('view', $node)) {
// User's got view permission without password
// (password for edit/delete rights).
return -1;
}
}
elseif ($op == 'edit') {
if (!node_access('update', $node)) {
// No rights to edit.
return TRUE;
}
elseif (user_access('edit protected content') && node_access('update', $node)) {
// User's got edit permission without password
// (password for edit/delete rights).
return -1;
}
// Rights to edit, but password is still required in this case!
}
elseif ($op == 'delete') {
if (!node_access('delete', $node)) {
// No rights to delete.
return TRUE;
}
// Rights to delete, but password is still required in this case!
}
else {
return TRUE;
}
// User already entered the global password?
if (isset($_SESSION['_protected_node']['passwords']['global'])) {
$when = $_SESSION['_protected_node']['passwords']['global'];
if ($when > variable_get('protected_node_session_timelimit', 0) && $when > $node->protected_node_passwd_changed) {
return FALSE;
}
// The session is out of date, we can as well get rid of it now.
unset($_SESSION['_protected_node']['passwords']['global']);
}
else {
// User already entered the password?
if (isset($_SESSION['_protected_node']['passwords'][$nid])) {
$when = $_SESSION['_protected_node']['passwords'][$nid];
if ($when > variable_get('protected_node_session_timelimit', 0) && $when > $node->protected_node_passwd_changed) {
return FALSE;
}
// The session is out of date, we can as well get rid of it now.
unset($_SESSION['_protected_node']['passwords'][$nid]);
}
}
return $nid;
}
/**
* Helper function.
*
* If gathering an attachment, verify that it is accessible and if
* not ask for the password.
*
* @param string $path
* The path to the attachment file.
*
* @return mixed
* File nid if user has access. FALSE otherwise.
*/
function protected_node_and_attachment($path) {
global $user;
if (user_access('edit protected content')) {
return FALSE;
}
// Check whether the node linked to this file attachment is protected.
$query = db_select('node', 'n');
$query
->join('file_usage', 'fu', 'n.nid = fu.id');
$query
->join('file_managed', 'fm', 'fm.fid = fu.fid');
$query
->join('protected_nodes', 'pn', 'n.nid = pn.nid');
$query
->fields('n', array(
'nid',
'uid',
));
$query
->fields('pn', array(
'protected_node_passwd_changed',
));
$query
->condition('fu.type', 'node');
$query
->condition('fm.uri', '%' . db_like($path), 'LIKE');
$query
->condition('pn.protected_node_is_protected', '1');
$number_of_results = $query
->countQuery()
->execute()
->fetchField();
// If number is 0, node is not protected, or file is in a field collection.
if (0 == $number_of_results) {
if (module_exists('field_collection')) {
// Check if file is attached to protected node via field collection.
$query = db_select('file_usage', 'fu');
$query
->join('file_managed', 'fm', 'fu.fid = fm.fid');
$query
->fields('fu', array(
'id',
));
$query
->condition('fu.type', 'field_collection_item');
$query
->condition('fm.uri', '%' . db_like($path), 'LIKE');
$in_field_collection = $query
->countQuery()
->execute()
->fetchField();
// The file is attached to a field collection item.
if ($in_field_collection != '0') {
$field_collection_ids = $query
->execute()
->fetchCol();
$field_collection_items = entity_load('field_collection_item', $field_collection_ids);
// Get the nids.
$protected_node_nids = array();
foreach ($field_collection_items as $field_collection_item) {
$protected_node_nids[] = $field_collection_item
->hostEntity()->nid;
}
// Query the node table again with the nid the field collection belongs
// to.
$query = db_select('node', 'n');
$query
->join('protected_nodes', 'pn', 'n.nid = pn.nid');
$query
->fields('n', array(
'nid',
'uid',
));
$query
->fields('pn', array(
'protected_node_passwd_changed',
));
$query
->condition('n.nid', $protected_node_nids, 'IN');
$query
->condition('pn.protected_node_is_protected', '1');
$number_of_results = $query
->countQuery()
->execute()
->fetchField();
if (0 == $number_of_results) {
return FALSE;
}
}
else {
return FALSE;
}
}
elseif (module_exists('paragraphs')) {
// Check if file is attached to protected node via paragraphs.
$query = db_select('file_usage', 'fu');
$query
->join('file_managed', 'fm', 'fu.fid = fm.fid');
$query
->fields('fu', array(
'id',
));
$query
->condition('fu.type', 'paragraphs_item');
$query
->condition('fm.uri', '%' . db_like($path), 'LIKE');
$in_paragraphs = $query
->countQuery()
->execute()
->fetchField();
// The file is attached to a paragraphs item.
if ($in_paragraphs != '0') {
$paragraphs_ids = $query
->execute()
->fetchCol();
/** @var \ParagraphsItemEntity[] $paragraphs_items */
$paragraphs_items = entity_load('paragraphs_item', $paragraphs_ids);
// Get the nids.
$protected_node_nids = array();
foreach ($paragraphs_items as $paragraphs_item) {
$nid = _protected_node_get_paragraph_node_host_entity_id($paragraphs_item);
if ($nid) {
$protected_node_nids[] = $nid;
}
}
// Query the node table again with the nid the paragraph belongs
// to.
if (!empty($protected_node_nids)) {
$query = db_select('node', 'n');
$query
->join('protected_nodes', 'pn', 'n.nid = pn.nid');
$query
->fields('n', array(
'nid',
'uid',
));
$query
->fields('pn', array(
'protected_node_passwd_changed',
));
$query
->condition('n.nid', $protected_node_nids, 'IN');
$query
->condition('pn.protected_node_is_protected', '1');
$number_of_results = $query
->countQuery()
->execute()
->fetchField();
if (0 == $number_of_results) {
return FALSE;
}
}
else {
return FALSE;
}
}
else {
return FALSE;
}
}
else {
// If not in node, nor in field_collection or paragraphs, return FALSE
return FALSE;
/* Row doesn't exist, it's not protected */
}
}
$result = $query
->execute();
foreach ($result as $file_info) {
// Row doesn't exist, it's not protected || $user is the author.
if ($file_info === FALSE || $user->uid && $user->uid == $file_info->uid) {
return FALSE;
}
// The user has the bypass password for view.
if (user_access('view protected content', $user)) {
return FALSE;
}
// Got the global password?
if (isset($_SESSION['_protected_node']['passwords']['global'])) {
$when = $_SESSION['_protected_node']['passwords']['global'];
// This page reset time && global reset time.
if ($when > $file_info->protected_node_passwd_changed && $when > variable_get('protected_node_session_timelimit', 0)) {
return FALSE;
}
// The session is out of date, we can as well get rid of it now.
unset($_SESSION['_protected_node']['passwords']['global']);
}
else {
// Got the password?
if (isset($_SESSION['_protected_node']['passwords'][$file_info->nid])) {
$when = $_SESSION['_protected_node']['passwords'][$file_info->nid];
// This page reset time && global reset time.
if ($when > $file_info->protected_node_passwd_changed && $when > variable_get('protected_node_session_timelimit', 0)) {
return FALSE;
}
// The session is out of date, we can as well get rid of it now.
unset($_SESSION['_protected_node']['passwords'][$file_info->nid]);
}
}
// No password, access denied.
return $file_info->nid;
}
}
/**
* Module invoke.
*
* Call module implemented functions with a parameter passed as reference
* instead of copy.
*
* For calls that require multiple parameters, use an array or object.
*
* @param[in] $hook
* The name of the hook to call.
*
* @param[in,out] $param
* The one parameter to pass to the hook functions.
*/
function protected_node_invoke($hook, &$param) {
foreach (module_implements($hook) as $module) {
call_user_func_array($module . '_' . $hook, array(
&$param,
));
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add the protected node form fieldset if the user editing has permission to
* edit this node type password.
*/
function protected_node_form_node_type_form_alter(&$form, &$form_state, $form_id) {
if (user_access('edit any protected node password') || user_access('edit ' . $form['type']['#value'] . ' password')) {
form_load_include($form_state, 'settings.inc', 'protected_node');
protected_node_node_type_form_alter($form);
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Add the protected node form fieldset if the user editing has permission to
* edit this node type password.
*/
function protected_node_form_node_form_alter(&$form, &$form_state, $form_id) {
if (user_access('edit any protected node password') || user_access('edit ' . $form['type']['#value'] . ' password')) {
form_load_include($form_state, 'settings.inc', 'protected_node');
protected_node_node_form_alter($form);
}
}
/**
* Implements hook_node_load().
*/
function protected_node_node_load($nodes, $types) {
return protected_node_load($nodes);
}
/**
* Implements hook_node_validate().
*/
function protected_node_node_validate($node, $form, &$form_state) {
// The node type is never protected if $node->protected_node_is_protected is
// not set.
if (!isset($node->protected_node_is_protected)) {
return;
}
global $_protected_node_emails;
global $_protected_node_random_passwd;
$_protected_node_emails = '';
$_protected_node_random_passwd = '';
if ($node->protected_node_is_protected && (user_access('edit any protected node password') || user_access('edit ' . $node->type . ' password'))) {
$missing_password = FALSE;
if (empty($node->protected_node_passwd)) {
// Global content type password exists ?
$global_content_type_password = variable_get('protected_node_node_type_password_' . $node->type, '');
if ($global_content_type_password != '') {
// it's ok.
}
else {
$result = db_select('protected_nodes')
->fields('protected_nodes', array(
'protected_node_passwd',
))
->condition('nid', $node->nid)
->execute()
->fetchField();
// Getting " " (40 spaces) when empty.
$result = trim($result);
if (empty($result)) {
$missing_password = TRUE;
}
}
}
if (!empty($node->protected_node_emails)) {
if ($node->status) {
// Verify each email address.
$emails = explode(',', str_replace(array(
"\r",
"\n",
), ',', $node->protected_node_emails));
foreach ($emails as $k => $m) {
$m = trim($m);
if ($m) {
if (!valid_email_address($m)) {
form_error($form['protected_node']['protected_node_emails'], t('Invalid email address: @m. Please correct this mistake and try again.', array(
'@m' => $m,
)));
// Unset just in case; should be useless though.
unset($emails[$k]);
}
else {
$emails[$k] = $m;
}
}
else {
// Ignore empty entries.
unset($emails[$k]);
}
}
$_protected_node_emails = implode(', ', $emails);
if ($_protected_node_emails && $missing_password && variable_get('protected_node_random_password', FALSE)) {
// Automatically generate a password for the email. Note that means
// the author won't know the password!
$_protected_node_random_passwd = user_password();
// Not missing anymore.
$missing_password = FALSE;
drupal_set_message(t('A random password was generated in order to send the email about this page. Remember that changing the password will prevent users you just emailed from accessing this page.'), 'warning');
}
}
else {
// The node is not published, forget about emails!
form_error($form['protected_node']['protected_node_emails'], t('The node is not published. Therefore no email will be sent.'));
}
}
if ($missing_password) {
global $user;
if ($user->uid == 0) {
// If anonymous user, then global password is not an option otherwise
// all the nodes could be edited by all the anonymous users!
$global_password = PROTECTED_NODE_PER_NODE_PASSWORD;
}
else {
$global_password = variable_get('protected_node_use_global_password', PROTECTED_NODE_PER_NODE_PASSWORD);
}
switch ($global_password) {
case PROTECTED_NODE_PER_NODE_PASSWORD:
form_error($form['protected_node']['protected_node_passwd'], t('To protect this page, please enter a password.'));
break;
}
}
}
elseif (isset($node->protected_node_emails) && trim($node->protected_node_emails)) {
form_error($form['protected_node']['protected_node_emails'], t('No email can be sent by the protected node module when the node is not protected or you do not have permission to set a password.'));
}
}
/**
* Implements hook_node_insert().
*/
function protected_node_node_insert($node) {
_protected_node_node_create_or_update($node);
}
/**
* Implements hook_node_update().
*/
function protected_node_node_update($node) {
_protected_node_node_create_or_update($node);
}
/**
* Helper function.
*
* Do protected node actions when creating or updating a node.
*
* @param object $node
* A node object.
*/
function _protected_node_node_create_or_update($node) {
// The node type is never protected if $node->protected_node_is_protected
// is not set.
if (!isset($node->protected_node_is_protected)) {
return;
}
// Ugly but we want to keep some variables between the validation and
// insert/update.
global $_protected_node_emails;
global $_protected_node_random_passwd;
if (!empty($_protected_node_random_passwd)) {
$node->protected_node_passwd = $_protected_node_random_passwd;
}
if (!empty($_protected_node_emails)) {
$node->protected_node_emails = $_protected_node_emails;
}
_protected_node_save($node);
// Send notifications if there is at least one email.
if ($node->protected_node_is_protected && !empty($node->protected_node_emails) && $node->status == 1 && isset($node->protected_node_clear_passwd)) {
module_load_include('mail.inc', 'protected_node');
protected_node_send_mail($node);
}
}
/**
* Implements hook_node_view().
*/
function protected_node_node_view($node, $view_mode, $langcode) {
global $user;
if (!empty($node->protected_node_is_protected)) {
// Accessed for search indexing? (usually by cron.php).
if ($view_mode == 'search_index') {
// "user" could see the node, but at this time, not its contents
// (the current user is Anonymous, so that statement is not exactly true,
// but at the time of the search index building we cannot know who will
// be searching so we let go without the access denied error).
protected_node_invoke('protected_node_hide', $node);
}
elseif (!user_access('view protected content') && _protected_node_check_view_mode($view_mode)) {
if (!$user->uid && variable_get('cache', 1)) {
// Prevent caching (do NOT use variable_set() since this is temporary
// for this session).
$GLOBALS['conf']['cache'] = 0;
}
$global_reset_time = variable_get('protected_node_session_timelimit', 0);
$this_page_reset_time = $node->protected_node_passwd_changed;
if ($node->uid !== $user->uid) {
// Is there a global password?
if (isset($_SESSION['_protected_node']['passwords']['global'])) {
// Is password out of date?
$when = $_SESSION['_protected_node']['passwords']['global'];
if ($when <= $global_reset_time || $when <= $this_page_reset_time) {
unset($_SESSION['_protected_node']['passwords']['global']);
}
}
// Is there a password?
if (isset($_SESSION['_protected_node']['passwords'][$node->nid])) {
// Is password out of date?
$when = $_SESSION['_protected_node']['passwords'][$node->nid];
if ($when <= $global_reset_time || $when <= $this_page_reset_time) {
unset($_SESSION['_protected_node']['passwords'][$node->nid]);
}
}
if (!isset($_SESSION['_protected_node']['passwords'][$node->nid]) && !isset($_SESSION['_protected_node']['passwords']['global'])) {
if (!user_access('access protected node password form')) {
// User will never be given access (no drupal_goto() call
// necessary).
drupal_access_denied();
exit;
}
// User could see the node, but at this time, not its contents.
protected_node_invoke('protected_node_hide', $node);
}
}
}
}
}
/**
* Implements hook_node_delete().
*/
function protected_node_node_delete($node) {
db_delete('protected_nodes')
->condition('nid', $node->nid)
->execute();
}
/**
* Implements hook_protected_node_hide().
*
* We implement this callback since it makes sense (I think) although
* it makes the module a bit slower.
*
* This function hides the body, and if requested on that node we hide the title
* as well.
*
* @param[in,out] $node
* The affected node.
*/
function protected_node_protected_node_hide(&$node) {
// Core module fields.
if (!$node->protected_node_show_title) {
$node->title = t('Password protected page');
}
$node->body = '';
// Remove $node->content children to avoid the user see content he/she should
// not see.
$content_children = element_children($node->content);
foreach ($content_children as $content_key) {
unset($node->content[$content_key]);
}
}
/**
* Implements hook_file_download().
*/
function protected_node_file_download($uri) {
global $user;
$path = file_uri_target($uri);
// Private file access for image style derivatives.
if (strpos($path, 'styles/') === 0) {
// Check that the file exists and is an image.
if (image_get_info($uri)) {
$original_uri = _protected_node_get_original_uri($path, $uri);
// Check the permissions of the original to grant access to this image.
$headers = module_invoke_all('file_download', $original_uri);
// Confirm there's at least one module granting access and none denying
// access.
if (!empty($headers) && !in_array(-1, $headers)) {
return array();
}
}
return array();
}
// Private file access for the original files.
$files = file_load_multiple(array(), array(
'uri' => $uri,
));
if (count($files)) {
$file = reset($files);
if ($file->status) {
// Is it a file submitted with a webform?
if (strpos($file->uri, '://webform/') !== FALSE) {
// Pass through Webform submissions to get the nid given the fid.
$query = db_select('file_usage', 'fu');
$query
->join('webform_submissions', 'ws', 'ws.sid = fu.id');
$query
->join('node', 'n', 'n.nid = ws.nid');
$query
->join('protected_nodes', 'pn', 'n.nid = pn.nid');
$query
->fields('n', array(
'nid',
'uid',
));
$query
->fields('pn', array(
'protected_node_passwd_changed',
));
$query
->condition('fu.module', 'webform');
$query
->condition('fu.type', 'submission');
$query
->condition('fu.fid', $file->fid);
$query
->condition('pn.protected_node_is_protected', '1');
}
else {
$query = db_select('node', 'n');
$query
->join('file_usage', 'fu', 'n.nid = fu.id');
$query
->join('protected_nodes', 'pn', 'n.nid = pn.nid');
$query
->fields('n', array(
'nid',
'uid',
));
$query
->fields('pn', array(
'protected_node_passwd_changed',
));
$query
->condition('fu.fid', $file->fid);
$query
->condition('fu.type', 'node');
$query
->condition('pn.protected_node_is_protected', '1');
}
$number_of_results = $query
->countQuery()
->execute()
->fetchField();
if (0 == $number_of_results) {
return array();
/* Row doesn't exist, it's not protected */
}
$result = $query
->execute();
foreach ($result as $file_info) {
// If the file belongs to the current user let them see it.
if ($file_info === FALSE || $user->uid && $user->uid == $file_info->uid) {
return array();
}
// The user has the bypass password for view.
if (user_access('view protected content', $user)) {
return array();
}
// Got the global password?
if (isset($_SESSION['_protected_node']['passwords']['global'])) {
$when = $_SESSION['_protected_node']['passwords']['global'];
// This page reset time && global reset time.
if ($when > $file_info->protected_node_passwd_changed && $when > variable_get('protected_node_session_timelimit', 0)) {
return array();
}
}
elseif (isset($_SESSION['_protected_node']['passwords'][$file_info->nid])) {
$when = $_SESSION['_protected_node']['passwords'][$file_info->nid];
// This page reset time && global reset time.
if ($when > $file_info->protected_node_passwd_changed && $when > variable_get('protected_node_session_timelimit', 0)) {
return array();
}
}
}
// No password, access denied.
return -1;
}
elseif (strpos($file->uri, '://webform/') !== FALSE) {
return array();
}
else {
return array();
}
}
// Not a file managed by a protected node.
return array();
}
/**
* Helper function used to return the original uri from a path and an uri.
*
* @see protected_node_file_download()
*/
function _protected_node_get_original_uri($path, $uri) {
$args = explode('/', $path);
// Discard the first part of the path (styles).
array_shift($args);
// Discard the second part of the path (style_name).
array_shift($args);
// Discard the third part of the path (scheme).
array_shift($args);
// Then the remaining parts are the path to the image.
$original_uri = file_uri_scheme($uri) . '://' . implode('/', $args);
return $original_uri;
}
/**
* Sets the given node to protected with the provided password.
*
* The password cannot be empty.
*
* If the node already password protected this method changes the password
* to the one you provided as $password parameter.
*
* @param[in,out] object $node
* The node to be saved.
*/
function _protected_node_save(&$node) {
// The node type is never protected if $node->protected_node_is_protected
// is not set.
if (!isset($node->protected_node_is_protected)) {
return;
}
// We first test whether a protected_nodes entry exist so we can use UPDATE
// or INSERT accordingly (UPDATE does not always properly report working
// with MySQL).
// We also retrieve nid because protected_node_passwd may exist and be empty.
$result = db_select('protected_nodes')
->fields('protected_nodes', array(
'nid',
'protected_node_passwd',
'protected_node_emails',
))
->condition('nid', $node->nid)
->execute()
->fetchAssoc();
if (!empty($result)) {
// Note: the following test prevents the user from using "0" as a password.
if (isset($node->protected_node_passwd)) {
$changed = $node->protected_node_passwd != $result['protected_node_passwd'];
if ($changed) {
if (empty($node->protected_node_passwd)) {
// Keep result if it's empty ...
$node->protected_node_passwd = $result['protected_node_passwd'];
$changed = FALSE;
}
else {
$node->protected_node_clear_passwd = $node->protected_node_passwd;
$node->protected_node_passwd = hash('sha256', $node->protected_node_passwd);
}
}
}
else {
$changed = FALSE;
$node->protected_node_passwd = $result['protected_node_passwd'];
}
// Check if the email addresses is empty.
if (empty($node->protected_node_emails)) {
if (!empty($result['protected_node_emails'])) {
// Keep the addresses.
$saved_emails = $result['protected_node_emails'];
}
else {
$saved_emails = '';
}
}
else {
$saved_emails = $node->protected_node_emails;
}
$args = array(
'protected_node_is_protected' => (int) $node->protected_node_is_protected,
'protected_node_passwd' => $node->protected_node_passwd,
'protected_node_show_title' => (int) $node->protected_node_show_title,
'protected_node_emails' => $saved_emails,
'protected_node_hint' => isset($node->protected_node_hint) ? $node->protected_node_hint : '',
);
if ($changed) {
$args['protected_node_passwd_changed'] = REQUEST_TIME;
}
db_update('protected_nodes')
->fields($args)
->condition('nid', $node->nid)
->execute();
}
else {
if (!isset($node->protected_node_passwd)) {
// This happens when the global password is to be used.
$node->protected_node_passwd = '';
}
elseif ($node->protected_node_passwd) {
$node->protected_node_clear_passwd = $node->protected_node_passwd;
$node->protected_node_passwd = hash('sha256', $node->protected_node_passwd);
}
// We don't need to set the protected_node_passwd_changed since no
// one has ever entered a password for this node.
db_insert('protected_nodes')
->fields(array(
'protected_node_is_protected' => (int) $node->protected_node_is_protected,
'protected_node_passwd' => $node->protected_node_passwd,
'protected_node_show_title' => (int) $node->protected_node_show_title,
'nid' => $node->nid,
'protected_node_emails' => isset($node->protected_node_emails) ? $node->protected_node_emails : '',
'protected_node_hint' => isset($node->protected_node_hint) ? $node->protected_node_hint : '',
))
->execute();
}
}
/**
* Load the node extension fields.
*
* @param[in] object $node
* The node to complement with the protected node parameters.
*
* @return array
* An array with the node extended fields or FALSE.
*/
function protected_node_load($nodes) {
foreach ($nodes as &$node) {
// Valid input parameters?
if (!is_object($node) || !is_numeric($node->nid)) {
return FALSE;
}
// Default fields for protected nodes.
static $default_fields = array(
'protected_node_is_protected' => 0,
'protected_node_passwd' => '',
'protected_node_passwd_changed' => 0,
'protected_node_show_title' => 0,
'protected_node_emails' => '',
'protected_node_hint' => '',
);
// Can the node be protected at all?
$protection = variable_get('protected_node_protection_' . $node->type, PROTECTED_NODE_PROTECTION_PROTECTABLE);
if ($protection == PROTECTED_NODE_PROTECTION_NEVER) {
// By default the node is not protected, return that.
return $default_fields;
}
$result = db_select('protected_nodes')
->fields('protected_nodes', array(
'protected_node_is_protected',
'protected_node_passwd',
'protected_node_passwd_changed',
'protected_node_show_title',
'protected_node_emails',
'protected_node_hint',
))
->condition('nid', $node->nid)
->execute()
->fetchAssoc();
if (!is_array($result)) {
// The SELECT failed, use the defaults.
$result = $default_fields;
}
else {
// Define any missing field.
$result += $default_fields;
}
// The password is a CHAR(40) and when empty it's all spaces
// (this is possible when the global password is used).
$result['protected_node_passwd'] = trim($result['protected_node_passwd']);
// If the user changed the mode to "always protected" then we force that
// here it means the node may not be accessible to people without
// administration privileges since it may not have a default password.
if ($protection == PROTECTED_NODE_PROTECTION_ALWAYS) {
$result['protected_node_is_protected'] = TRUE;
}
foreach ($result as $property => &$value) {
$node->{$property} = $value;
}
}
}
/**
* Implements hook_token_info().
*
* This function defines some extras for the protected node (i.e. whether a
* node is protected, title flag, last time the password was changed, etc.)
*/
function protected_node_token_info() {
$info['tokens']['node'] = array(
'is-protected' => array(
'name' => t('Node protected status'),
'description' => t("Whether the node is protected (yes/no)."),
),
'password' => array(
'name' => t('Node protected password'),
'description' => t("The password in clear (only if available, empty otherwise)."),
),
'protected-title' => array(
'name' => t('Node protected show title'),
'description' => t("Whether the title node of the node is protected (yes/no)."),
),
'password-hint' => array(
'name' => t('Node protected password hint'),
'description' => t("The password hint as entered in this node."),
),
);
return $info;
}
/**
* Implements hook_node_type_delete().
*
* This function deletes the variables corresponding to the fields added
* to the node type form.
*
* @param[in] $op
* The operation performed on the node type.
* @param[in] $type
* The type object concerned.
*/
function protected_node_node_type_delete($info) {
variable_del('protected_node_fieldset_' . $info->type);
variable_del('protected_node_protection_' . $info->type);
variable_del('protected_node_node_type_password_' . $info->type);
// Should already be deleted by the submit().
variable_del('protected_node_node_type_password_field_' . $info->type);
}
/**
* Implements hook_tokens().
*/
function protected_node_tokens($type, $tokens, array $data = array(), array $options = array()) {
$replacements = array();
if ($type == 'node' && !empty($data['node'])) {
$node = $data['node'];
if (!empty($node->protected_node_is_protected)) {
foreach ($tokens as $name => $original) {
switch ($name) {
case 'is-protected':
$replacements[$original] = t('yes');
break;
case 'password':
$replacements[$original] = empty($node->protected_node_clear_passwd) ? '' : $node->protected_node_clear_passwd;
break;
case 'protected-title':
$replacements[$original] = empty($node->protected_node_show_title) ? t('yes') : t('no');
break;
case 'password-hint':
$replacements[$original] = $node->protected_node_hint;
break;
}
}
}
}
return $replacements;
}
/**
* After_build function to disable autocomplete for the password fields.
*
* Without this FF >= 3 will attempt to autocomplete the fields with the user's
* login info.
*/
function protected_node_autocomplete_off($form_element, &$form_state) {
$form_element['pass1']['#attributes']['autocomplete'] = 'new-password';
$form_element['pass2']['#attributes']['autocomplete'] = 'new-password';
return $form_element;
}
/**
* Implements hook_boost_is_cacheable().
*
* Prevent boost from caching protected nodes.
*
* @todo
* We also need to make sure the cache gets cleared whenever
* the protection is turned on.
*/
function protected_node_boost_is_cacheable($path) {
if (arg(0) == 'node' && is_numeric(arg(1))) {
// If protected, do not cache (i.e. not caching == return FALSE).
return array(
'is_cacheable' => !protected_node_isset_protected(arg(1)),
);
}
return array(
'is_cacheable' => TRUE,
);
}
/**
* This method marks the specified node as protected.
*
* The method accepts a password. It is legal to not pass a password in
* which case the previously defined password is used or the global password.
* If no password is available, then the node gets locked until edited by
* the author or the administrator (UID=1) and a password is added.
*
* If the \p $passwd parameter is set, then the change is marked in the
* database. In other words, all users who had previously enter a password
* will be kicked out.
*
* @param[in] $param
* The node identifier or whatever valid $param passed to node_load.
* @param[in] $passwd
* The node password.
*
* @return bool
* TRUE if the node is protection on return.
*/
function protected_node_set_protected($param, $passwd = NULL) {
// Get the existing node.
$node = node_load($param);
if ($node == FALSE) {
// Not even a valid node identifier?!
return FALSE;
}
if (empty($node->protected_node_is_protected)) {
// Node exists in our table?
$select = db_select('protected_nodes')
->fields('protected_nodes', array(
'nid',
))
->condition('nid', $node->nid)
->execute()
->fetchField();
if ($select) {
if (empty($passwd)) {
// In this case, an empty password is fine.
$result = db_update('protected_nodes')
->fields(array(
'protected_node_is_protected' => 1,
))
->condition('nid', $node->nid)
->execute() !== FALSE;
}
else {
// We have to also update the password in this case.
$result = db_update('protected_nodes')
->fields(array(
'protected_node_is_protected' => 1,
'protected_node_passwd' => hash('sha256', $passwd),
'protected_node_passwd_changed' => REQUEST_TIME,
))
->condition('nid', $node->nid)
->execute() !== FALSE;
}
}
else {
// No entry in the database yet, add it now.
if (empty($passwd)) {
$passwd = '';
}
else {
$passwd = hash('sha256', $passwd);
}
$result = db_insert('protected_nodes')
->fields(array(
'nid' => $node->nid,
'protected_node_is_protected' => 1,
'protected_node_passwd' => $passwd,
'protected_node_show_title' => variable_get('protected_node_show_node_titles', FALSE),
))
->execute() !== FALSE;
}
}
else {
// The node is already protected, change the password if necessary.
if (empty($passwd)) {
// It is protected; we're done (the password is not to be changed).
return TRUE;
}
$result = db_update('protected_nodes')
->fields(array(
'protected_node_passwd' => hash('sha256', $passwd),
'protected_node_passwd_changed' => REQUEST_TIME,
))
->condition('nid', $node->nid)
->execute() !== FALSE;
}
return $result;
}
/**
* This method marks the specified node as unprotected.
*
* This function ensures that the specified node is not protected anymore.
* It does not delete the row from the database which means calling the
* protected_node_set_protected() function with the same $nid parameter
* will restore the previous state (assuming the node was protected before.)
*
* When the node was previously protected and this call succeeds, the method
* returns TRUE.
*
* If an invalid $nid is passed FALSE is returned.
*
* @param[in] int $nid
* The node identifier.
*
* @return bool
* TRUE if the node was protected before the call, FALSE otherwise.
*/
function protected_node_unset_protected($nid) {
$result = db_select('protected_nodes')
->fields('protected_nodes', array(
'protected_node_is_protected',
))
->condition('nid', $nid)
->execute()
->fetchField() == 1;
db_update('protected_nodes')
->fields(array(
'protected_node_is_protected' => 0,
))
->condition('nid', $nid)
->execute();
return $result;
}
/**
* This method determines the protected flag status for the given node id.
*
* Note that doesn't mean the node is protected for the current user
* (i.e. the current user may have entered the password successfully.)
*
* @param[in] int $nid
* The node id to check.
*
* @return bool
* TRUE if the node identified by the nid you provided is protected, FALSE
* otherwise.
*/
function protected_node_isset_protected($nid) {
if (!is_numeric($nid)) {
return FALSE;
}
$result = db_select('protected_nodes')
->fields('protected_nodes', array(
'protected_node_is_protected',
))
->condition('nid', $nid)
->execute()
->fetchField() == 1;
return $result;
}
/**
* Revoke access to the current used from the specified protected node.
*
* The effect is immediate.
*
* Note that the date is not checked so it is possible that the node was
* already locked and this function still returns TRUE (i.e. the lock
* release was out of date and thus the node was anyway not accessible.)
*
* @param[in] $nid
* The node to lock.
*
* @return bool
* TRUE if the node gets unlocked.
*/
function protected_node_lock($nid) {
if (is_numeric($nid) && isset($_SESSION['_protected_node']['passwords']['global'])) {
unset($_SESSION['_protected_node']['passwords']['global']);
return TRUE;
}
if (is_numeric($nid) && isset($_SESSION['_protected_node']['passwords'][$nid])) {
unset($_SESSION['_protected_node']['passwords'][$nid]);
return TRUE;
}
return FALSE;
}
/**
* Give access to the current user to the specified protected node.
*
* The duration of the lock is as expected starting now.
*
* @param[in] $nid
* The node to unlock.
*
* @return bool
* TRUE if the node gets unlocked.
*/
function protected_node_unlock($nid) {
if (is_numeric($nid)) {
// Make sure the node exists.
$node = node_load($nid);
if ($node->protected_node_is_protected) {
if (isset($_SESSION['has_entered_global_password'])) {
$_SESSION['_protected_node']['passwords']['global'] = REQUEST_TIME;
}
else {
$_SESSION['_protected_node']['passwords'][$nid] = REQUEST_TIME;
}
return TRUE;
}
}
return FALSE;
}
/**
* Helper function.
*
* Evaluate whether the current view mode is one to check the password for.
*
* @param string $view_mode
* Eg: full, teaser, or custom (eg: ds view modes).
*
* @return bool
* TRUE if we are checking passwords, FALSE if not.
*/
function _protected_node_check_view_mode($view_mode) {
$permitted_view_modes = variable_get('protected_node_checked_view_modes', _protected_node_get_default_checked_view_modes());
return in_array($view_mode, $permitted_view_modes);
}
/**
* Helper function.
*
* Used to get the default checked view modes.
*/
function _protected_node_get_default_checked_view_modes() {
$default_view_modes =& drupal_static(__FUNCTION__);
if (!isset($default_view_modes)) {
$node_info = entity_get_info('node');
$default_view_modes = array();
foreach ($node_info['view modes'] as $id => $item) {
$default_view_modes[] = $id;
}
}
return $default_view_modes;
}
/**
* Helper function.
*
* Used to get the options for the protected_node_failed_password_ip_limit
* variable.
*/
function _protected_node_get_failed_password_ip_limit_options() {
return drupal_map_assoc(array(
1,
2,
3,
4,
5,
6,
7,
8,
9,
10,
20,
30,
40,
50,
75,
100,
125,
150,
200,
250,
500,
));
}
/**
* Helper function.
*
* Used to get the options for the protected_node_failed_password_ip_window
* variable.
*/
function _protected_node_get_failed_password_ip_window_options() {
return array(
0 => t('None (flood control disabled)'),
) + drupal_map_assoc(array(
60,
180,
300,
600,
900,
1800,
2700,
3600,
10800,
21600,
32400,
43200,
86400,
), 'format_interval');
}
/**
* Helper function to get recursively the host entity nid of the paragraph.
*
* @param \ParagraphsItemEntity $paragraphs_item
* A paragraph item.
*
* @return int|false
* Return FALSE if the paragraph or the parent paragraphs are not attached to
* a node
*/
function _protected_node_get_paragraph_node_host_entity_id(ParagraphsItemEntity $paragraphs_item) {
// Need to load the host entity otherwise host entity date are null.
paragraphs_item_get_host_entity($paragraphs_item);
$host_entity_type = $paragraphs_item
->hostEntityType();
if ($host_entity_type == 'node') {
return $paragraphs_item
->hostEntity()->nid;
}
elseif ($host_entity_type == 'paragraphs_item') {
$host_id = array(
$paragraphs_item
->hostEntityId(),
);
$host_paragraphs_items = entity_load('paragraphs_item', $host_id);
return _protected_node_get_paragraph_node_host_entity_id(array_shift($host_paragraphs_items));
}
return FALSE;
}
Functions
Constants
Name | Description |
---|---|
PROTECTED_NODE_GLOBAL_PASSWORD | Global password only. |
PROTECTED_NODE_PER_NODE_AND_GLOBAL_PASSWORD | Per node or global password. |
PROTECTED_NODE_PER_NODE_PASSWORD | Per node password. |
PROTECTED_NODE_PROTECTION_ALWAYS | The nodes of this type will always be protected. |
PROTECTED_NODE_PROTECTION_NEVER | Never protect these types of nodes. |
PROTECTED_NODE_PROTECTION_PROTECTABLE | The author can choose whether the node is protected or not. |
PROTECTED_NODE_PROTECTION_PROTECTED | The author can choose whether the node is protected or not. |