hasPermission('access administration pages') && node_access_needs_rebuild()) { if ($route_name == 'system.status') { $message = t('The content access permissions need to be rebuilt.'); } else { $message = t('The content access permissions need to be rebuilt. Rebuild permissions.', array('!node_access_rebuild' => \Drupal::url('node.configure_rebuild_confirm'))); } drupal_set_message($message, 'error'); } switch ($route_name) { case 'help.page.node': $output = ''; $output .= '
' . t('The Node module manages the creation, editing, deletion, settings, and display of the main site content. Content items managed by the Node module are typically displayed as pages on your site, and include a title, some meta-data (author, creation time, content type, etc.), and optional fields containing text or other data (fields are managed by the Field module). For more information, see the online documentation for the Node module.', array('!node' => 'https://drupal.org/documentation/modules/node', '!field' => \Drupal::url('help.page', array('name' => 'field')))) . '
'; $output .= '' . t('Individual content types can have different fields, behaviors, and permissions assigned to them.') . '
'; case 'field_ui.form_display_overview_node': case 'field_ui.form_display_overview_form_mode_node': $type = $route_match->getParameter('node_type'); return '' . t('Content items can be edited using different form modes. Here, you can define which fields are shown and hidden when %type content is edited in each form mode, and define how the field form widgets are displayed in each form mode.', array('%type' => $type->label())) . '
' ; case 'field_ui.display_overview_node': case 'field_ui.display_overview_view_mode_node': $type = $route_match->getParameter('node_type'); return '' . t('Content items can be displayed using different view modes: Teaser, Full content, Print, RSS, etc. Teaser is a short format that is typically used in lists of multiple content items. Full content is typically used when the content is displayed on its own page.') . '
' . '' . t('Here, you can define which fields are shown and hidden when %type content is displayed in each view mode, and define how the fields are displayed in each view mode.', array('%type' => $type->label())) . '
'; case 'entity.node.version_history': return '' . t('Revisions allow you to track differences between multiple versions of your content, and revert back to older versions.') . '
'; case 'entity.node.edit_form': $node = $route_match->getParameter('node'); $type = $node->getType(); return (!empty($type->help) ? Xss::filterAdmin($type->help) : ''); case 'node.add': $type = $route_match->getParameter('node_type'); return (!empty($type->help) ? Xss::filterAdmin($type->help) : ''); } } /** * Implements hook_theme(). */ function node_theme() { return array( 'node' => array( 'render element' => 'elements', 'template' => 'node', ), 'node_search_admin' => array( 'render element' => 'form', ), 'node_add_list' => array( 'variables' => array('content' => NULL), 'file' => 'node.pages.inc', 'template' => 'node-add-list', ), 'node_edit_form' => array( 'render element' => 'form', 'template' => 'node-edit-form', ), 'field__node__title' => array( 'base hook' => 'field', 'template' => 'field--node--title', ), 'field__node__uid' => array( 'base hook' => 'field', 'template' => 'field--node--uid', ), 'field__node__created' => array( 'base hook' => 'field', 'template' => 'field--node--created', ), ); } /** * Implements hook_entity_view_display_alter(). */ function node_entity_view_display_alter(EntityViewDisplayInterface $display, $context) { if ($context['entity_type'] == 'node') { // Hide field labels in search index. if ($context['view_mode'] == 'search_index') { foreach ($display->getComponents() as $name => $options) { if (isset($options['label'])) { $options['label'] = 'hidden'; $display->setComponent($name, $options); } } } } } /** * Gathers a listing of links to nodes. * * @param $result * A database result object from a query to fetch node entities. If your * query joins the {comment_entity_statistics} table so that the comment_count * field is available, a title attribute will be added to show the number of * comments. * @param $title * (optional) A heading for the resulting list. * * @return * A renderable array containing a list of linked node titles fetched from * $result, or FALSE if there are no rows in $result. */ function node_title_list($result, $title = NULL) { $items = array(); $num_rows = FALSE; foreach ($result as $node) { // Do not use $node->label() here, because $node comes from the database. $items[] = l($node->title, 'node/' . $node->nid, !empty($node->comment_count) ? array('attributes' => array('title' => format_plural($node->comment_count, '1 comment', '@count comments'))) : array()); $num_rows = TRUE; } return $num_rows ? array('#theme' => 'item_list__node', '#items' => $items, '#title' => $title) : FALSE; } /** * Determines the type of marker to be displayed for a given node. * * @param int $nid * Node ID whose history supplies the "last viewed" timestamp. * @param int $timestamp * Time which is compared against node's "last viewed" timestamp. * * @return int * One of the MARK constants. */ function node_mark($nid, $timestamp) { $cache = &drupal_static(__FUNCTION__, array()); if (\Drupal::currentUser()->isAnonymous() || !\Drupal::moduleHandler()->moduleExists('history')) { return MARK_READ; } if (!isset($cache[$nid])) { $cache[$nid] = history_read($nid); } if ($cache[$nid] == 0 && $timestamp > HISTORY_READ_LIMIT) { return MARK_NEW; } elseif ($timestamp > $cache[$nid] && $timestamp > HISTORY_READ_LIMIT) { return MARK_UPDATED; } return MARK_READ; } /** * Returns a list of all the available node types. * * This list can include types that are queued for addition or deletion. * * @return \Drupal\node\NodeTypeInterface[] * An array of node type entities, keyed by ID. * * @deprecated in Drupal 8.x, will be removed before Drupal 9.0. * Use \Drupal\node\Entity\NodeType::loadMultiple(). * * @see node_type_load() */ function node_type_get_types() { return NodeType::loadMultiple(); } /** * Returns a list of available node type names. * * This list can include types that are queued for addition or deletion. * * @return string[] * An array of node type labels, keyed by the node type name. */ function node_type_get_names() { return array_map(function ($bundle_info) { return $bundle_info['label']; }, \Drupal::entityManager()->getBundleInfo('node')); } /** * Returns the node type label for the passed node. * * @param \Drupal\node\NodeInterface $node * A node entity to return the node type's label for. * * @return string|false * The node type label or FALSE if the node type is not found. * * @todo Add this as generic helper method for config entities representing * entity bundles. */ function node_get_type_label(NodeInterface $node) { $type = entity_load('node_type', $node->bundle()); return $type ? $type->label() : FALSE; } /** * Description callback: Returns the node type description. * * @param \Drupal\node\NodeTypeInterface $node_type * The node type object. * * @return string * The node type description. */ function node_type_get_description(NodeTypeInterface $node_type) { return $node_type->description; } /** * Menu argument loader: Loads a node type by string. * * @param $name * The machine name of a node type to load. * * @return \Drupal\node\NodeTypeInterface * A node type object or NULL if $name does not exist. * * @deprecated in Drupal 8.x, will be removed before Drupal 9.0. * Use \Drupal\node\Entity\NodeType::load(). */ function node_type_load($name) { return NodeType::load($name); } /** * Adds the default body field to a node type. * * @param \Drupal\node\NodeTypeInterface $type * A node type object. * @param $label * (optional) The label for the body instance. * * @return * Body field instance. */ function node_add_body_field(NodeTypeInterface $type, $label = 'Body') { // Add or remove the body field, as needed. $field_storage = FieldStorageConfig::loadByName('node', 'body'); $instance = FieldInstanceConfig::loadByName('node', $type->id(), 'body'); if (empty($field_storage)) { $field_storage = entity_create('field_storage_config', array( 'name' => 'body', 'entity_type' => 'node', 'type' => 'text_with_summary', )); $field_storage->save(); } if (empty($instance)) { $instance = entity_create('field_instance_config', array( 'field_storage' => $field_storage, 'bundle' => $type->id(), 'label' => $label, 'settings' => array('display_summary' => TRUE), )); $instance->save(); // Assign widget settings for the 'default' form mode. entity_get_form_display('node', $type->type, 'default') ->setComponent('body', array( 'type' => 'text_textarea_with_summary', )) ->save(); // Assign display settings for the 'default' and 'teaser' view modes. entity_get_display('node', $type->type, 'default') ->setComponent('body', array( 'label' => 'hidden', 'type' => 'text_default', )) ->save(); // The teaser view mode is created by the Standard profile and therefore // might not exist. $view_modes = \Drupal::entityManager()->getViewModes('node'); if (isset($view_modes['teaser'])) { entity_get_display('node', $type->type, 'teaser') ->setComponent('body', array( 'label' => 'hidden', 'type' => 'text_summary_or_trimmed', )) ->save(); } } return $instance; } /** * Implements hook_entity_extra_field_info(). */ function node_entity_extra_field_info() { $extra = array(); $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language'); $description = t('Node module element'); foreach (node_type_get_types() as $bundle) { // Add the 'language' select if Language module is enabled and the bundle // has multilingual support. // Visibility of the ordering of the language selector is the same as on the // node/add form. if ($module_language_enabled) { $configuration = language_get_default_configuration('node', $bundle->type); if ($configuration['language_show']) { $extra['node'][$bundle->type]['form']['langcode'] = array( 'label' => t('Language'), 'description' => $description, 'weight' => 0, ); } } $extra['node'][$bundle->type]['display']['langcode'] = array( 'label' => t('Language'), 'description' => $description, 'weight' => 0, 'visible' => FALSE, ); $extra['node'][$bundle->type]['display']['links'] = array( 'label' => t('Links'), 'description' => $description, 'weight' => 100, 'visible' => TRUE, ); } return $extra; } /** * Updates all nodes of one type to be of another type. * * @param string $old_id * The current node type of the nodes. * @param string $new_id * The new node type of the nodes. * * @return * The number of nodes whose node type field was modified. */ function node_type_update_nodes($old_id, $new_id) { return \Drupal::entityManager()->getStorage('node')->updateType($old_id, $new_id); } /** * Loads node entities from the database. * * This function should be used whenever you need to load more than one node * from the database. Nodes are loaded into memory and will not require database * access if loaded again during the same page request. * * @param array $nids * (optional) An array of entity IDs. If omitted, all entities are loaded. * @param bool $reset * (optional) Whether to reset the internal node_load() cache. Defaults to * FALSE. * * @return \Drupal\node\NodeInterface[] * An array of node entities indexed by nid. * * @deprecated in Drupal 8.x, will be removed before Drupal 9.0. * Use \Drupal\node\Entity\Node::loadMultiple(). * * @see entity_load_multiple() * @see \Drupal\Core\Entity\Query\EntityQueryInterface */ function node_load_multiple(array $nids = NULL, $reset = FALSE) { if ($reset) { \Drupal::entityManager()->getStorage('node')->resetCache($nids); } return Node::loadMultiple($nids); } /** * Loads a node entity from the database. * * @param int $nid * The node ID. * @param bool $reset * (optional) Whether to reset the node_load_multiple() cache. Defaults to * FALSE. * * @return \Drupal\node\NodeInterface|null * A fully-populated node entity, or NULL if the node is not found. * * @deprecated in Drupal 8.x, will be removed before Drupal 9.0. * Use \Drupal\node\Entity\Node::load(). */ function node_load($nid = NULL, $reset = FALSE) { if ($reset) { \Drupal::entityManager()->getStorage('node')->resetCache(array($nid)); } return Node::load($nid); } /** * Loads a node revision from the database. * * @param int $vid * The node revision id. * * @return \Drupal\node\NodeInterface|null * A fully-populated node entity, or NULL if the node is not found. */ function node_revision_load($vid = NULL) { return entity_revision_load('node', $vid); } /** * Deletes a node revision. * * @param $revision_id * The revision ID to delete. * * @return * TRUE if the revision deletion was successful; otherwise, FALSE. */ function node_revision_delete($revision_id) { entity_revision_delete('node', $revision_id); } /** * Checks whether the current page is the full page view of the passed-in node. * * @param \Drupal\node\NodeInterface $node * A node entity. * * @return * The ID of the node if this is a full page view, otherwise FALSE. */ function node_is_page(NodeInterface $node) { $route_match = \Drupal::routeMatch(); if ($route_match->getRouteName() == 'entity.node.canonical') { $page_node = $route_match->getParameter('node'); } return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE); } /** * Implements hook_preprocess_HOOK() for HTML document templates. */ function node_preprocess_html(&$variables) { // If on an individual node page, add the node type to body classes. if (($node = \Drupal::routeMatch()->getParameter('node')) && $node instanceof NodeInterface) { $variables['attributes']['class'][] = drupal_html_class('node--type-' . $node->getType()); } } /** * Implements hook_preprocess_HOOK() for block templates. */ function node_preprocess_block(&$variables) { if ($variables['configuration']['provider'] == 'node') { switch ($variables['elements']['#plugin_id']) { case 'node_syndicate_block': $variables['attributes']['role'] = 'complementary'; break; } } } /** * Implements hook_theme_suggestions_HOOK(). */ function node_theme_suggestions_node(array $variables) { $suggestions = array(); $node = $variables['elements']['#node']; $sanitized_view_mode = strtr($variables['elements']['#view_mode'], '.', '_'); $suggestions[] = 'node__' . $sanitized_view_mode; $suggestions[] = 'node__' . $node->bundle(); $suggestions[] = 'node__' . $node->bundle() . '__' . $sanitized_view_mode; $suggestions[] = 'node__' . $node->id(); $suggestions[] = 'node__' . $node->id() . '__' . $sanitized_view_mode; return $suggestions; } /** * Prepares variables for node templates. * * Default template: node.html.twig. * * Most themes utilize their own copy of node.html.twig. The default is located * inside "/core/modules/node/templates/node.html.twig". Look in there for the full * list of variables. * * @param array $variables * An associative array containing: * - elements: An array of elements to display in view mode. * - node: The node object. * - view_mode: View mode; e.g., 'full', 'teaser'... */ function template_preprocess_node(&$variables) { $variables['view_mode'] = $variables['elements']['#view_mode']; // Provide a distinct $teaser boolean. $variables['teaser'] = $variables['view_mode'] == 'teaser'; $variables['node'] = $variables['elements']['#node']; /** @var \Drupal\node\NodeInterface $node */ $node = $variables['node']; $variables['date'] = drupal_render($variables['elements']['created'], TRUE); unset($variables['elements']['created']); $variables['author_name'] = drupal_render($variables['elements']['uid'], TRUE); unset($variables['elements']['uid']); $variables['url'] = $node->url('canonical', array( 'language' => $node->language(), )); $variables['label'] = $variables['elements']['title']; unset($variables['elements']['title']); // The 'page' variable is set to TRUE in two occasions: // - The view mode is 'full' and we are on the 'node.view' route. // - The node is in preview and view mode is either 'full' or 'default'. $variables['page'] = ($variables['view_mode'] == 'full' && (node_is_page($node)) || (isset($node->in_preview) && in_array($node->preview_view_mode, array('full', 'default')))); // Helpful $content variable for templates. $variables += array('content' => array()); foreach (Element::children($variables['elements']) as $key) { $variables['content'][$key] = $variables['elements'][$key]; } // Display post information only on certain node types. $node_type = $node->type->entity; // Used by RDF to add attributes around the author and date submitted. $variables['author_attributes'] = new Attribute(); $variables['display_submitted'] = $node_type->displaySubmitted(); if ($variables['display_submitted']) { if (theme_get_setting('features.node_user_picture')) { // To change user picture settings (e.g. image style), edit the 'compact' // view mode on the User entity. Note that the 'compact' view mode might // not be configured, so remember to always check the theme setting first. $variables['author_picture'] = user_view($node->getOwner(), 'compact'); } } // Add article ARIA role. $variables['attributes']['role'] = 'article'; // Gather node classes. $variables['attributes']['class'][] = 'node'; $variables['attributes']['class'][] = drupal_html_class('node--type-' . $node->bundle()); if ($node->isPromoted()) { $variables['attributes']['class'][] = 'node--promoted'; } if ($node->isSticky()) { $variables['attributes']['class'][] = 'node--sticky'; } if (!$node->isPublished()) { $variables['attributes']['class'][] = 'node--unpublished'; } if ($variables['view_mode']) { $variables['attributes']['class'][] = drupal_html_class('node--view-mode-' . $variables['view_mode']); } if (isset($node->preview)) { $variables['attributes']['class'][] = 'node--preview'; } } /** * Implements hook_ranking(). */ function node_ranking() { // Create the ranking array and add the basic ranking options. $ranking = array( 'relevance' => array( 'title' => t('Keyword relevance'), // Average relevance values hover around 0.15 'score' => 'i.relevance', ), 'sticky' => array( 'title' => t('Content is sticky at top of lists'), // The sticky flag is either 0 or 1, which is automatically normalized. 'score' => 'n.sticky', ), 'promote' => array( 'title' => t('Content is promoted to the front page'), // The promote flag is either 0 or 1, which is automatically normalized. 'score' => 'n.promote', ), ); // Add relevance based on creation or changed date. if ($node_cron_last = \Drupal::state()->get('node.cron_last')) { $ranking['recent'] = array( 'title' => t('Recently posted'), // Exponential decay with half-life of 6 months, starting at last indexed node 'score' => 'POW(2.0, (GREATEST(n.created, n.changed) - :node_cron_last) * 6.43e-8)', 'arguments' => array(':node_cron_last' => $node_cron_last), ); } return $ranking; } /** * Implements hook_user_cancel(). */ function node_user_cancel($edit, $account, $method) { switch ($method) { case 'user_cancel_block_unpublish': // Unpublish nodes (current revisions). $nids = \Drupal::entityQuery('node') ->condition('uid', $account->id()) ->execute(); module_load_include('inc', 'node', 'node.admin'); node_mass_update($nids, array('status' => 0), NULL, TRUE); break; case 'user_cancel_reassign': // Anonymize all of the nodes for this old account. module_load_include('inc', 'node', 'node.admin'); $vids = \Drupal::entityManager()->getStorage('node')->userRevisionIds($account); node_mass_update($vids, array( 'uid' => 0, 'revision_uid' => 0, ), NULL, TRUE, TRUE); break; } } /** * Implements hook_ENTITY_TYPE_predelete() for user entities. */ function node_user_predelete($account) { // Delete nodes (current revisions). // @todo Introduce node_mass_delete() or make node_mass_update() more flexible. $nids = \Drupal::entityQuery('node') ->condition('uid', $account->id()) ->execute(); entity_delete_multiple('node', $nids); // Delete old revisions. $storage_controller = \Drupal::entityManager()->getStorage('node'); $revisions = $storage_controller->userRevisionIds($account); foreach ($revisions as $revision) { node_revision_delete($revision); } } /** * Returns HTML for the content ranking part of the search settings admin page. * * @param $variables * An associative array containing: * - form: A render element representing the form. * * @see node_search_admin() * @ingroup themeable */ function theme_node_search_admin($variables) { $form = $variables['form']; $output = drupal_render($form['info']); $header = array(t('Factor'), t('Influence')); foreach (Element::children($form['factors']) as $key) { $row = array(); $row[] = $form['factors'][$key]['#title']; $form['factors'][$key]['#title_display'] = 'invisible'; $row[] = drupal_render($form['factors'][$key]); $rows[] = $row; } $table = array( '#type' => 'table', '#header' => $header, '#rows' => $rows, ); $output .= drupal_render($table); $output .= drupal_render_children($form); return $output; } /** * Title callback: Displays the node's title. * * @param \Drupal\node\NodeInterface $node * The node entity. * * @return * An unsanitized string that is the title of the node. * * @see node_menu() */ function node_page_title(NodeInterface $node) { return $node->label(); } /** * Finds the last time a node was changed. * * @param $nid * The ID of a node. * @param string $langcode * (optional) The language the node has been last modified in. Defaults to the * node language. * * @return string * A unix timestamp indicating the last time the node was changed. * * @todo Remove once https://drupal.org/node/2002180 is resolved. It's only used * for validation, which will be done by EntityChangedConstraintValidator. */ function node_last_changed($nid, $langcode = NULL) { $changed = \Drupal::entityManager()->getStorage('node')->loadUnchanged($nid)->getChangedTime(); return $changed ? $changed : FALSE; } /** * Finds the most recently changed nodes that are available to the current user. * * @param $number * (optional) The maximum number of nodes to find. Defaults to 10. * * @return * An array of node entities or an empty array if there are no recent nodes * visible to the current user. */ function node_get_recent($number = 10) { $account = \Drupal::currentUser(); $query = \Drupal::entityQuery('node'); if (!$account->hasPermission('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. $access_query = \Drupal::entityQuery('node') ->condition('uid', $account->id()) ->condition('status', NODE_NOT_PUBLISHED); if ($account->hasPermission('view own unpublished content') && ($own_unpublished = $access_query->execute())) { $query->orConditionGroup() ->condition('status', NODE_PUBLISHED) ->condition('nid', $own_unpublished, 'IN'); } else { // If not, restrict the query to published nodes. $query->condition('status', NODE_PUBLISHED); } } $nids = $query ->sort('changed', 'DESC') ->range(0, $number) ->addTag('node_access') ->execute(); $nodes = node_load_multiple($nids); return $nodes ? $nodes : array(); } /** * Page callback: Generates and prints an RSS feed. * * Generates an RSS feed from an array of node IDs, and prints it with an HTTP * header, with Content Type set to RSS/XML. * * @param $nids * (optional) An array of node IDs (nid). Defaults to FALSE so empty feeds can * be generated with passing an empty array, if no items are to be added * to the feed. * @param $channel * (optional) An associative array containing 'title', 'link', 'description', * and other keys, to be parsed by format_rss_channel() and * format_xml_elements(). A list of channel elements can be found at the * @link http://cyber.law.harvard.edu/rss/rss.html RSS 2.0 Specification. @endlink * The link should be an absolute URL. * * @todo Convert taxonomy_term_feed() to a view, so this method is not needed * anymore. * * @return Symfony\Component\HttpFoundation\Response * A response object. * * @see node_menu() */ function node_feed($nids = FALSE, $channel = array()) { global $base_url; $language_content = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT); $rss_config = \Drupal::config('system.rss'); if ($nids === FALSE) { $nids = \Drupal::entityQuery('node') ->condition('status', 1) ->condition('promote', 1) ->sort('created', 'DESC') ->range(0, $rss_config->get('items.limit')) ->addTag('node_access') ->execute(); } $view_mode = $rss_config->get('items.view_mode'); $namespaces = array('xmlns:dc' => 'http://purl.org/dc/elements/1.1/'); // Load all nodes to be rendered. /** @var \Drupal\node\NodeInterface[] $nodes */ $nodes = node_load_multiple($nids); $items = ''; foreach ($nodes as $node) { $item_text = ''; $node->link = url('node/' . $node->id(), array('absolute' => TRUE)); $node->rss_namespaces = array(); $node->rss_elements = array( array('key' => 'pubDate', 'value' => gmdate('r', $node->getCreatedTime())), array('key' => 'dc:creator', 'value' => $node->getOwner()->label()), array('key' => 'guid', 'value' => $node->id() . ' at ' . $base_url, 'attributes' => array('isPermaLink' => 'false')) ); // The node gets built and modules add to or modify $node->rss_elements // and $node->rss_namespaces. $build = node_view($node, 'rss'); unset($build['#theme']); if (!empty($node->rss_namespaces)) { $namespaces = array_merge($namespaces, $node->rss_namespaces); } if ($view_mode != 'title') { // We render node contents and force links to be last. $build['links']['#weight'] = 1000; $item_text .= drupal_render($build); } $items .= format_rss_item($node->label(), $node->link, $item_text, $node->rss_elements); } $channel_defaults = array( 'version' => '2.0', 'title' => \Drupal::config('system.site')->get('name'), 'link' => $base_url, 'description' => $rss_config->get('channel.description'), 'language' => $language_content->id ); $channel_extras = array_diff_key($channel, $channel_defaults); $channel = array_merge($channel_defaults, $channel); $output = "\n"; $output .= "