First commit

This commit is contained in:
Theodotos Andreou 2018-01-14 13:10:16 +00:00
commit c6e2478c40
13918 changed files with 2303184 additions and 0 deletions

View file

@ -0,0 +1,450 @@
<?php
/**
* @file
* Content type editing user interface.
*/
/**
* Displays the content type admin overview page.
*/
function node_overview_types() {
$types = node_type_get_types();
$names = node_type_get_names();
$field_ui = module_exists('field_ui') && user_access('administer fields');
$header = array(t('Name'), array('data' => t('Operations'), 'colspan' => $field_ui ? '4' : '2'));
$rows = array();
foreach ($names as $key => $name) {
$type = $types[$key];
if (node_hook($type->type, 'form')) {
$type_url_str = str_replace('_', '-', $type->type);
$row = array(theme('node_admin_overview', array('name' => $name, 'type' => $type)));
// Set the edit column.
$row[] = array('data' => l(t('edit'), 'admin/structure/types/manage/' . $type_url_str));
if ($field_ui) {
// Manage fields.
$row[] = array('data' => l(t('manage fields'), 'admin/structure/types/manage/' . $type_url_str . '/fields'));
// Display fields.
$row[] = array('data' => l(t('manage display'), 'admin/structure/types/manage/' . $type_url_str . '/display'));
}
// Set the delete column.
if ($type->custom) {
$row[] = array('data' => l(t('delete'), 'admin/structure/types/manage/' . $type_url_str . '/delete'));
}
else {
$row[] = array('data' => '');
}
$rows[] = $row;
}
}
$build['node_table'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $rows,
'#empty' => t('No content types available. <a href="@link">Add content type</a>.', array('@link' => url('admin/structure/types/add'))),
);
return $build;
}
/**
* Returns HTML for a node type description for the content type admin overview page.
*
* @param $variables
* An associative array containing:
* - name: The human-readable name of the content type.
* - type: An object containing the 'type' (machine name) and 'description' of
* the content type.
*
* @ingroup themeable
*/
function theme_node_admin_overview($variables) {
$name = $variables['name'];
$type = $variables['type'];
$output = check_plain($name);
$output .= ' <small>' . t('(Machine name: @type)', array('@type' => $type->type)) . '</small>';
$output .= '<div class="description">' . filter_xss_admin($type->description) . '</div>';
return $output;
}
/**
* Form constructor for the node type editing form.
*
* @param $type
* (optional) An object representing the node type, when editing an existing
* node type.
*
* @see node_type_form_validate()
* @see node_type_form_submit()
* @ingroup forms
*/
function node_type_form($form, &$form_state, $type = NULL) {
if (!isset($type->type)) {
// This is a new type. Node module managed types are custom and unlocked.
$type = node_type_set_defaults(array('custom' => 1, 'locked' => 0));
}
// Make the type object available to implementations of hook_form_alter.
$form['#node_type'] = $type;
$form['name'] = array(
'#title' => t('Name'),
'#type' => 'textfield',
'#default_value' => $type->name,
'#description' => t('The human-readable name of this content type. This text will be displayed as part of the list on the <em>Add new content</em> page. It is recommended that this name begin with a capital letter and contain only letters, numbers, and spaces. This name must be unique.'),
'#required' => TRUE,
'#size' => 30,
);
$form['type'] = array(
'#type' => 'machine_name',
'#default_value' => $type->type,
'#maxlength' => 32,
'#disabled' => $type->locked,
'#machine_name' => array(
'exists' => 'node_type_load',
),
'#description' => t('A unique machine-readable name for this content type. It must only contain lowercase letters, numbers, and underscores. This name will be used for constructing the URL of the %node-add page, in which underscores will be converted into hyphens.', array(
'%node-add' => t('Add new content'),
)),
);
$form['description'] = array(
'#title' => t('Description'),
'#type' => 'textarea',
'#default_value' => $type->description,
'#description' => t('Describe this content type. The text will be displayed on the <em>Add new content</em> page.'),
);
$form['additional_settings'] = array(
'#type' => 'vertical_tabs',
'#attached' => array(
'js' => array(drupal_get_path('module', 'node') . '/content_types.js'),
),
);
$form['submission'] = array(
'#type' => 'fieldset',
'#title' => t('Submission form settings'),
'#collapsible' => TRUE,
'#group' => 'additional_settings',
);
$form['submission']['title_label'] = array(
'#title' => t('Title field label'),
'#type' => 'textfield',
'#default_value' => $type->title_label,
'#required' => TRUE,
);
if (!$type->has_title) {
// Avoid overwriting a content type that intentionally does not have a
// title field.
$form['submission']['title_label']['#attributes'] = array('disabled' => 'disabled');
$form['submission']['title_label']['#description'] = t('This content type does not have a title field.');
$form['submission']['title_label']['#required'] = FALSE;
}
$form['submission']['node_preview'] = array(
'#type' => 'radios',
'#title' => t('Preview before submitting'),
'#default_value' => variable_get('node_preview_' . $type->type, DRUPAL_OPTIONAL),
'#options' => array(
DRUPAL_DISABLED => t('Disabled'),
DRUPAL_OPTIONAL => t('Optional'),
DRUPAL_REQUIRED => t('Required'),
),
);
$form['submission']['help'] = array(
'#type' => 'textarea',
'#title' => t('Explanation or submission guidelines'),
'#default_value' => $type->help,
'#description' => t('This text will be displayed at the top of the page when creating or editing content of this type.'),
);
$form['workflow'] = array(
'#type' => 'fieldset',
'#title' => t('Publishing options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
);
$form['workflow']['node_options'] = array('#type' => 'checkboxes',
'#title' => t('Default options'),
'#default_value' => variable_get('node_options_' . $type->type, array('status', 'promote')),
'#options' => array(
'status' => t('Published'),
'promote' => t('Promoted to front page'),
'sticky' => t('Sticky at top of lists'),
'revision' => t('Create new revision'),
),
'#description' => t('Users with the <em>Administer content</em> permission will be able to override these options.'),
);
$form['display'] = array(
'#type' => 'fieldset',
'#title' => t('Display settings'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
);
$form['display']['node_submitted'] = array(
'#type' => 'checkbox',
'#title' => t('Display author and date information.'),
'#default_value' => variable_get('node_submitted_' . $type->type, TRUE),
'#description' => t('Author username and publish date will be displayed.'),
);
$form['old_type'] = array(
'#type' => 'value',
'#value' => $type->type,
);
$form['orig_type'] = array(
'#type' => 'value',
'#value' => isset($type->orig_type) ? $type->orig_type : '',
);
$form['base'] = array(
'#type' => 'value',
'#value' => $type->base,
);
$form['custom'] = array(
'#type' => 'value',
'#value' => $type->custom,
);
$form['modified'] = array(
'#type' => 'value',
'#value' => $type->modified,
);
$form['locked'] = array(
'#type' => 'value',
'#value' => $type->locked,
);
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Save content type'),
'#weight' => 40,
);
if ($type->custom) {
if (!empty($type->type)) {
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete content type'),
'#weight' => 45,
);
}
}
return $form;
}
/**
* Helper function for teaser length choices.
*/
function _node_characters($length) {
return ($length == 0) ? t('Unlimited') : format_plural($length, '1 character', '@count characters');
}
/**
* Form validation handler for node_type_form().
*
* @see node_type_form_submit()
*/
function node_type_form_validate($form, &$form_state) {
$type = new stdClass();
$type->type = $form_state['values']['type'];
$type->name = trim($form_state['values']['name']);
// Work out what the type was before the user submitted this form
$old_type = $form_state['values']['old_type'];
$types = node_type_get_names();
if (!$form_state['values']['locked']) {
// 'theme' conflicts with theme_node_form().
// '0' is invalid, since elsewhere we check it using empty().
if (in_array($type->type, array('0', 'theme'))) {
form_set_error('type', t("Invalid machine-readable name. Enter a name other than %invalid.", array('%invalid' => $type->type)));
}
}
$names = array_flip($types);
if (isset($names[$type->name]) && $names[$type->name] != $old_type) {
form_set_error('name', t('The human-readable name %name is already taken.', array('%name' => $type->name)));
}
}
/**
* Form submission handler for node_type_form().
*
* @see node_type_form_validate()
*/
function node_type_form_submit($form, &$form_state) {
$op = isset($form_state['values']['op']) ? $form_state['values']['op'] : '';
$type = node_type_set_defaults();
$type->type = $form_state['values']['type'];
$type->name = trim($form_state['values']['name']);
$type->orig_type = trim($form_state['values']['orig_type']);
$type->old_type = isset($form_state['values']['old_type']) ? $form_state['values']['old_type'] : $type->type;
$type->description = $form_state['values']['description'];
$type->help = $form_state['values']['help'];
$type->title_label = $form_state['values']['title_label'];
// title_label is required in core; has_title will always be true, unless a
// module alters the title field.
$type->has_title = ($type->title_label != '');
$type->base = !empty($form_state['values']['base']) ? $form_state['values']['base'] : 'node_content';
$type->custom = $form_state['values']['custom'];
$type->modified = TRUE;
$type->locked = $form_state['values']['locked'];
if (isset($form['#node_type']->module)) {
$type->module = $form['#node_type']->module;
}
if ($op == t('Delete content type')) {
$form_state['redirect'] = 'admin/structure/types/manage/' . str_replace('_', '-', $type->old_type) . '/delete';
return;
}
$variables = $form_state['values'];
// Remove everything that's been saved already - whatever's left is assumed
// to be a persistent variable.
foreach ($variables as $key => $value) {
if (isset($type->$key)) {
unset($variables[$key]);
}
}
unset($variables['form_token'], $variables['op'], $variables['submit'], $variables['delete'], $variables['reset'], $variables['form_id'], $variables['form_build_id']);
// Save or reset persistent variable values.
foreach ($variables as $key => $value) {
$variable_new = $key . '_' . $type->type;
$variable_old = $key . '_' . $type->old_type;
if (is_array($value)) {
$value = array_keys(array_filter($value));
}
variable_set($variable_new, $value);
if ($variable_new != $variable_old) {
variable_del($variable_old);
}
}
// Saving the content type after saving the variables allows modules to act
// on those variables via hook_node_type_insert().
$status = node_type_save($type);
node_types_rebuild();
menu_rebuild();
$t_args = array('%name' => $type->name);
if ($status == SAVED_UPDATED) {
drupal_set_message(t('The content type %name has been updated.', $t_args));
}
elseif ($status == SAVED_NEW) {
node_add_body_field($type);
drupal_set_message(t('The content type %name has been added.', $t_args));
watchdog('node', 'Added content type %name.', $t_args, WATCHDOG_NOTICE, l(t('view'), 'admin/structure/types'));
}
$form_state['redirect'] = 'admin/structure/types';
return;
}
/**
* Implements hook_node_type_insert().
*/
function node_node_type_insert($info) {
if (!empty($info->old_type) && $info->old_type != $info->type) {
$update_count = node_type_update_nodes($info->old_type, $info->type);
if ($update_count) {
drupal_set_message(format_plural($update_count, 'Changed the content type of 1 post from %old-type to %type.', 'Changed the content type of @count posts from %old-type to %type.', array('%old-type' => $info->old_type, '%type' => $info->type)));
}
}
}
/**
* Implements hook_node_type_update().
*/
function node_node_type_update($info) {
if (!empty($info->old_type) && $info->old_type != $info->type) {
$update_count = node_type_update_nodes($info->old_type, $info->type);
if ($update_count) {
drupal_set_message(format_plural($update_count, 'Changed the content type of 1 post from %old-type to %type.', 'Changed the content type of @count posts from %old-type to %type.', array('%old-type' => $info->old_type, '%type' => $info->type)));
}
}
}
/**
* Resets relevant fields of a module-defined node type to their default values.
*
* @param $type
* The node type to reset. The node type is passed back by reference with its
* resetted values. If there is no module-defined info for this node type,
* then nothing happens.
*/
function node_type_reset($type) {
$info_array = module_invoke_all('node_info');
if (isset($info_array[$type->orig_type])) {
$info_array[$type->orig_type]['type'] = $type->orig_type;
$info = node_type_set_defaults($info_array[$type->orig_type]);
foreach ($info as $field => $value) {
$type->$field = $value;
}
}
}
/**
* Menu callback; delete a single content type.
*
* @ingroup forms
*/
function node_type_delete_confirm($form, &$form_state, $type) {
$form['type'] = array('#type' => 'value', '#value' => $type->type);
$form['name'] = array('#type' => 'value', '#value' => $type->name);
$message = t('Are you sure you want to delete the content type %type?', array('%type' => $type->name));
$caption = '';
$num_nodes = db_query("SELECT COUNT(*) FROM {node} WHERE type = :type", array(':type' => $type->type))->fetchField();
if ($num_nodes) {
$caption .= '<p>' . format_plural($num_nodes, '%type is used by 1 piece of content on your site. If you remove this content type, you will not be able to edit the %type content and it may not display correctly.', '%type is used by @count pieces of content on your site. If you remove %type, you will not be able to edit the %type content and it may not display correctly.', array('%type' => $type->name)) . '</p>';
}
$caption .= '<p>' . t('This action cannot be undone.') . '</p>';
return confirm_form($form, $message, 'admin/structure/types', $caption, t('Delete'));
}
/**
* Process content type delete confirm submissions.
*
* @see node_type_delete_confirm()
*/
function node_type_delete_confirm_submit($form, &$form_state) {
node_type_delete($form_state['values']['type']);
variable_del('node_preview_' . $form_state['values']['type']);
$t_args = array('%name' => $form_state['values']['name']);
drupal_set_message(t('The content type %name has been deleted.', $t_args));
watchdog('node', 'Deleted content type %name.', $t_args, WATCHDOG_NOTICE);
node_types_rebuild();
menu_rebuild();
$form_state['redirect'] = 'admin/structure/types';
return;
}

View file

@ -0,0 +1,34 @@
(function ($) {
Drupal.behaviors.contentTypes = {
attach: function (context) {
// Provide the vertical tab summaries.
$('fieldset#edit-submission', context).drupalSetSummary(function(context) {
var vals = [];
vals.push(Drupal.checkPlain($('#edit-title-label', context).val()) || Drupal.t('Requires a title'));
return vals.join(', ');
});
$('fieldset#edit-workflow', context).drupalSetSummary(function(context) {
var vals = [];
$("input[name^='node_options']:checked", context).parent().each(function() {
vals.push(Drupal.checkPlain($(this).text()));
});
if (!$('#edit-node-options-status', context).is(':checked')) {
vals.unshift(Drupal.t('Not published'));
}
return vals.join(', ');
});
$('fieldset#edit-display', context).drupalSetSummary(function(context) {
var vals = [];
$('input:checked', context).next('label').each(function() {
vals.push(Drupal.checkPlain($(this).text()));
});
if (!$('#edit-node-submitted', context).is(':checked')) {
vals.unshift(Drupal.t("Don't display post information"));
}
return vals.join(', ');
});
}
};
})(jQuery);

712
modules/node/node.admin.inc Normal file
View file

@ -0,0 +1,712 @@
<?php
/**
* @file
* Content administration and module settings UI.
*/
/**
* Menu callback: confirm rebuilding of permissions.
*
* @see node_configure_rebuild_confirm_submit()
* @see node_menu()
* @ingroup forms
*/
function node_configure_rebuild_confirm() {
return confirm_form(array(), t('Are you sure you want to rebuild the permissions on site content?'),
'admin/reports/status', t('This action rebuilds all permissions on site content, and may be a lengthy process. This action cannot be undone.'), t('Rebuild permissions'), t('Cancel'));
}
/**
* Handler for wipe confirmation
*
* @see node_configure_rebuild_confirm()
*/
function node_configure_rebuild_confirm_submit($form, &$form_state) {
node_access_rebuild(TRUE);
$form_state['redirect'] = 'admin/reports/status';
}
/**
* Implements hook_node_operations().
*/
function node_node_operations() {
$operations = array(
'publish' => array(
'label' => t('Publish selected content'),
'callback' => 'node_mass_update',
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED)),
),
'unpublish' => array(
'label' => t('Unpublish selected content'),
'callback' => 'node_mass_update',
'callback arguments' => array('updates' => array('status' => NODE_NOT_PUBLISHED)),
),
'promote' => array(
'label' => t('Promote selected content to front page'),
'callback' => 'node_mass_update',
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'promote' => NODE_PROMOTED)),
),
'demote' => array(
'label' => t('Demote selected content from front page'),
'callback' => 'node_mass_update',
'callback arguments' => array('updates' => array('promote' => NODE_NOT_PROMOTED)),
),
'sticky' => array(
'label' => t('Make selected content sticky'),
'callback' => 'node_mass_update',
'callback arguments' => array('updates' => array('status' => NODE_PUBLISHED, 'sticky' => NODE_STICKY)),
),
'unsticky' => array(
'label' => t('Make selected content not sticky'),
'callback' => 'node_mass_update',
'callback arguments' => array('updates' => array('sticky' => NODE_NOT_STICKY)),
),
'delete' => array(
'label' => t('Delete selected content'),
'callback' => NULL,
),
);
return $operations;
}
/**
* List node administration filters that can be applied.
*
* @return
* An associative array of filters.
*/
function node_filters() {
// Regular filters
$filters['status'] = array(
'title' => t('status'),
'options' => array(
'[any]' => t('any'),
'status-1' => t('published'),
'status-0' => t('not published'),
'promote-1' => t('promoted'),
'promote-0' => t('not promoted'),
'sticky-1' => t('sticky'),
'sticky-0' => t('not sticky'),
),
);
// Include translation states if we have this module enabled
if (module_exists('translation')) {
$filters['status']['options'] += array(
'translate-0' => t('Up to date translation'),
'translate-1' => t('Outdated translation'),
);
}
$filters['type'] = array(
'title' => t('type'),
'options' => array(
'[any]' => t('any'),
) + node_type_get_names(),
);
// Language filter if there is a list of languages
if ($languages = module_invoke('locale', 'language_list')) {
$languages = array(LANGUAGE_NONE => t('Language neutral')) + $languages;
$filters['language'] = array(
'title' => t('language'),
'options' => array(
'[any]' => t('any'),
) + $languages,
);
}
return $filters;
}
/**
* Applies filters for node administration filters based on session.
*
* @param $query
* A SelectQuery to which the filters should be applied.
*/
function node_build_filter_query(SelectQueryInterface $query) {
// Build query
$filter_data = isset($_SESSION['node_overview_filter']) ? $_SESSION['node_overview_filter'] : array();
foreach ($filter_data as $index => $filter) {
list($key, $value) = $filter;
switch ($key) {
case 'status':
// Note: no exploitable hole as $key/$value have already been checked when submitted
list($key, $value) = explode('-', $value, 2);
case 'type':
case 'language':
$query->condition('n.' . $key, $value);
break;
}
}
}
/**
* Returns the node administration filters form array to node_admin_content().
*
* @see node_admin_nodes()
* @see node_admin_nodes_submit()
* @see node_admin_nodes_validate()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm()
* @see node_multiple_delete_confirm_submit()
*
* @ingroup forms
*/
function node_filter_form() {
$session = isset($_SESSION['node_overview_filter']) ? $_SESSION['node_overview_filter'] : array();
$filters = node_filters();
$i = 0;
$form['filters'] = array(
'#type' => 'fieldset',
'#title' => t('Show only items where'),
'#theme' => 'exposed_filters__node',
);
foreach ($session as $filter) {
list($type, $value) = $filter;
if ($type == 'term') {
// Load term name from DB rather than search and parse options array.
$value = module_invoke('taxonomy', 'term_load', $value);
$value = $value->name;
}
elseif ($type == 'language') {
$value = $value == LANGUAGE_NONE ? t('Language neutral') : module_invoke('locale', 'language_name', $value);
}
else {
$value = $filters[$type]['options'][$value];
}
$t_args = array('%property' => $filters[$type]['title'], '%value' => $value);
if ($i++) {
$form['filters']['current'][] = array('#markup' => t('and where %property is %value', $t_args));
}
else {
$form['filters']['current'][] = array('#markup' => t('where %property is %value', $t_args));
}
if (in_array($type, array('type', 'language'))) {
// Remove the option if it is already being filtered on.
unset($filters[$type]);
}
}
$form['filters']['status'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('clearfix')),
'#prefix' => ($i ? '<div class="additional-filters">' . t('and where') . '</div>' : ''),
);
$form['filters']['status']['filters'] = array(
'#type' => 'container',
'#attributes' => array('class' => array('filters')),
);
foreach ($filters as $key => $filter) {
$form['filters']['status']['filters'][$key] = array(
'#type' => 'select',
'#options' => $filter['options'],
'#title' => $filter['title'],
'#default_value' => '[any]',
);
}
$form['filters']['status']['actions'] = array(
'#type' => 'actions',
'#attributes' => array('class' => array('container-inline')),
);
$form['filters']['status']['actions']['submit'] = array(
'#type' => 'submit',
'#value' => count($session) ? t('Refine') : t('Filter'),
);
if (count($session)) {
$form['filters']['status']['actions']['undo'] = array('#type' => 'submit', '#value' => t('Undo'));
$form['filters']['status']['actions']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
}
drupal_add_js('misc/form.js');
return $form;
}
/**
* Form submission handler for node_filter_form().
*
* @see node_admin_content()
* @see node_admin_nodes()
* @see node_admin_nodes_submit()
* @see node_admin_nodes_validate()
* @see node_filter_form()
* @see node_multiple_delete_confirm()
* @see node_multiple_delete_confirm_submit()
*/
function node_filter_form_submit($form, &$form_state) {
$filters = node_filters();
switch ($form_state['values']['op']) {
case t('Filter'):
case t('Refine'):
// Apply every filter that has a choice selected other than 'any'.
foreach ($filters as $filter => $options) {
if (isset($form_state['values'][$filter]) && $form_state['values'][$filter] != '[any]') {
// Flatten the options array to accommodate hierarchical/nested options.
$flat_options = form_options_flatten($filters[$filter]['options']);
// Only accept valid selections offered on the dropdown, block bad input.
if (isset($flat_options[$form_state['values'][$filter]])) {
$_SESSION['node_overview_filter'][] = array($filter, $form_state['values'][$filter]);
}
}
}
break;
case t('Undo'):
array_pop($_SESSION['node_overview_filter']);
break;
case t('Reset'):
$_SESSION['node_overview_filter'] = array();
break;
}
}
/**
* Make mass update of nodes, changing all nodes in the $nodes array
* to update them with the field values in $updates.
*
* IMPORTANT NOTE: This function is intended to work when called from a form
* submission handler. Calling it outside of the form submission process may not
* work correctly.
*
* @param array $nodes
* Array of node nids to update.
* @param array $updates
* Array of key/value pairs with node field names and the value to update that
* field to.
*/
function node_mass_update($nodes, $updates) {
// We use batch processing to prevent timeout when updating a large number
// of nodes.
if (count($nodes) > 10) {
$batch = array(
'operations' => array(
array('_node_mass_update_batch_process', array($nodes, $updates))
),
'finished' => '_node_mass_update_batch_finished',
'title' => t('Processing'),
// We use a single multi-pass operation, so the default
// 'Remaining x of y operations' message will be confusing here.
'progress_message' => '',
'error_message' => t('The update has encountered an error.'),
// The operations do not live in the .module file, so we need to
// tell the batch engine which file to load before calling them.
'file' => drupal_get_path('module', 'node') . '/node.admin.inc',
);
batch_set($batch);
}
else {
foreach ($nodes as $nid) {
_node_mass_update_helper($nid, $updates);
}
drupal_set_message(t('The update has been performed.'));
}
}
/**
* Updates individual nodes when fewer than 10 are queued.
*
* @param $nid
* ID of node to update.
* @param $updates
* Associative array of updates.
*
* @return object
* An updated node object.
*
* @see node_mass_update()
*/
function _node_mass_update_helper($nid, $updates) {
$node = node_load($nid, NULL, TRUE);
// For efficiency manually save the original node before applying any changes.
$node->original = clone $node;
foreach ($updates as $name => $value) {
$node->$name = $value;
}
node_save($node);
return $node;
}
/**
* Implements callback_batch_operation().
*
* Executes a batch operation for node_mass_update().
*
* @param array $nodes
* An array of node IDs.
* @param array $updates
* Associative array of updates.
* @param array $context
* An array of contextual key/values.
*/
function _node_mass_update_batch_process($nodes, $updates, &$context) {
if (!isset($context['sandbox']['progress'])) {
$context['sandbox']['progress'] = 0;
$context['sandbox']['max'] = count($nodes);
$context['sandbox']['nodes'] = $nodes;
}
// Process nodes by groups of 5.
$count = min(5, count($context['sandbox']['nodes']));
for ($i = 1; $i <= $count; $i++) {
// For each nid, load the node, reset the values, and save it.
$nid = array_shift($context['sandbox']['nodes']);
$node = _node_mass_update_helper($nid, $updates);
// Store result for post-processing in the finished callback.
$context['results'][] = l($node->title, 'node/' . $node->nid);
// Update our progress information.
$context['sandbox']['progress']++;
}
// Inform the batch engine that we are not finished,
// and provide an estimation of the completion level we reached.
if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
$context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
}
}
/**
* Implements callback_batch_finished().
*
* Reports the status of batch operation for node_mass_update().
*
* @param bool $success
* A boolean indicating whether the batch mass update operation successfully
* concluded.
* @param int $results
* The number of nodes updated via the batch mode process.
* @param array $operations
* An array of function calls (not used in this function).
*/
function _node_mass_update_batch_finished($success, $results, $operations) {
if ($success) {
drupal_set_message(t('The update has been performed.'));
}
else {
drupal_set_message(t('An error occurred and processing did not complete.'), 'error');
$message = format_plural(count($results), '1 item successfully processed:', '@count items successfully processed:');
$message .= theme('item_list', array('items' => $results));
drupal_set_message($message);
}
}
/**
* Page callback: Form constructor for the content administration form.
*
* @see node_admin_nodes()
* @see node_admin_nodes_submit()
* @see node_admin_nodes_validate()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_menu()
* @see node_multiple_delete_confirm()
* @see node_multiple_delete_confirm_submit()
* @ingroup forms
*/
function node_admin_content($form, $form_state) {
if (isset($form_state['values']['operation']) && $form_state['values']['operation'] == 'delete') {
return node_multiple_delete_confirm($form, $form_state, array_filter($form_state['values']['nodes']));
}
$form['filter'] = node_filter_form();
$form['#submit'][] = 'node_filter_form_submit';
$form['admin'] = node_admin_nodes();
return $form;
}
/**
* Form builder: Builds the node administration overview.
*
* @see node_admin_nodes_submit()
* @see node_admin_nodes_validate()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm()
* @see node_multiple_delete_confirm_submit()
*
* @ingroup forms
*/
function node_admin_nodes() {
$admin_access = user_access('administer nodes');
// Build the 'Update options' form.
$form['options'] = array(
'#type' => 'fieldset',
'#title' => t('Update options'),
'#attributes' => array('class' => array('container-inline')),
'#access' => $admin_access,
);
$options = array();
foreach (module_invoke_all('node_operations') as $operation => $array) {
$options[$operation] = $array['label'];
}
$form['options']['operation'] = array(
'#type' => 'select',
'#title' => t('Operation'),
'#title_display' => 'invisible',
'#options' => $options,
'#default_value' => 'approve',
);
$form['options']['submit'] = array(
'#type' => 'submit',
'#value' => t('Update'),
'#validate' => array('node_admin_nodes_validate'),
'#submit' => array('node_admin_nodes_submit'),
);
// Enable language column if translation module is enabled or if we have any
// node with language.
$multilanguage = (module_exists('translation') || db_query_range("SELECT 1 FROM {node} WHERE language <> :language", 0, 1, array(':language' => LANGUAGE_NONE))->fetchField());
// Build the sortable table header.
$header = array(
'title' => array('data' => t('Title'), 'field' => 'n.title'),
'type' => array('data' => t('Type'), 'field' => 'n.type'),
'author' => t('Author'),
'status' => array('data' => t('Status'), 'field' => 'n.status'),
'changed' => array('data' => t('Updated'), 'field' => 'n.changed', 'sort' => 'desc')
);
if ($multilanguage) {
$header['language'] = array('data' => t('Language'), 'field' => 'n.language');
}
$header['operations'] = array('data' => t('Operations'));
$query = db_select('node', 'n')->extend('PagerDefault')->extend('TableSort');
$query->addTag('node_admin_filter');
node_build_filter_query($query);
if (!user_access('bypass node access')) {
// If the user is able to view their own unpublished nodes, allow them
// to see these in addition to published nodes. Check that they actually
// have some unpublished nodes to view before adding the condition.
if (user_access('view own unpublished content') && $own_unpublished = db_query('SELECT nid FROM {node} WHERE uid = :uid AND status = :status', array(':uid' => $GLOBALS['user']->uid, ':status' => 0))->fetchCol()) {
$query->condition(db_or()
->condition('n.status', 1)
->condition('n.nid', $own_unpublished, 'IN')
);
}
else {
// If not, restrict the query to published nodes.
$query->condition('n.status', 1);
}
}
$nids = $query
->fields('n',array('nid'))
->limit(50)
->orderByHeader($header)
->addTag('node_access')
->execute()
->fetchCol();
$nodes = node_load_multiple($nids);
// Prepare the list of nodes.
$languages = language_list();
$destination = drupal_get_destination();
$options = array();
foreach ($nodes as $node) {
$langcode = entity_language('node', $node);
$uri = entity_uri('node', $node);
if ($langcode != LANGUAGE_NONE && isset($languages[$langcode])) {
$uri['options']['language'] = $languages[$langcode];
}
$options[$node->nid] = array(
'title' => array(
'data' => array(
'#type' => 'link',
'#title' => $node->title,
'#href' => $uri['path'],
'#options' => $uri['options'],
'#suffix' => ' ' . theme('mark', array('type' => node_mark($node->nid, $node->changed))),
),
),
'type' => check_plain(node_type_get_name($node)),
'author' => theme('username', array('account' => $node)),
'status' => $node->status ? t('published') : t('not published'),
'changed' => format_date($node->changed, 'short'),
);
if ($multilanguage) {
if ($langcode == LANGUAGE_NONE || isset($languages[$langcode])) {
$options[$node->nid]['language'] = $langcode == LANGUAGE_NONE ? t('Language neutral') : t($languages[$langcode]->name);
}
else {
$options[$node->nid]['language'] = t('Undefined language (@langcode)', array('@langcode' => $langcode));
}
}
// Build a list of all the accessible operations for the current node.
$operations = array();
if (node_access('update', $node)) {
$operations['edit'] = array(
'title' => t('edit'),
'href' => 'node/' . $node->nid . '/edit',
'query' => $destination,
);
}
if (node_access('delete', $node)) {
$operations['delete'] = array(
'title' => t('delete'),
'href' => 'node/' . $node->nid . '/delete',
'query' => $destination,
);
}
$options[$node->nid]['operations'] = array();
if (count($operations) > 1) {
// Render an unordered list of operations links.
$options[$node->nid]['operations'] = array(
'data' => array(
'#theme' => 'links__node_operations',
'#links' => $operations,
'#attributes' => array('class' => array('links', 'inline')),
),
);
}
elseif (!empty($operations)) {
// Render the first and only operation as a link.
$link = reset($operations);
$options[$node->nid]['operations'] = array(
'data' => array(
'#type' => 'link',
'#title' => $link['title'],
'#href' => $link['href'],
'#options' => array('query' => $link['query']),
),
);
}
}
// Only use a tableselect when the current user is able to perform any
// operations.
if ($admin_access) {
$form['nodes'] = array(
'#type' => 'tableselect',
'#header' => $header,
'#options' => $options,
'#empty' => t('No content available.'),
);
}
// Otherwise, use a simple table.
else {
$form['nodes'] = array(
'#theme' => 'table',
'#header' => $header,
'#rows' => $options,
'#empty' => t('No content available.'),
);
}
$form['pager'] = array('#markup' => theme('pager'));
return $form;
}
/**
* Validate node_admin_nodes form submissions.
*
* Checks whether any nodes have been selected to perform the chosen 'Update
* option' on.
*
* @see node_admin_nodes()
* @see node_admin_nodes_submit()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm()
* @see node_multiple_delete_confirm_submit()
*/
function node_admin_nodes_validate($form, &$form_state) {
// Error if there are no items to select.
if (!is_array($form_state['values']['nodes']) || !count(array_filter($form_state['values']['nodes']))) {
form_set_error('', t('No items selected.'));
}
}
/**
* Process node_admin_nodes form submissions.
*
* Executes the chosen 'Update option' on the selected nodes.
*
* @see node_admin_nodes()
* @see node_admin_nodes_validate()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm()
* @see node_multiple_delete_confirm_submit()
*/
function node_admin_nodes_submit($form, &$form_state) {
$operations = module_invoke_all('node_operations');
$operation = $operations[$form_state['values']['operation']];
// Filter out unchecked nodes
$nodes = array_filter($form_state['values']['nodes']);
if ($function = $operation['callback']) {
// Add in callback arguments if present.
if (isset($operation['callback arguments'])) {
$args = array_merge(array($nodes), $operation['callback arguments']);
}
else {
$args = array($nodes);
}
call_user_func_array($function, $args);
cache_clear_all();
}
else {
// We need to rebuild the form to go to a second step. For example, to
// show the confirmation form for the deletion of nodes.
$form_state['rebuild'] = TRUE;
}
}
/**
* Multiple node deletion confirmation form for node_admin_content().
*
* @see node_admin_nodes()
* @see node_admin_nodes_submit()
* @see node_admin_nodes_validate()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm_submit()
* @ingroup forms
*/
function node_multiple_delete_confirm($form, &$form_state, $nodes) {
$form['nodes'] = array('#prefix' => '<ul>', '#suffix' => '</ul>', '#tree' => TRUE);
// array_filter returns only elements with TRUE values
foreach ($nodes as $nid => $value) {
$title = db_query('SELECT title FROM {node} WHERE nid = :nid', array(':nid' => $nid))->fetchField();
$form['nodes'][$nid] = array(
'#type' => 'hidden',
'#value' => $nid,
'#prefix' => '<li>',
'#suffix' => check_plain($title) . "</li>\n",
);
}
$form['operation'] = array('#type' => 'hidden', '#value' => 'delete');
$form['#submit'][] = 'node_multiple_delete_confirm_submit';
$confirm_question = format_plural(count($nodes),
'Are you sure you want to delete this item?',
'Are you sure you want to delete these items?');
return confirm_form($form,
$confirm_question,
'admin/content', t('This action cannot be undone.'),
t('Delete'), t('Cancel'));
}
/**
* Form submission handler for node_multiple_delete_confirm().
*
* @see node_admin_nodes()
* @see node_admin_nodes_submit()
* @see node_admin_nodes_validate()
* @see node_filter_form()
* @see node_filter_form_submit()
* @see node_multiple_delete_confirm()
*/
function node_multiple_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
node_delete_multiple(array_keys($form_state['values']['nodes']));
cache_clear_all();
$count = count($form_state['values']['nodes']);
watchdog('content', 'Deleted @count posts.', array('@count' => $count));
drupal_set_message(format_plural($count, 'Deleted 1 post.', 'Deleted @count posts.'));
}
$form_state['redirect'] = 'admin/content';
}

1333
modules/node/node.api.php Normal file

File diff suppressed because it is too large Load diff

10
modules/node/node.css Normal file
View file

@ -0,0 +1,10 @@
.node-unpublished {
background-color: #fff4f4;
}
.preview .node {
background-color: #ffffea;
}
td.revision-current {
background: #ffc;
}

16
modules/node/node.info Normal file
View file

@ -0,0 +1,16 @@
name = Node
description = Allows content to be submitted to the site and displayed on pages.
package = Core
version = VERSION
core = 7.x
files[] = node.module
files[] = node.test
required = TRUE
configure = admin/structure/types
stylesheets[all][] = node.css
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

966
modules/node/node.install Normal file
View file

@ -0,0 +1,966 @@
<?php
/**
* @file
* Install, update and uninstall functions for the node module.
*/
/**
* Implements hook_schema().
*/
function node_schema() {
$schema['node'] = array(
'description' => 'The base table for nodes.',
'fields' => array(
'nid' => array(
'description' => 'The primary identifier for a node.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
// Defaults to NULL in order to avoid a brief period of potential
// deadlocks on the index.
'vid' => array(
'description' => 'The current {node_revision}.vid version identifier.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'default' => NULL,
),
'type' => array(
'description' => 'The {node_type}.type of this node.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'default' => '',
),
'language' => array(
'description' => 'The {languages}.language of this node.',
'type' => 'varchar',
'length' => 12,
'not null' => TRUE,
'default' => '',
),
'title' => array(
'description' => 'The title of this node, always treated as non-markup plain text.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'uid' => array(
'description' => 'The {users}.uid that owns this node; initially, this is the user that created it.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'status' => array(
'description' => 'Boolean indicating whether the node is published (visible to non-administrators).',
'type' => 'int',
'not null' => TRUE,
'default' => 1,
),
'created' => array(
'description' => 'The Unix timestamp when the node was created.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'changed' => array(
'description' => 'The Unix timestamp when the node was most recently saved.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'comment' => array(
'description' => 'Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(
'description' => 'Boolean indicating whether the node should be displayed on the front page.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'sticky' => array(
'description' => 'Boolean indicating whether the node should be displayed at the top of lists in which it appears.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'tnid' => array(
'description' => 'The translation set id for this node, which equals the node id of the source post in each set.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'translate' => array(
'description' => 'A boolean indicating whether this translation page needs to be updated.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'indexes' => array(
'node_changed' => array('changed'),
'node_created' => array('created'),
'node_frontpage' => array('promote', 'status', 'sticky', 'created'),
'node_status_type' => array('status', 'type', 'nid'),
'node_title_type' => array('title', array('type', 4)),
'node_type' => array(array('type', 4)),
'uid' => array('uid'),
'tnid' => array('tnid'),
'translate' => array('translate'),
'language' => array('language'),
),
'unique keys' => array(
'vid' => array('vid'),
),
'foreign keys' => array(
'node_revision' => array(
'table' => 'node_revision',
'columns' => array('vid' => 'vid'),
),
'node_author' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
),
'primary key' => array('nid'),
);
$schema['node_access'] = array(
'description' => 'Identifies which realm/grant pairs a user must possess in order to view, update, or delete specific nodes.',
'fields' => array(
'nid' => array(
'description' => 'The {node}.nid this record affects.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'gid' => array(
'description' => "The grant ID a user must possess in the specified realm to gain this row's privileges on the node.",
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'realm' => array(
'description' => 'The realm in which the user must possess the grant ID. Each node access node can define one or more realms.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'grant_view' => array(
'description' => 'Boolean indicating whether a user with the realm/grant pair can view this node.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'grant_update' => array(
'description' => 'Boolean indicating whether a user with the realm/grant pair can edit this node.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'grant_delete' => array(
'description' => 'Boolean indicating whether a user with the realm/grant pair can delete this node.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
),
'primary key' => array('nid', 'gid', 'realm'),
'foreign keys' => array(
'affected_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
),
);
$schema['node_revision'] = array(
'description' => 'Stores information about each saved version of a {node}.',
'fields' => array(
'nid' => array(
'description' => 'The {node} this version belongs to.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'vid' => array(
'description' => 'The primary identifier for this version.',
'type' => 'serial',
'unsigned' => TRUE,
'not null' => TRUE,
),
'uid' => array(
'description' => 'The {users}.uid that created this version.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'title' => array(
'description' => 'The title of this version.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
'log' => array(
'description' => 'The log entry explaining the changes in this version.',
'type' => 'text',
'not null' => TRUE,
'size' => 'big',
),
'timestamp' => array(
'description' => 'A Unix timestamp indicating when this version was created.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'status' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) is published (visible to non-administrators).',
'type' => 'int',
'not null' => TRUE,
'default' => 1,
),
'comment' => array(
'description' => 'Whether comments are allowed on this node (at the time of this revision): 0 = no, 1 = closed (read only), 2 = open (read/write).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'promote' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed on the front page.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'sticky' => array(
'description' => 'Boolean indicating whether the node (at the time of this revision) should be displayed at the top of lists in which it appears.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'indexes' => array(
'nid' => array('nid'),
'uid' => array('uid'),
),
'primary key' => array('vid'),
'foreign keys' => array(
'versioned_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
'version_author' => array(
'table' => 'users',
'columns' => array('uid' => 'uid'),
),
),
);
$schema['node_type'] = array(
'description' => 'Stores information about all defined {node} types.',
'fields' => array(
'type' => array(
'description' => 'The machine-readable name of this type.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
),
'name' => array(
'description' => 'The human-readable name of this type.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'translatable' => TRUE,
),
'base' => array(
'description' => 'The base string used to construct callbacks corresponding to this node type.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
),
'module' => array(
'description' => 'The module defining this node type.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
),
'description' => array(
'description' => 'A brief description of this type.',
'type' => 'text',
'not null' => TRUE,
'size' => 'medium',
'translatable' => TRUE,
),
'help' => array(
'description' => 'Help information shown to the user when creating a {node} of this type.',
'type' => 'text',
'not null' => TRUE,
'size' => 'medium',
'translatable' => TRUE,
),
'has_title' => array(
'description' => 'Boolean indicating whether this type uses the {node}.title field.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'size' => 'tiny',
),
'title_label' => array(
'description' => 'The label displayed for the title field on the edit form.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
'translatable' => TRUE,
),
'custom' => array(
'description' => 'A boolean indicating whether this type is defined by a module (FALSE) or by a user via Add content type (TRUE).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'modified' => array(
'description' => 'A boolean indicating whether this type has been modified by an administrator; currently not used in any way.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'locked' => array(
'description' => 'A boolean indicating whether the administrator can change the machine name of this type.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny',
),
'disabled' => array(
'description' => 'A boolean indicating whether the node type is disabled.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny'
),
'orig_type' => array(
'description' => 'The original machine-readable name of this node type. This may be different from the current type name if the locked field is 0.',
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
'default' => '',
),
),
'primary key' => array('type'),
);
$schema['block_node_type'] = array(
'description' => 'Sets up display criteria for blocks based on content types',
'fields' => array(
'module' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'description' => "The block's origin module, from {block}.module.",
),
'delta' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The block's unique delta within module, from {block}.delta.",
),
'type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The machine-readable name of this type from {node_type}.type.",
),
),
'primary key' => array('module', 'delta', 'type'),
'indexes' => array(
'type' => array('type'),
),
);
$schema['history'] = array(
'description' => 'A record of which {users} have read which {node}s.',
'fields' => array(
'uid' => array(
'description' => 'The {users}.uid that read the {node} nid.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
'nid' => array(
'description' => 'The {node}.nid that was read.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'timestamp' => array(
'description' => 'The Unix timestamp at which the read occurred.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('uid', 'nid'),
'indexes' => array(
'nid' => array('nid'),
),
);
return $schema;
}
/**
* Implements hook_install().
*/
function node_install() {
// Populate the node access table.
db_insert('node_access')
->fields(array(
'nid' => 0,
'gid' => 0,
'realm' => 'all',
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
))
->execute();
}
/**
* Implements hook_update_dependencies().
*/
function node_update_dependencies() {
// node_update_7006() migrates node data to fields and therefore must run
// after all Field modules have been enabled, which happens in
// system_update_7027(). It also needs to query the {filter_format} table to
// get a list of existing text formats, so it must run after
// filter_update_7000(), which creates that table.
$dependencies['node'][7006] = array(
'system' => 7027,
'filter' => 7000,
);
// node_update_7008() migrates role permissions and therefore must run after
// the {role} and {role_permission} tables are properly set up, which happens
// in user_update_7007().
$dependencies['node'][7008] = array(
'user' => 7007,
);
return $dependencies;
}
/**
* Utility function: fetch the node types directly from the database.
*
* This function is valid for a database schema version 7000.
*
* @ingroup update_api
*/
function _update_7000_node_get_types() {
$node_types = db_query('SELECT * FROM {node_type}')->fetchAllAssoc('type', PDO::FETCH_OBJ);
// Create default settings for orphan nodes.
$all_types = db_query('SELECT DISTINCT type FROM {node}')->fetchCol();
$extra_types = array_diff($all_types, array_keys($node_types));
foreach ($extra_types as $type) {
$type_object = new stdClass();
$type_object->type = $type;
// In Drupal 6, whether you have a body field or not is a flag in the node
// type table. If it's enabled, nodes may or may not have an empty string
// for the bodies. As we can't detect what this setting should be in
// Drupal 7 without access to the Drupal 6 node type settings, we assume
// the default, which is to enable the body field.
$type_object->has_body = 1;
$type_object->body_label = 'Body';
$node_types[$type_object->type] = $type_object;
}
return $node_types;
}
/**
* @addtogroup updates-6.x-to-7.x
* @{
*/
/**
* Upgrade the node type table and fix node type 'module' attribute to avoid name-space conflicts.
*/
function node_update_7000() {
// Rename the module column to base.
db_change_field('node_type', 'module', 'base', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE));
db_add_field('node_type', 'module', array(
'description' => 'The module defining this node type.',
'type' => 'varchar',
'default' => '',
'length' => 255,
'not null' => TRUE,
));
db_add_field('node_type', 'disabled', array(
'description' => 'A boolean indicating whether the node type is disabled.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
'size' => 'tiny'
));
$modules = db_select('system', 's')
->fields('s', array('name'))
->condition('type', 'module');
db_update('node_type')
->expression('module', 'base')
->condition('base', $modules, 'IN')
->execute();
db_update('node_type')
->fields(array('base' => 'node_content'))
->condition('base', 'node')
->execute();
}
/**
* Rename {node_revisions} table to {node_revision}.
*/
function node_update_7001() {
db_rename_table('node_revisions', 'node_revision');
}
/**
* Extend the node_promote_status index to include all fields required for the node page query.
*/
function node_update_7002() {
db_drop_index('node', 'node_promote_status');
db_add_index('node', 'node_frontpage', array('promote', 'status', 'sticky', 'created'));
}
/**
* Remove the node_counter if the statistics module is uninstalled.
*/
function node_update_7003() {
if (drupal_get_installed_schema_version('statistics') == SCHEMA_UNINSTALLED) {
db_drop_table('node_counter');
}
}
/**
* Extend the existing default preview and teaser settings to all node types.
*/
function node_update_7004() {
// Get original settings and all types.
$original_length = variable_get('teaser_length', 600);
$original_preview = variable_get('node_preview', 0);
// Map old preview setting to new values order.
$original_preview ? $original_preview = 2 : $original_preview = 1;
node_type_cache_reset();
// Apply original settings to all types.
foreach (_update_7000_node_get_types() as $type => $type_object) {
variable_set('teaser_length_' . $type, $original_length);
variable_set('node_preview_' . $type, $original_preview);
}
// Delete old variable but leave 'teaser_length' for aggregator module upgrade.
variable_del('node_preview');
}
/**
* Add status/comment/promote and sticky columns to the {node_revision} table.
*/
function node_update_7005() {
foreach (array('status', 'comment', 'promote', 'sticky') as $column) {
db_add_field('node_revision', $column, array(
'type' => 'int',
'not null' => TRUE,
'default' => 0,
));
}
}
/**
* Convert body and teaser from node properties to fields, and migrate status/comment/promote and sticky columns to the {node_revision} table.
*/
function node_update_7006(&$sandbox) {
$sandbox['#finished'] = 0;
// Get node type info for every invocation.
node_type_cache_reset();
if (!isset($sandbox['total'])) {
// Initial invocation.
// First, create the body field.
$body_field = array(
'field_name' => 'body',
'type' => 'text_with_summary',
'module' => 'text',
'cardinality' => 1,
'entity_types' => array('node'),
'translatable' => TRUE,
);
_update_7000_field_create_field($body_field);
$default_trim_length = variable_get('teaser_length', 600);
// Get node type info, specifically the body field settings.
$node_types = _update_7000_node_get_types();
// Add body field instances for existing node types.
foreach ($node_types as $node_type) {
if ($node_type->has_body) {
$trim_length = variable_get('teaser_length_' . $node_type->type, $default_trim_length);
$instance = array(
'entity_type' => 'node',
'bundle' => $node_type->type,
'label' => $node_type->body_label,
'description' => isset($node_type->description) ? $node_type->description : '',
'required' => (isset($node_type->min_word_count) && $node_type->min_word_count > 0) ? 1 : 0,
'widget' => array(
'type' => 'text_textarea_with_summary',
'settings' => array(
'rows' => 20,
'summary_rows' => 5,
),
'weight' => -4,
'module' => 'text',
),
'settings' => array('display_summary' => TRUE),
'display' => array(
'default' => array(
'label' => 'hidden',
'type' => 'text_default',
),
'teaser' => array(
'label' => 'hidden',
'type' => 'text_summary_or_trimmed',
'trim_length' => $trim_length,
),
),
);
_update_7000_field_create_instance($body_field, $instance);
variable_del('teaser_length_' . $node_type->type);
}
// Leave 'teaser_length' variable for aggregator module upgrade.
$sandbox['node_types_info'][$node_type->type] = array(
'has_body' => $node_type->has_body,
);
}
// Used below when updating the stored text format of each node body.
$sandbox['existing_text_formats'] = db_query("SELECT format FROM {filter_format}")->fetchCol();
// Initialize state for future calls.
$sandbox['last'] = 0;
$sandbox['count'] = 0;
$query = db_select('node', 'n');
$query->join('node_revision', 'nr', 'n.nid = nr.nid');
$sandbox['total'] = $query->countQuery()->execute()->fetchField();
$sandbox['body_field_id'] = $body_field['id'];
}
else {
// Subsequent invocations.
$found = FALSE;
if ($sandbox['total']) {
// Operate on every revision of every node (whee!), in batches.
$batch_size = 200;
$query = db_select('node_revision', 'nr');
$query->innerJoin('node', 'n', 'n.nid = nr.nid');
$query
->fields('nr', array('nid', 'vid', 'body', 'teaser', 'format'))
->fields('n', array('type', 'status', 'comment', 'promote', 'sticky', 'language'))
->condition('nr.vid', $sandbox['last'], '>')
->orderBy('nr.vid', 'ASC')
->range(0, $batch_size);
$revisions = $query->execute();
// Load each revision of each node, set up 'body'
// appropriately, and save the node's field data. Note that
// node_load() will not return the body or teaser values from
// {node_revision} because those columns have been removed from the
// schema structure in memory (but not yet from the database),
// so we get the values from the explicit query of the table
// instead.
foreach ($revisions as $revision) {
$found = TRUE;
if ($sandbox['node_types_info'][$revision->type]['has_body']) {
$node = (object) array(
'nid' => $revision->nid,
'vid' => $revision->vid,
'type' => $revision->type,
);
// After node_update_7009() we will always have LANGUAGE_NONE as
// language neutral language code, but here we still have empty
// strings.
$langcode = empty($revision->language) ? LANGUAGE_NONE : $revision->language;
if (!empty($revision->teaser) && $revision->teaser != text_summary($revision->body)) {
$node->body[$langcode][0]['summary'] = $revision->teaser;
}
// Do this after text_summary() above.
$break = '<!--break-->';
if (substr($revision->body, 0, strlen($break)) == $break) {
$revision->body = substr($revision->body, strlen($break));
}
$node->body[$langcode][0]['value'] = $revision->body;
// Update the revision's text format for the changes to the Drupal 7
// filter system. This uses the same kind of logic that occurs, for
// example, in user_update_7010(), but we do this here rather than
// via a separate set of database queries, since we are already
// migrating the data.
if (empty($revision->body) && empty($revision->format)) {
$node->body[$langcode][0]['format'] = NULL;
}
elseif (!in_array($revision->format, $sandbox['existing_text_formats'])) {
$node->body[$langcode][0]['format'] = variable_get('filter_default_format', 1);
}
else {
$node->body[$langcode][0]['format'] = $revision->format;
}
// This is a core update and no contrib modules are enabled yet, so
// we can assume default field storage for a faster update.
_update_7000_field_sql_storage_write('node', $node->type, $node->nid, $node->vid, 'body', $node->body);
}
// Migrate the status columns to the {node_revision} table.
db_update('node_revision')
->fields(array(
'status' => $revision->status,
'comment' => $revision->comment,
'promote' => $revision->promote,
'sticky' => $revision->sticky,
))
->condition('vid', $revision->vid)
->execute();
$sandbox['last'] = $revision->vid;
$sandbox['count'] += 1;
}
$sandbox['#finished'] = min(0.99, $sandbox['count'] / $sandbox['total']);
}
if (!$found) {
// All nodes are processed.
// Remove the now-obsolete body info from node_revision.
db_drop_field('node_revision', 'body');
db_drop_field('node_revision', 'teaser');
db_drop_field('node_revision', 'format');
// Remove node_type properties related to the former 'body'.
db_drop_field('node_type', 'has_body');
db_drop_field('node_type', 'body_label');
// We're done.
$sandbox['#finished'] = 1;
}
}
}
/**
* Remove column min_word_count.
*/
function node_update_7007() {
db_drop_field('node_type', 'min_word_count');
}
/**
* Split the 'administer nodes' permission from 'access content overview'.
*/
function node_update_7008() {
$roles = user_roles(FALSE, 'administer nodes');
foreach ($roles as $rid => $role) {
_update_7000_user_role_grant_permissions($rid, array('access content overview'), 'node');
}
}
/**
* Convert node languages from the empty string to LANGUAGE_NONE.
*/
function node_update_7009() {
db_update('node')
->fields(array('language' => LANGUAGE_NONE))
->condition('language', '')
->execute();
}
/**
* Add the {block_node_type} table.
*/
function node_update_7010() {
$schema['block_node_type'] = array(
'description' => 'Sets up display criteria for blocks based on content types',
'fields' => array(
'module' => array(
'type' => 'varchar',
'length' => 64,
'not null' => TRUE,
'description' => "The block's origin module, from {block}.module.",
),
'delta' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The block's unique delta within module, from {block}.delta.",
),
'type' => array(
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
'description' => "The machine-readable name of this type from {node_type}.type.",
),
),
'primary key' => array('module', 'delta', 'type'),
'indexes' => array(
'type' => array('type'),
),
);
db_create_table('block_node_type', $schema['block_node_type']);
}
/**
* @} End of "addtogroup updates-6.x-to-7.x".
*/
/**
* @addtogroup updates-7.x-extra
* @{
*/
/**
* Update the database from Drupal 6 to match the schema.
*/
function node_update_7011() {
// Drop node moderation field.
db_drop_field('node', 'moderate');
db_drop_index('node', 'node_moderate');
// Change {node_revision}.status field to default to 1.
db_change_field('node_revision', 'status', 'status', array(
'type' => 'int',
'not null' => TRUE,
'default' => 1,
));
// Change {node_type}.module field default.
db_change_field('node_type', 'module', 'module', array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
));
}
/**
* Switches body fields to untranslatable while upgrading from D6 and makes them language neutral.
*/
function node_update_7012() {
// If we are upgrading from D6, then body fields should be set back to
// untranslatable, as D6 did not know about the idea of translating fields,
// but only nodes. If a D7 > D7 update is running we need to skip this update,
// as it is a valid use case to have translatable body fields in this context.
if (variable_get('update_d6', FALSE)) {
// Make node bodies untranslatable: field_update_field() cannot be used
// throughout the upgrade process and we do not have an update counterpart
// for _update_7000_field_create_field(). Hence we are forced to update the
// 'field_config' table directly. This is a safe operation since it is
// being performed while upgrading from D6. Perfoming the same operation
// during a D7 update is highly discouraged.
db_update('field_config')
->fields(array('translatable' => 0))
->condition('field_name', 'body')
->execute();
// Switch field languages to LANGUAGE_NONE, since initially they were
// assigned the node language.
foreach (array('field_data_body', 'field_revision_body') as $table) {
db_update($table)
->fields(array('language' => LANGUAGE_NONE))
->execute();
}
node_type_cache_reset();
}
}
/**
* Change {node}.vid default value from 0 to NULL to avoid deadlock issues on MySQL.
*/
function node_update_7013() {
db_drop_unique_key('node', 'vid');
db_change_field('node', 'vid', 'vid', array(
'description' => 'The current {node_revision}.vid version identifier.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => FALSE,
'default' => NULL,
));
db_add_unique_key('node', 'vid', array('vid'));
}
/**
* Add an index on {node}.language.
*/
function node_update_7014() {
db_add_index('node', 'language', array('language'));
}
/**
* Enable node types that may have been erroneously disabled in Drupal 7.36.
*/
function node_update_7015() {
db_update('node_type')
->fields(array('disabled' => 0))
->condition('base', 'node_content')
->execute();
}
/**
* Change {history}.nid to an unsigned int in order to match {node}.nid.
*/
function node_update_7016() {
db_drop_primary_key('history');
db_drop_index('history', 'nid');
db_change_field('history', 'nid', 'nid', array(
'description' => 'The {node}.nid that was read.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
));
db_add_primary_key('history', array('uid', 'nid'));
db_add_index('history', 'nid', array('nid'));
}
/**
* @} End of "addtogroup updates-7.x-extra".
*/

43
modules/node/node.js Normal file
View file

@ -0,0 +1,43 @@
(function ($) {
Drupal.behaviors.nodeFieldsetSummaries = {
attach: function (context) {
$('fieldset.node-form-revision-information', context).drupalSetSummary(function (context) {
var revisionCheckbox = $('.form-item-revision input', context);
// Return 'New revision' if the 'Create new revision' checkbox is checked,
// or if the checkbox doesn't exist, but the revision log does. For users
// without the "Administer content" permission the checkbox won't appear,
// but the revision log will if the content type is set to auto-revision.
if (revisionCheckbox.is(':checked') || (!revisionCheckbox.length && $('.form-item-log textarea', context).length)) {
return Drupal.t('New revision');
}
return Drupal.t('No revision');
});
$('fieldset.node-form-author', context).drupalSetSummary(function (context) {
var name = $('.form-item-name input', context).val() || Drupal.settings.anonymous,
date = $('.form-item-date input', context).val();
return date ?
Drupal.t('By @name on @date', { '@name': name, '@date': date }) :
Drupal.t('By @name', { '@name': name });
});
$('fieldset.node-form-options', context).drupalSetSummary(function (context) {
var vals = [];
$('input:checked', context).parent().each(function () {
vals.push(Drupal.checkPlain($.trim($(this).text())));
});
if (!$('.form-item-status input', context).is(':checked')) {
vals.unshift(Drupal.t('Not published'));
}
return vals.join(', ');
});
}
};
})(jQuery);

4199
modules/node/node.module Normal file

File diff suppressed because it is too large Load diff

680
modules/node/node.pages.inc Normal file
View file

@ -0,0 +1,680 @@
<?php
/**
* @file
* Page callbacks for adding, editing, deleting, and revisions management for content.
*/
/**
* Menu callback; presents the node editing form.
*/
function node_page_edit($node) {
$type_name = node_type_get_name($node);
drupal_set_title(t('<em>Edit @type</em> @title', array('@type' => $type_name, '@title' => $node->title)), PASS_THROUGH);
return drupal_get_form($node->type . '_node_form', $node);
}
/**
* Page callback: Displays add content links for available content types.
*
* Redirects to node/add/[type] if only one content type is available.
*
* @see node_menu()
*/
function node_add_page() {
$item = menu_get_item();
$content = system_admin_menu_block($item);
// Bypass the node/add listing if only one content type is available.
if (count($content) == 1) {
$item = array_shift($content);
drupal_goto($item['href']);
}
return theme('node_add_list', array('content' => $content));
}
/**
* Returns HTML for a list of available node types for node creation.
*
* @param $variables
* An associative array containing:
* - content: An array of content types.
*
* @ingroup themeable
*/
function theme_node_add_list($variables) {
$content = $variables['content'];
$output = '';
if ($content) {
$output = '<dl class="node-type-list">';
foreach ($content as $item) {
$output .= '<dt>' . l($item['title'], $item['href'], $item['localized_options']) . '</dt>';
$output .= '<dd>' . filter_xss_admin($item['description']) . '</dd>';
}
$output .= '</dl>';
}
else {
$output = '<p>' . t('You have not created any content types yet. Go to the <a href="@create-content">content type creation page</a> to add a new content type.', array('@create-content' => url('admin/structure/types/add'))) . '</p>';
}
return $output;
}
/**
* Returns a node submission form.
*
* @param $type
* The node type for the submitted node.
*
* @return
* The themed form.
*/
function node_add($type) {
global $user;
$types = node_type_get_types();
$node = (object) array('uid' => $user->uid, 'name' => (isset($user->name) ? $user->name : ''), 'type' => $type, 'language' => LANGUAGE_NONE);
drupal_set_title(t('Create @name', array('@name' => $types[$type]->name)), PASS_THROUGH);
$output = drupal_get_form($type . '_node_form', $node);
return $output;
}
/**
* Form validation handler for node_form().
*
* @see node_form()
* @see node_form_submit()
*/
function node_form_validate($form, &$form_state) {
// $form_state['node'] contains the actual entity being edited, but we must
// not update it with form values that have not yet been validated, so we
// create a pseudo-entity to use during validation.
$node = (object) $form_state['values'];
node_validate($node, $form, $form_state);
entity_form_field_validate('node', $form, $form_state);
}
/**
* Form constructor for the node add/edit form.
*
* @see node_form_validate()
* @see node_form_submit()
* @see node_form_build_preview()
* @see node_form_delete_submit()
* @ingroup forms
*/
function node_form($form, &$form_state, $node) {
global $user;
// During initial form build, add the node entity to the form state for use
// during form building and processing. During a rebuild, use what is in the
// form state.
if (!isset($form_state['node'])) {
if (!isset($node->title)) {
$node->title = NULL;
}
node_object_prepare($node);
$form_state['node'] = $node;
}
else {
$node = $form_state['node'];
}
// Some special stuff when previewing a node.
if (isset($form_state['node_preview'])) {
$form['#prefix'] = $form_state['node_preview'];
$node->in_preview = TRUE;
}
else {
unset($node->in_preview);
}
// Identify this as a node edit form.
// @todo D8: Remove. Modules can implement hook_form_BASE_FORM_ID_alter() now.
$form['#node_edit_form'] = TRUE;
$form['#attributes']['class'][] = 'node-form';
if (!empty($node->type)) {
$form['#attributes']['class'][] = 'node-' . $node->type . '-form';
}
// Basic node information.
// These elements are just values so they are not even sent to the client.
foreach (array('nid', 'vid', 'uid', 'created', 'type', 'language') as $key) {
$form[$key] = array(
'#type' => 'value',
'#value' => isset($node->$key) ? $node->$key : NULL,
);
}
// Changed must be sent to the client, for later overwrite error checking.
$form['changed'] = array(
'#type' => 'hidden',
'#default_value' => isset($node->changed) ? $node->changed : NULL,
);
// Invoke hook_form() to get the node-specific bits. Can't use node_invoke(),
// because hook_form() needs to be able to receive $form_state by reference.
// @todo hook_form() implementations are unable to add #validate or #submit
// handlers to the form buttons below. Remove hook_form() entirely.
$function = node_type_get_base($node) . '_form';
if (function_exists($function) && ($extra = $function($node, $form_state))) {
$form = array_merge_recursive($form, $extra);
}
// If the node type has a title, and the node type form defined no special
// weight for it, we default to a weight of -5 for consistency.
if (isset($form['title']) && !isset($form['title']['#weight'])) {
$form['title']['#weight'] = -5;
}
// @todo D8: Remove. Modules should access the node using $form_state['node'].
$form['#node'] = $node;
$form['additional_settings'] = array(
'#type' => 'vertical_tabs',
'#weight' => 99,
);
// Add a log field if the "Create new revision" option is checked, or if the
// current user has the ability to check that option.
$form['revision_information'] = array(
'#type' => 'fieldset',
'#title' => t('Revision information'),
'#collapsible' => TRUE,
// Collapsed by default when "Create new revision" is unchecked
'#collapsed' => !$node->revision,
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array('node-form-revision-information'),
),
'#attached' => array(
'js' => array(drupal_get_path('module', 'node') . '/node.js'),
),
'#weight' => 20,
'#access' => $node->revision || user_access('administer nodes'),
);
$form['revision_information']['revision'] = array(
'#type' => 'checkbox',
'#title' => t('Create new revision'),
'#default_value' => $node->revision,
'#access' => user_access('administer nodes'),
);
// Check the revision log checkbox when the log textarea is filled in.
// This must not happen if "Create new revision" is enabled by default, since
// the state would auto-disable the checkbox otherwise.
if (!$node->revision) {
$form['revision_information']['revision']['#states'] = array(
'checked' => array(
'textarea[name="log"]' => array('empty' => FALSE),
),
);
}
$form['revision_information']['log'] = array(
'#type' => 'textarea',
'#title' => t('Revision log message'),
'#rows' => 4,
'#default_value' => !empty($node->log) ? $node->log : '',
'#description' => t('Provide an explanation of the changes you are making. This will help other authors understand your motivations.'),
);
// Node author information for administrators
$form['author'] = array(
'#type' => 'fieldset',
'#access' => user_access('administer nodes'),
'#title' => t('Authoring information'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array('node-form-author'),
),
'#attached' => array(
'js' => array(
drupal_get_path('module', 'node') . '/node.js',
array(
'type' => 'setting',
'data' => array('anonymous' => variable_get('anonymous', t('Anonymous'))),
),
),
),
'#weight' => 90,
);
$form['author']['name'] = array(
'#type' => 'textfield',
'#title' => t('Authored by'),
'#maxlength' => 60,
'#autocomplete_path' => 'user/autocomplete',
'#default_value' => !empty($node->name) ? $node->name : '',
'#weight' => -1,
'#description' => t('Leave blank for %anonymous.', array('%anonymous' => variable_get('anonymous', t('Anonymous')))),
);
$form['author']['date'] = array(
'#type' => 'textfield',
'#title' => t('Authored on'),
'#maxlength' => 25,
'#description' => t('Format: %time. The date format is YYYY-MM-DD and %timezone is the time zone offset from UTC. Leave blank to use the time of form submission.', array('%time' => !empty($node->date) ? date_format(date_create($node->date), 'Y-m-d H:i:s O') : format_date($node->created, 'custom', 'Y-m-d H:i:s O'), '%timezone' => !empty($node->date) ? date_format(date_create($node->date), 'O') : format_date($node->created, 'custom', 'O'))),
'#default_value' => !empty($node->date) ? $node->date : '',
);
// Node options for administrators
$form['options'] = array(
'#type' => 'fieldset',
'#access' => user_access('administer nodes'),
'#title' => t('Publishing options'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'additional_settings',
'#attributes' => array(
'class' => array('node-form-options'),
),
'#attached' => array(
'js' => array(drupal_get_path('module', 'node') . '/node.js'),
),
'#weight' => 95,
);
$form['options']['status'] = array(
'#type' => 'checkbox',
'#title' => t('Published'),
'#default_value' => $node->status,
);
$form['options']['promote'] = array(
'#type' => 'checkbox',
'#title' => t('Promoted to front page'),
'#default_value' => $node->promote,
);
$form['options']['sticky'] = array(
'#type' => 'checkbox',
'#title' => t('Sticky at top of lists'),
'#default_value' => $node->sticky,
);
// Add the buttons.
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_REQUIRED || (!form_get_errors() && isset($form_state['node_preview'])),
'#value' => t('Save'),
'#weight' => 5,
'#submit' => array('node_form_submit'),
);
$form['actions']['preview'] = array(
'#access' => variable_get('node_preview_' . $node->type, DRUPAL_OPTIONAL) != DRUPAL_DISABLED,
'#type' => 'submit',
'#value' => t('Preview'),
'#weight' => 10,
'#submit' => array('node_form_build_preview'),
);
if (!empty($node->nid) && node_access('delete', $node)) {
$form['actions']['delete'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#weight' => 15,
'#submit' => array('node_form_delete_submit'),
);
}
// This form uses a button-level #submit handler for the form's main submit
// action. node_form_submit() manually invokes all form-level #submit handlers
// of the form. Without explicitly setting #submit, Form API would auto-detect
// node_form_submit() as submit handler, but that is the button-level #submit
// handler for the 'Save' action. To maintain backwards compatibility, a
// #submit handler is auto-suggested for custom node type modules.
$form['#validate'][] = 'node_form_validate';
if (!isset($form['#submit']) && function_exists($node->type . '_node_form_submit')) {
$form['#submit'][] = $node->type . '_node_form_submit';
}
$form += array('#submit' => array());
field_attach_form('node', $node, $form, $form_state, entity_language('node', $node));
return $form;
}
/**
* Form submission handler for node_form().
*
* Handles the 'Delete' button on the node form.
*
* @see node_form()
* @see node_form_validate()
*/
function node_form_delete_submit($form, &$form_state) {
$destination = array();
if (isset($_GET['destination'])) {
$destination = drupal_get_destination();
unset($_GET['destination']);
}
$node = $form['#node'];
$form_state['redirect'] = array('node/' . $node->nid . '/delete', array('query' => $destination));
}
/**
* Form submission handler for node_form().
*
* Handles the 'Preview' button on the node form.
*
* @see node_form()
* @see node_form_validate()
*/
function node_form_build_preview($form, &$form_state) {
$node = node_form_submit_build_node($form, $form_state);
$form_state['node_preview'] = node_preview($node);
$form_state['rebuild'] = TRUE;
}
/**
* Generates a node preview.
*
* @param $node
* The node to preview.
*
* @return
* An HTML-formatted string of a node preview.
*
* @see node_form_build_preview()
*/
function node_preview($node) {
// Clone the node before previewing it to prevent the node itself from being
// modified.
$cloned_node = clone $node;
if (node_access('create', $cloned_node) || node_access('update', $cloned_node)) {
_field_invoke_multiple('load', 'node', array($cloned_node->nid => $cloned_node));
// Load the user's name when needed.
if (isset($cloned_node->name)) {
// The use of isset() is mandatory in the context of user IDs, because
// user ID 0 denotes the anonymous user.
if ($user = user_load_by_name($cloned_node->name)) {
$cloned_node->uid = $user->uid;
$cloned_node->picture = $user->picture;
}
else {
$cloned_node->uid = 0; // anonymous user
}
}
elseif ($cloned_node->uid) {
$user = user_load($cloned_node->uid);
$cloned_node->name = $user->name;
$cloned_node->picture = $user->picture;
}
$cloned_node->changed = REQUEST_TIME;
$nodes = array($cloned_node->nid => $cloned_node);
// Display a preview of the node.
if (!form_get_errors()) {
$cloned_node->in_preview = TRUE;
$output = theme('node_preview', array('node' => $cloned_node));
unset($cloned_node->in_preview);
}
drupal_set_title(t('Preview'), PASS_THROUGH);
return $output;
}
}
/**
* Returns HTML for a node preview for display during node creation and editing.
*
* @param $variables
* An associative array containing:
* - node: The node object which is being previewed.
*
* @see node_preview()
* @ingroup themeable
*/
function theme_node_preview($variables) {
$node = $variables['node'];
$output = '<div class="preview">';
$preview_trimmed_version = FALSE;
$elements = node_view(clone $node, 'teaser');
$trimmed = drupal_render($elements);
$elements = node_view($node, 'full');
$full = drupal_render($elements);
// Do we need to preview trimmed version of post as well as full version?
if ($trimmed != $full) {
drupal_set_message(t('The trimmed version of your post shows what your post looks like when promoted to the main page or when exported for syndication.<span class="no-js"> You can insert the delimiter "&lt;!--break--&gt;" (without the quotes) to fine-tune where your post gets split.</span>'));
$output .= '<h3>' . t('Preview trimmed version') . '</h3>';
$output .= $trimmed;
$output .= '<h3>' . t('Preview full version') . '</h3>';
$output .= $full;
}
else {
$output .= $full;
}
$output .= "</div>\n";
return $output;
}
/**
* Form submission handler for node_form().
*
* @see node_form()
* @see node_form_validate()
*/
function node_form_submit($form, &$form_state) {
$node = node_form_submit_build_node($form, $form_state);
$insert = empty($node->nid);
node_save($node);
$node_link = l(t('view'), 'node/' . $node->nid);
$watchdog_args = array('@type' => $node->type, '%title' => $node->title);
$t_args = array('@type' => node_type_get_name($node), '%title' => $node->title);
if ($insert) {
watchdog('content', '@type: added %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
drupal_set_message(t('@type %title has been created.', $t_args));
}
else {
watchdog('content', '@type: updated %title.', $watchdog_args, WATCHDOG_NOTICE, $node_link);
drupal_set_message(t('@type %title has been updated.', $t_args));
}
if ($node->nid) {
$form_state['values']['nid'] = $node->nid;
$form_state['nid'] = $node->nid;
$form_state['redirect'] = node_access('view', $node) ? 'node/' . $node->nid : '<front>';
}
else {
// In the unlikely case something went wrong on save, the node will be
// rebuilt and node form redisplayed the same way as in preview.
drupal_set_message(t('The post could not be saved.'), 'error');
$form_state['rebuild'] = TRUE;
}
// Clear the page and block caches.
cache_clear_all();
}
/**
* Updates the form state's node entity by processing this submission's values.
*
* This is the default builder function for the node form. It is called
* during the "Save" and "Preview" submit handlers to retrieve the entity to
* save or preview. This function can also be called by a "Next" button of a
* wizard to update the form state's entity with the current step's values
* before proceeding to the next step.
*
* @see node_form()
*/
function node_form_submit_build_node($form, &$form_state) {
// @todo Legacy support for modules that extend the node form with form-level
// submit handlers that adjust $form_state['values'] prior to those values
// being used to update the entity. Module authors are encouraged to instead
// adjust the node directly within a hook_node_submit() implementation. For
// Drupal 8, evaluate whether the pattern of triggering form-level submit
// handlers during button-level submit processing is worth supporting
// properly, and if so, add a Form API function for doing so.
unset($form_state['submit_handlers']);
form_execute_handlers('submit', $form, $form_state);
$node = $form_state['node'];
entity_form_submit_build_entity('node', $node, $form, $form_state);
node_submit($node);
foreach (module_implements('node_submit') as $module) {
$function = $module . '_node_submit';
$function($node, $form, $form_state);
}
return $node;
}
/**
* Form constructor for the node deletion confirmation form.
*
* @see node_delete_confirm_submit()
*/
function node_delete_confirm($form, &$form_state, $node) {
$form['#node'] = $node;
// Always provide entity id in the same form key as in the entity edit form.
$form['nid'] = array('#type' => 'value', '#value' => $node->nid);
return confirm_form($form,
t('Are you sure you want to delete %title?', array('%title' => $node->title)),
'node/' . $node->nid,
t('This action cannot be undone.'),
t('Delete'),
t('Cancel')
);
}
/**
* Executes node deletion.
*
* @see node_delete_confirm()
*/
function node_delete_confirm_submit($form, &$form_state) {
if ($form_state['values']['confirm']) {
$node = node_load($form_state['values']['nid']);
node_delete($form_state['values']['nid']);
cache_clear_all();
watchdog('content', '@type: deleted %title.', array('@type' => $node->type, '%title' => $node->title));
drupal_set_message(t('@type %title has been deleted.', array('@type' => node_type_get_name($node), '%title' => $node->title)));
}
$form_state['redirect'] = '<front>';
}
/**
* Generates an overview table of older revisions of a node.
*
* @param $node
* A node object.
*
* @return array
* An array as expected by drupal_render().
*
* @see node_menu()
*/
function node_revision_overview($node) {
drupal_set_title(t('Revisions for %title', array('%title' => $node->title)), PASS_THROUGH);
$header = array(t('Revision'), array('data' => t('Operations'), 'colspan' => 2));
$revisions = node_revision_list($node);
$rows = array();
$revert_permission = FALSE;
if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
$revert_permission = TRUE;
}
$delete_permission = FALSE;
if ((user_access('delete revisions') || user_access('administer nodes')) && node_access('delete', $node)) {
$delete_permission = TRUE;
}
foreach ($revisions as $revision) {
$row = array();
$operations = array();
if ($revision->current_vid > 0) {
$row[] = array('data' => t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid"), '!username' => theme('username', array('account' => $revision))))
. (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : ''),
'class' => array('revision-current'));
$operations[] = array('data' => drupal_placeholder(t('current revision')), 'class' => array('revision-current'), 'colspan' => 2);
}
else {
$row[] = t('!date by !username', array('!date' => l(format_date($revision->timestamp, 'short'), "node/$node->nid/revisions/$revision->vid/view"), '!username' => theme('username', array('account' => $revision))))
. (($revision->log != '') ? '<p class="revision-log">' . filter_xss($revision->log) . '</p>' : '');
if ($revert_permission) {
$operations[] = l(t('revert'), "node/$node->nid/revisions/$revision->vid/revert");
}
if ($delete_permission) {
$operations[] = l(t('delete'), "node/$node->nid/revisions/$revision->vid/delete");
}
}
$rows[] = array_merge($row, $operations);
}
$build['node_revisions_table'] = array(
'#theme' => 'table',
'#rows' => $rows,
'#header' => $header,
);
return $build;
}
/**
* Asks for confirmation of the reversion to prevent against CSRF attacks.
*
* @param int $node_revision
* The node revision ID.
*
* @return array
* An array as expected by drupal_render().
*
* @see node_menu()
* @see node_revision_revert_confirm_submit()
* @ingroup forms
*/
function node_revision_revert_confirm($form, $form_state, $node_revision) {
$form['#node_revision'] = $node_revision;
return confirm_form($form, t('Are you sure you want to revert to the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', '', t('Revert'), t('Cancel'));
}
/**
* Form submission handler for node_revision_revert_confirm().
*/
function node_revision_revert_confirm_submit($form, &$form_state) {
$node_revision = $form['#node_revision'];
$node_revision->revision = 1;
$node_revision->log = t('Copy of the revision from %date.', array('%date' => format_date($node_revision->revision_timestamp)));
node_save($node_revision);
watchdog('content', '@type: reverted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->title, '%revision' => $node_revision->vid));
drupal_set_message(t('@type %title has been reverted back to the revision from %revision-date.', array('@type' => node_type_get_name($node_revision), '%title' => $node_revision->title, '%revision-date' => format_date($node_revision->revision_timestamp))));
$form_state['redirect'] = 'node/' . $node_revision->nid . '/revisions';
}
/**
* Form constructor for the revision deletion confirmation form.
*
* This form prevents against CSRF attacks.
*
* @param $node_revision
* The node revision ID.
*
* @return
* An array as expected by drupal_render().
*
* @see node_menu()
* @see node_revision_delete_confirm_submit()
* @ingroup forms
*/
function node_revision_delete_confirm($form, $form_state, $node_revision) {
$form['#node_revision'] = $node_revision;
return confirm_form($form, t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($node_revision->revision_timestamp))), 'node/' . $node_revision->nid . '/revisions', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}
/**
* Form submission handler for node_revision_delete_confirm().
*/
function node_revision_delete_confirm_submit($form, &$form_state) {
$node_revision = $form['#node_revision'];
node_revision_delete($node_revision->vid);
watchdog('content', '@type: deleted %title revision %revision.', array('@type' => $node_revision->type, '%title' => $node_revision->title, '%revision' => $node_revision->vid));
drupal_set_message(t('Revision from %revision-date of @type %title has been deleted.', array('%revision-date' => format_date($node_revision->revision_timestamp), '@type' => node_type_get_name($node_revision), '%title' => $node_revision->title)));
$form_state['redirect'] = 'node/' . $node_revision->nid;
if (db_query('SELECT COUNT(vid) FROM {node_revision} WHERE nid = :nid', array(':nid' => $node_revision->nid))->fetchField() > 1) {
$form_state['redirect'] .= '/revisions';
}
}

3021
modules/node/node.test Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,210 @@
<?php
/**
* @file
* Builds placeholder replacement tokens for node-related data.
*/
/**
* Implements hook_token_info().
*/
function node_token_info() {
$type = array(
'name' => t('Nodes'),
'description' => t('Tokens related to individual content items, or "nodes".'),
'needs-data' => 'node',
);
// Core tokens for nodes.
$node['nid'] = array(
'name' => t("Content ID"),
'description' => t('The unique ID of the content item, or "node".'),
);
$node['vid'] = array(
'name' => t("Revision ID"),
'description' => t("The unique ID of the node's latest revision."),
);
$node['tnid'] = array(
'name' => t("Translation set ID"),
'description' => t("The unique ID of the original-language version of this node, if one exists."),
);
$node['type'] = array(
'name' => t("Content type"),
'description' => t("The type of the node."),
);
$node['type-name'] = array(
'name' => t("Content type name"),
'description' => t("The human-readable name of the node type."),
);
$node['title'] = array(
'name' => t("Title"),
'description' => t("The title of the node."),
);
$node['body'] = array(
'name' => t("Body"),
'description' => t("The main body text of the node."),
);
$node['summary'] = array(
'name' => t("Summary"),
'description' => t("The summary of the node's main body text."),
);
$node['language'] = array(
'name' => t("Language"),
'description' => t("The language the node is written in."),
);
$node['url'] = array(
'name' => t("URL"),
'description' => t("The URL of the node."),
);
$node['edit-url'] = array(
'name' => t("Edit URL"),
'description' => t("The URL of the node's edit page."),
);
// Chained tokens for nodes.
$node['created'] = array(
'name' => t("Date created"),
'description' => t("The date the node was posted."),
'type' => 'date',
);
$node['changed'] = array(
'name' => t("Date changed"),
'description' => t("The date the node was most recently updated."),
'type' => 'date',
);
$node['author'] = array(
'name' => t("Author"),
'description' => t("The author of the node."),
'type' => 'user',
);
return array(
'types' => array('node' => $type),
'tokens' => array('node' => $node),
);
}
/**
* Implements hook_tokens().
*/
function node_tokens($type, $tokens, array $data = array(), array $options = array()) {
$url_options = array('absolute' => TRUE);
if (isset($options['language'])) {
$url_options['language'] = $options['language'];
$language_code = $options['language']->language;
}
else {
$language_code = NULL;
}
$sanitize = !empty($options['sanitize']);
$replacements = array();
if ($type == 'node' && !empty($data['node'])) {
$node = $data['node'];
foreach ($tokens as $name => $original) {
switch ($name) {
// Simple key values on the node.
case 'nid':
$replacements[$original] = $node->nid;
break;
case 'vid':
$replacements[$original] = $node->vid;
break;
case 'tnid':
$replacements[$original] = $node->tnid;
break;
case 'type':
$replacements[$original] = $sanitize ? check_plain($node->type) : $node->type;
break;
case 'type-name':
$type_name = node_type_get_name($node);
$replacements[$original] = $sanitize ? check_plain($type_name) : $type_name;
break;
case 'title':
$replacements[$original] = $sanitize ? check_plain($node->title) : $node->title;
break;
case 'body':
case 'summary':
if ($items = field_get_items('node', $node, 'body', $language_code)) {
$instance = field_info_instance('node', 'body', $node->type);
$field_langcode = field_language('node', $node, 'body', $language_code);
// If the summary was requested and is not empty, use it.
if ($name == 'summary' && !empty($items[0]['summary'])) {
$output = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], 'summary') : $items[0]['summary'];
}
// Attempt to provide a suitable version of the 'body' field.
else {
$output = $sanitize ? _text_sanitize($instance, $field_langcode, $items[0], 'value') : $items[0]['value'];
// A summary was requested.
if ($name == 'summary') {
if (isset($instance['display']['teaser']['settings']['trim_length'])) {
$trim_length = $instance['display']['teaser']['settings']['trim_length'];
}
else {
// Use default value.
$trim_length = NULL;
}
// Generate an optionally trimmed summary of the body field.
$output = text_summary($output, $instance['settings']['text_processing'] ? $items[0]['format'] : NULL, $trim_length);
}
}
$replacements[$original] = $output;
}
break;
case 'language':
$langcode = entity_language('node', $node);
$replacements[$original] = $sanitize ? check_plain($langcode) : $langcode;
break;
case 'url':
$replacements[$original] = url('node/' . $node->nid, $url_options);
break;
case 'edit-url':
$replacements[$original] = url('node/' . $node->nid . '/edit', $url_options);
break;
// Default values for the chained tokens handled below.
case 'author':
$account = user_load($node->uid);
$name = format_username($account);
$replacements[$original] = $sanitize ? check_plain($name) : $name;
break;
case 'created':
$replacements[$original] = format_date($node->created, 'medium', '', NULL, $language_code);
break;
case 'changed':
$replacements[$original] = format_date($node->changed, 'medium', '', NULL, $language_code);
break;
}
}
if ($author_tokens = token_find_with_prefix($tokens, 'author')) {
$author = user_load($node->uid);
$replacements += token_generate('user', $author_tokens, array('user' => $author), $options);
}
if ($created_tokens = token_find_with_prefix($tokens, 'created')) {
$replacements += token_generate('date', $created_tokens, array('date' => $node->created), $options);
}
if ($changed_tokens = token_find_with_prefix($tokens, 'changed')) {
$replacements += token_generate('date', $changed_tokens, array('date' => $node->changed), $options);
}
}
return $replacements;
}

112
modules/node/node.tpl.php Normal file
View file

@ -0,0 +1,112 @@
<?php
/**
* @file
* Default theme implementation to display a node.
*
* Available variables:
* - $title: the (sanitized) title of the node.
* - $content: An array of node items. Use render($content) to print them all,
* or print a subset such as render($content['field_example']). Use
* hide($content['field_example']) to temporarily suppress the printing of a
* given element.
* - $user_picture: The node author's picture from user-picture.tpl.php.
* - $date: Formatted creation date. Preprocess functions can reformat it by
* calling format_date() with the desired parameters on the $created variable.
* - $name: Themed username of node author output from theme_username().
* - $node_url: Direct URL of the current node.
* - $display_submitted: Whether submission information should be displayed.
* - $submitted: Submission information created from $name and $date during
* template_preprocess_node().
* - $classes: String of classes that can be used to style contextually through
* CSS. It can be manipulated through the variable $classes_array from
* preprocess functions. The default values can be one or more of the
* following:
* - node: The current template type; for example, "theming hook".
* - node-[type]: The current node type. For example, if the node is a
* "Blog entry" it would result in "node-blog". Note that the machine
* name will often be in a short form of the human readable label.
* - node-teaser: Nodes in teaser form.
* - node-preview: Nodes in preview mode.
* The following are controlled through the node publishing options.
* - node-promoted: Nodes promoted to the front page.
* - node-sticky: Nodes ordered above other non-sticky nodes in teaser
* listings.
* - node-unpublished: Unpublished nodes visible only to administrators.
* - $title_prefix (array): An array containing additional output populated by
* modules, intended to be displayed in front of the main title tag that
* appears in the template.
* - $title_suffix (array): An array containing additional output populated by
* modules, intended to be displayed after the main title tag that appears in
* the template.
*
* Other variables:
* - $node: Full node object. Contains data that may not be safe.
* - $type: Node type; for example, story, page, blog, etc.
* - $comment_count: Number of comments attached to the node.
* - $uid: User ID of the node author.
* - $created: Time the node was published formatted in Unix timestamp.
* - $classes_array: Array of html class attribute values. It is flattened
* into a string within the variable $classes.
* - $zebra: Outputs either "even" or "odd". Useful for zebra striping in
* teaser listings.
* - $id: Position of the node. Increments each time it's output.
*
* Node status variables:
* - $view_mode: View mode; for example, "full", "teaser".
* - $teaser: Flag for the teaser state (shortcut for $view_mode == 'teaser').
* - $page: Flag for the full page state.
* - $promote: Flag for front page promotion state.
* - $sticky: Flags for sticky post setting.
* - $status: Flag for published status.
* - $comment: State of comment settings for the node.
* - $readmore: Flags true if the teaser content of the node cannot hold the
* main body content.
* - $is_front: Flags true when presented in the front page.
* - $logged_in: Flags true when the current user is a logged-in member.
* - $is_admin: Flags true when the current user is an administrator.
*
* Field variables: for each field instance attached to the node a corresponding
* variable is defined; for example, $node->body becomes $body. When needing to
* access a field's raw values, developers/themers are strongly encouraged to
* use these variables. Otherwise they will have to explicitly specify the
* desired field language; for example, $node->body['en'], thus overriding any
* language negotiation rule that was previously applied.
*
* @see template_preprocess()
* @see template_preprocess_node()
* @see template_process()
*
* @ingroup themeable
*/
?>
<div id="node-<?php print $node->nid; ?>" class="<?php print $classes; ?> clearfix"<?php print $attributes; ?>>
<?php print $user_picture; ?>
<?php print render($title_prefix); ?>
<?php if (!$page): ?>
<h2<?php print $title_attributes; ?>><a href="<?php print $node_url; ?>"><?php print $title; ?></a></h2>
<?php endif; ?>
<?php print render($title_suffix); ?>
<?php if ($display_submitted): ?>
<div class="submitted">
<?php print $submitted; ?>
</div>
<?php endif; ?>
<div class="content"<?php print $content_attributes; ?>>
<?php
// We hide the comments and links now so that we can render them later.
hide($content['comments']);
hide($content['links']);
print render($content);
?>
</div>
<?php print render($content['links']); ?>
<?php print render($content['comments']); ?>
</div>

View file

@ -0,0 +1,12 @@
name = "Node module access tests"
description = "Support module for node permission testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,42 @@
<?php
/**
* @file
* Install, update and uninstall functions for the node_access_test module.
*/
/**
* Implements hook_schema().
*/
function node_access_test_schema() {
$schema['node_access_test'] = array(
'description' => 'The base table for node_access_test.',
'fields' => array(
'nid' => array(
'description' => 'The {node}.nid this record affects.',
'type' => 'int',
'unsigned' => TRUE,
'not null' => TRUE,
'default' => 0,
),
'private' => array(
'description' => 'Boolean indicating whether the node is private (visible to administrator) or not (visible to non-administrators).',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'indexes' => array(
'nid' => array('nid'),
),
'primary key' => array('nid'),
'foreign keys' => array(
'versioned_node' => array(
'table' => 'node',
'columns' => array('nid' => 'nid'),
),
),
);
return $schema;
}

View file

@ -0,0 +1,230 @@
<?php
/**
* @file
* A dummy module implementing node access related hooks for testing purposes.
*
* A dummy module implementing node access related hooks to test API interaction
* with the Node module. This module restricts view permission to those with
* a special 'node test view' permission.
*/
/**
* Implements hook_node_grants().
*/
function node_access_test_node_grants($account, $op) {
$grants = array();
// First grant a grant to the author for own content.
$grants['node_access_test_author'] = array($account->uid);
if ($op == 'view' && user_access('node test view', $account)) {
$grants['node_access_test'] = array(8888, 8889);
}
if ($op == 'view' && $account->uid == variable_get('node_test_node_access_all_uid', 0)) {
$grants['node_access_all'] = array(0);
}
return $grants;
}
/**
* Implements hook_node_access_records().
*/
function node_access_test_node_access_records($node) {
$grants = array();
// For NodeAccessBaseTableTestCase, only set records for private nodes.
if (!variable_get('node_access_test_private') || $node->private) {
$grants[] = array(
'realm' => 'node_access_test',
'gid' => 8888,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
$grants[] = array(
'realm' => 'node_access_test',
'gid' => 8889,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
// For the author realm, the GID is equivalent to a UID, which
// means there are many many groups of just 1 user.
$grants[] = array(
'realm' => 'node_access_test_author',
'gid' => $node->uid,
'grant_view' => 1,
'grant_update' => 1,
'grant_delete' => 1,
'priority' => 0,
);
}
return $grants;
}
/**
* Implements hook_permission().
*
* Sets up permissions for this module.
*/
function node_access_test_permission() {
return array('node test view' => array('title' => 'View content'));
}
/**
* Implements hook_menu().
*
* Sets up a page that lists nodes.
*/
function node_access_test_menu() {
$items = array();
$items['node_access_test_page'] = array(
'title' => 'Node access test',
'page callback' => 'node_access_test_page',
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
);
$items['node_access_entity_test_page'] = array(
'title' => 'Node access test',
'page callback' => 'node_access_entity_test_page',
'access arguments' => array('access content'),
'type' => MENU_SUGGESTED_ITEM,
);
return $items;
}
/**
* Page callback for node access test page.
*
* Page should say "No nodes" if there are no nodes, and "Yes, # nodes" (with
* the number filled in) if there were nodes the user could access. Also, the
* database query is shown, and a list of the node IDs, for debugging purposes.
* And if there is a query exception, the page says "Exception" and gives the
* error.
*/
function node_access_test_page() {
$output = '';
try {
$query = db_select('node', 'mytab')
->fields('mytab');
$query->addTag('node_access');
$result = $query->execute()->fetchAll();
if (count($result)) {
$output .= '<p>Yes, ' . count($result) . ' nodes</p>';
$output .= '<ul>';
foreach ($result as $item) {
$output .= '<li>' . $item->nid . '</li>';
}
$output .= '</ul>';
}
else {
$output .= '<p>No nodes</p>';
}
$output .= '<p>' . ((string) $query ) . '</p>';
}
catch (Exception $e) {
$output = '<p>Exception</p>';
$output .= '<p>' . $e->getMessage() . '</p>';
}
return $output;
}
/**
* Page callback for node access entity test page.
*
* Page should say "No nodes" if there are no nodes, and "Yes, # nodes" (with
* the number filled in) if there were nodes the user could access. Also, the
* database query is shown, and a list of the node IDs, for debugging purposes.
* And if there is a query exception, the page says "Exception" and gives the
* error.
*
* @see node_access_test_menu()
*/
function node_access_entity_test_page() {
$output = '';
try {
$query = new EntityFieldQuery;
$result = $query->fieldCondition('body', 'value', 'A', 'STARTS_WITH')->execute();
if (!empty($result['node'])) {
$output .= '<p>Yes, ' . count($result['node']) . ' nodes</p>';
$output .= '<ul>';
foreach ($result['node'] as $nid => $v) {
$output .= '<li>' . $nid . '</li>';
}
$output .= '</ul>';
}
else {
$output .= '<p>No nodes</p>';
}
}
catch (Exception $e) {
$output = '<p>Exception</p>';
$output .= '<p>' . $e->getMessage() . '</p>';
}
return $output;
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*/
function node_access_test_form_node_form_alter(&$form, $form_state) {
// Only show this checkbox for NodeAccessBaseTableTestCase.
if (variable_get('node_access_test_private')) {
$form['private'] = array(
'#type' => 'checkbox',
'#title' => t('Private'),
'#description' => t('Check here if this content should be set private and only shown to privileged users.'),
'#default_value' => isset($form['#node']->private) ? $form['#node']->private : FALSE,
);
}
}
/**
* Implements hook_node_load().
*/
function node_access_test_node_load($nodes, $types) {
$result = db_query('SELECT nid, private FROM {node_access_test} WHERE nid IN(:nids)', array(':nids' => array_keys($nodes)));
foreach ($result as $record) {
$nodes[$record->nid]->private = $record->private;
}
}
/**
* Implements hook_node_delete().
*/
function node_access_test_node_delete($node) {
db_delete('node_access_test')->condition('nid', $node->nid)->execute();
}
/**
* Implements hook_node_insert().
*/
function node_access_test_node_insert($node) {
_node_access_test_node_write($node);
}
/**
* Implements hook_node_update().
*/
function node_access_test_node_update($node) {
_node_access_test_node_write($node);
}
/**
* Helper for node insert/update.
*/
function _node_access_test_node_write($node) {
if (isset($node->private)) {
db_merge('node_access_test')
->key(array('nid' => $node->nid))
->fields(array('private' => (int) $node->private))
->execute();
}
}

View file

@ -0,0 +1,12 @@
name = "Node module tests"
description = "Support module for node related testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,181 @@
<?php
/**
* @file
* A dummy module for testing node related hooks.
*
* This is a dummy module that implements node related hooks to test API
* interaction with the Node module.
*/
/**
* Implements hook_node_load().
*/
function node_test_node_load($nodes, $types) {
// Add properties to each loaded node which record the parameters that were
// passed in to this function, so the tests can check that (a) this hook was
// called, and (b) the parameters were what we expected them to be.
$nids = array_keys($nodes);
ksort($nids);
sort($types);
foreach ($nodes as $node) {
$node->node_test_loaded_nids = $nids;
$node->node_test_loaded_types = $types;
}
}
/**
* Implements hook_node_view().
*/
function node_test_node_view($node, $view_mode) {
if ($view_mode == 'rss') {
// Add RSS elements and namespaces when building the RSS feed.
$node->rss_elements[] = array(
'key' => 'testElement',
'value' => t('Value of testElement RSS element for node !nid.', array('!nid' => $node->nid)),
);
$node->rss_namespaces['xmlns:drupaltest'] = 'http://example.com/test-namespace';
// Add content that should be displayed only in the RSS feed.
$node->content['extra_feed_content'] = array(
'#markup' => '<p>' . t('Extra data that should appear only in the RSS feed for node !nid.', array('!nid' => $node->nid)) . '</p>',
'#weight' => 10,
);
}
if ($view_mode != 'rss') {
// Add content that should NOT be displayed in the RSS feed.
$node->content['extra_non_feed_content'] = array(
'#markup' => '<p>' . t('Extra data that should appear everywhere except the RSS feed for node !nid.', array('!nid' => $node->nid)) . '</p>',
);
}
}
/**
* Implements hook_node_grants().
*/
function node_test_node_grants($account, $op) {
// Give everyone full grants so we don't break other node tests.
// Our node access tests asserts three realms of access.
// See testGrantAlter().
return array(
'test_article_realm' => array(1),
'test_page_realm' => array(1),
'test_alter_realm' => array(2),
);
}
/**
* Implements hook_node_access_records().
*/
function node_test_node_access_records($node) {
// Return nothing when testing for empty responses.
if (!empty($node->disable_node_access)) {
return;
}
$grants = array();
if ($node->type == 'article') {
// Create grant in arbitrary article_realm for article nodes.
$grants[] = array(
'realm' => 'test_article_realm',
'gid' => 1,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
}
elseif ($node->type == 'page') {
// Create grant in arbitrary page_realm for page nodes.
$grants[] = array(
'realm' => 'test_page_realm',
'gid' => 1,
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
'priority' => 0,
);
}
return $grants;
}
/**
* Implements hook_node_access_records_alter().
*/
function node_test_node_access_records_alter(&$grants, $node) {
if (!empty($grants)) {
foreach ($grants as $key => $grant) {
// Alter grant from test_page_realm to test_alter_realm and modify the gid.
if ($grant['realm'] == 'test_page_realm' && $node->promote) {
$grants[$key]['realm'] = 'test_alter_realm';
$grants[$key]['gid'] = 2;
}
}
}
}
/**
* Implements hook_node_grants_alter().
*/
function node_test_node_grants_alter(&$grants, $account, $op) {
// Return an empty array of grants to prove that we can alter by reference.
$grants = array();
}
/**
* Implements hook_node_presave().
*/
function node_test_node_presave($node) {
if ($node->title == 'testing_node_presave') {
// Sun, 19 Nov 1978 05:00:00 GMT
$node->created = 280299600;
// Drupal 1.0 release.
$node->changed = 979534800;
}
// Determine changes.
if (!empty($node->original) && $node->original->title == 'test_changes') {
if ($node->original->title != $node->title) {
$node->title .= '_presave';
}
}
}
/**
* Implements hook_node_update().
*/
function node_test_node_update($node) {
// Determine changes on update.
if (!empty($node->original) && $node->original->title == 'test_changes') {
if ($node->original->title != $node->title) {
$node->title .= '_update';
}
}
}
/**
* Implements hook_entity_view_mode_alter().
*/
function node_test_entity_view_mode_alter(&$view_mode, $context) {
// Only alter the view mode if we are on the test callback.
if ($change_view_mode = variable_get('node_test_change_view_mode', '')) {
$view_mode = $change_view_mode;
}
}
/**
* Implements hook_node_insert().
*
* This tests saving a node on node insert.
*
* @see NodeSaveTest::testNodeSaveOnInsert()
*/
function node_test_node_insert($node) {
// Set the node title to the node ID and save.
if ($node->title == 'new') {
$node->title = 'Node '. $node->nid;
// Remove the is_new flag, so that the node is updated and not inserted
// again.
unset($node->is_new);
node_save($node);
}
}

View file

@ -0,0 +1,12 @@
name = "Node module exception tests"
description = "Support module for node related exception testing."
package = Testing
version = VERSION
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2017-06-21
version = "7.56"
project = "drupal"
datestamp = "1498069849"

View file

@ -0,0 +1,15 @@
<?php
/**
* @file
* A module implementing node related hooks to test API interaction.
*/
/**
* Implements hook_node_insert().
*/
function node_test_exception_node_insert($node) {
if ($node->title == 'testing_transaction_exception') {
throw new Exception('Test exception for rollback.');
}
}