node.module 64.6 KB
Newer Older
Dries's avatar
 
Dries committed
1 2
<?php

Dries's avatar
 
Dries committed
3 4
/**
 * @file
5 6 7 8
 * The core module that allows content to be submitted to the site.
 *
 * Modules and scripts may programmatically submit nodes using the usual form
 * API pattern.
Dries's avatar
 
Dries committed
9 10
 */

11
use Drupal\Component\Utility\String;
12
use Drupal\Core\Language\Language;
13
use Symfony\Component\HttpFoundation\Response;
14
use Drupal\Core\Cache\Cache;
15 16
use Drupal\Core\Database\Query\AlterableInterface;
use Drupal\Core\Database\Query\SelectInterface;
17
use Drupal\node\NodeTypeInterface;
18
use Drupal\node\NodeInterface;
19
use Drupal\Core\Entity\EntityInterface;
20 21
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
use Drupal\Core\Entity\Display\EntityFormDisplayInterface;
22
use Drupal\Core\Template\Attribute;
23
use Drupal\file\Entity\File;
24
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
25

26
/**
27
 * Denotes that the node is not published.
28
 */
29
const NODE_NOT_PUBLISHED = 0;
30 31

/**
32
 * Denotes that the node is published.
33
 */
34
const NODE_PUBLISHED = 1;
35 36

/**
37
 * Denotes that the node is not promoted to the front page.
38
 */
39
const NODE_NOT_PROMOTED = 0;
40 41

/**
42
 * Denotes that the node is promoted to the front page.
43
 */
44
const NODE_PROMOTED = 1;
45 46

/**
47
 * Denotes that the node is not sticky at the top of the page.
48
 */
49
const NODE_NOT_STICKY = 0;
50 51

/**
52
 * Denotes that the node is sticky at the top of the page.
53
 */
54
const NODE_STICKY = 1;
55

56
/**
57 58 59 60
 * Denotes that access is allowed for a node.
 *
 * Modules should return this value from hook_node_access() to allow access to a
 * node.
61
 */
62
const NODE_ACCESS_ALLOW = TRUE;
63 64

/**
65 66 67 68
 * Denotes that access is denied for a node.
 *
 * Modules should return this value from hook_node_access() to deny access to a
 * node.
69
 */
70
const NODE_ACCESS_DENY = FALSE;
71 72

/**
73 74 75 76
 * Denotes that access is unaffected for a node.
 *
 * Modules should return this value from hook_node_access() to indicate no
 * effect on node access.
77
 */
78
const NODE_ACCESS_IGNORE = NULL;
79

Dries's avatar
 
Dries committed
80
/**
81
 * Implements hook_help().
Dries's avatar
 
Dries committed
82
 */
83
function node_help($path, $arg) {
84 85 86
  // Remind site administrators about the {node_access} table being flagged
  // for rebuild. We don't need to issue the message on the confirm form, or
  // while the rebuild is being processed.
87
  if ($path != 'admin/reports/status/rebuild' && $path != 'batch' && strpos($path, '#') === FALSE
88
      && user_access('access administration pages') && node_access_needs_rebuild()) {
89
    if ($path == 'admin/reports/status') {
90 91 92
      $message = t('The content access permissions need to be rebuilt.');
    }
    else {
93
      $message = t('The content access permissions need to be rebuilt. <a href="!node_access_rebuild">Rebuild permissions</a>.', array('!node_access_rebuild' => \Drupal::url('node.configure_rebuild_confirm')));
94 95 96 97
    }
    drupal_set_message($message, 'error');
  }

98
  switch ($path) {
Dries's avatar
 
Dries committed
99
    case 'admin/help#node':
100 101
      $output = '';
      $output .= '<h3>' . t('About') . '</h3>';
102
      $output .= '<p>' . 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 <a href="!field">Field module</a>). For more information, see <a href="!node">the online documentation for the Node module</a>.', array('!node' => 'https://drupal.org/documentation/modules/node', '!field' => \Drupal::url('help.page', array('name' => 'field')))) . '</p>';
103 104
      $output .= '<h3>' . t('Uses') . '</h3>';
      $output .= '<dl>';
105
      $output .= '<dt>' . t('Creating content') . '</dt>';
106
      $output .= '<dd>' . t('When new content is created, the Node module records basic information about the content, including the author, date of creation, and the <a href="!content-type">Content type</a>. It also manages the <em>publishing options</em>, which define whether or not the content is published, promoted to the front page of the site, and/or sticky at the top of content lists. Default settings can be configured for each <a href="!content-type">type of content</a> on your site.', array('!content-type' => \Drupal::url('node.overview_types'))) . '</dd>';
107
      $output .= '<dt>' . t('Creating custom content types') . '</dt>';
108
      $output .= '<dd>' . t('The Node module gives users with the <em>Administer content types</em> permission the ability to <a href="!content-new">create new content types</a> in addition to the default ones already configured. Creating custom content types allows you the flexibility to add <a href="!field">fields</a> and configure default settings that suit the differing needs of various site content.', array('!content-new' => \Drupal::url('node.type_add'), '!field' => \Drupal::url('help.page', array('name' => 'field')))) . '</dd>';
109
      $output .= '<dt>' . t('Administering content') . '</dt>';
110
      $output .= '<dd>' . t('The <a href="!content">Content administration page</a> allows you to review and bulk manage your site content.', array('!content' => \Drupal::url('node.content_overview'))) . '</dd>';
111 112
      $output .= '<dt>' . t('Creating revisions') . '</dt>';
      $output .= '<dd>' . t('The Node module also enables you to create multiple versions of any content, and revert to older versions using the <em>Revision information</em> settings.') . '</dd>';
113
      $output .= '<dt>' . t('User permissions') . '</dt>';
114
      $output .= '<dd>' . t('The Node module makes a number of permissions available for each content type, which can be set by role on the <a href="!permissions">permissions page</a>.', array('!permissions' => \Drupal::url('user.admin_permissions', array(), array('fragment' => 'module-node')))) . '</dd>';
115
      $output .= '</dl>';
116
      return $output;
117

118
    case 'admin/structure/types/add':
119
      return '<p>' . t('Individual content types can have different fields, behaviors, and permissions assigned to them.') . '</p>';
120

121
    case 'admin/structure/types/manage/%/form-display':
122 123
      $type = entity_load('node_type', $arg[4]);
      return '<p>' . 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())) . '</p>' ;
124

125
    case 'admin/structure/types/manage/%/display':
126
      $type =  entity_load('node_type', $arg[4]);
127
      return '<p>' . t('Content items can be displayed using different view modes: Teaser, Full content, Print, RSS, etc. <em>Teaser</em> is a short format that is typically used in lists of multiple content items. <em>Full content</em> is typically used when the content is displayed on its own page.') . '</p>' .
128
        '<p>' . 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())) . '</p>';
129

130
    case 'node/%/revisions':
131
      return '<p>' . t('Revisions allow you to track differences between multiple versions of your content, and revert back to older versions.') . '</p>';
132

133 134
    case 'node/%/edit':
      $node = node_load($arg[1]);
135
      $type = node_type_load($node->bundle());
136
      return (!empty($type->help) ? filter_xss_admin($type->help) : '');
Dries's avatar
 
Dries committed
137
  }
Dries's avatar
 
Dries committed
138

139
  if ($arg[0] == 'node' && $arg[1] == 'add' && $arg[2]) {
140
    $type = node_type_load($arg[2]);
141
    return (!empty($type->help) ? filter_xss_admin($type->help) : '');
142
  }
Dries's avatar
 
Dries committed
143 144
}

145
/**
146
 * Implements hook_theme().
147 148 149
 */
function node_theme() {
  return array(
150
    'node' => array(
151
      'render element' => 'elements',
152
      'template' => 'node',
153
    ),
154
    'node_search_admin' => array(
155
      'render element' => 'form',
156
    ),
157
    'node_add_list' => array(
158
      'variables' => array('content' => NULL),
159
      'file' => 'node.pages.inc',
160
      'template' => 'node-add-list',
161 162
    ),
    'node_preview' => array(
163
      'variables' => array('node' => NULL),
164
      'file' => 'node.pages.inc',
165
      'template' => 'node-preview',
166
    ),
167 168 169 170
    'node_edit_form' => array(
      'render element' => 'form',
      'template' => 'node-edit-form',
    ),
171 172 173
    'field__node__title' => array(
      'base hook' => 'field',
    ),
174 175 176
  );
}

177 178 179 180 181
/**
 * Implements hook_entity_bundle_info().
 */
function node_entity_bundle_info() {
  $bundles = array();
182
  // Bundles must provide a human readable name so we can create help and error
183
  // messages.
184 185
  foreach (node_type_get_names() as $id => $label) {
    $bundles['node'][$id]['label'] = $label;
186
  }
187
  return $bundles;
Dries's avatar
 
Dries committed
188 189
}

190
/**
191
 * Implements hook_entity_view_display_alter().
192
 */
193
function node_entity_view_display_alter(EntityViewDisplayInterface $display, $context) {
194 195 196 197 198 199 200 201
  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);
        }
202 203
      }
    }
204 205 206 207 208 209
  }
}

/**
 * Implements hook_entity_form_display_alter().
 */
210
function node_entity_form_display_alter(EntityFormDisplayInterface $form_display, $context) {
211 212
  if ($context['entity_type'] == 'node') {
    $node_type = node_type_load($context['bundle']);
213 214 215
    // @todo Reconsider when per-bundle overrides of field definitions are
    //   possible - https://drupal.org/node/2114707.
    if (!$node_type->has_title) {
216 217
      $form_display->removeComponent('title');
    }
218 219 220
  }
}

221
/**
222
 * Entity URI callback.
223
 *
224
 * @param \Drupal\node\NodeInterface $node
225
 *   A node entity.
226 227
 *
 * @return array
228
 *   An array with 'path' as the key and the path to the node as its value.
229
 */
230
function node_uri(NodeInterface $node) {
231
  return array(
232 233 234 235
    'route_name' => 'node.view',
    'route_parameters' => array(
      'node' => $node->id(),
    ),
236
  );
237 238
}

Dries's avatar
 
Dries committed
239
/**
240
 * Gathers a listing of links to nodes.
Dries's avatar
 
Dries committed
241 242
 *
 * @param $result
243
 *   A database result object from a query to fetch node entities. If your
244
 *   query joins the {comment_entity_statistics} table so that the comment_count
245 246
 *   field is available, a title attribute will be added to show the number of
 *   comments.
Dries's avatar
 
Dries committed
247
 * @param $title
248
 *   (optional) A heading for the resulting list.
Dries's avatar
 
Dries committed
249 250
 *
 * @return
251 252
 *   A renderable array containing a list of linked node titles fetched from
 *   $result, or FALSE if there are no rows in $result.
Dries's avatar
 
Dries committed
253
 */
Dries's avatar
 
Dries committed
254
function node_title_list($result, $title = NULL) {
255
  $items = array();
256
  $num_rows = FALSE;
257
  foreach ($result as $node) {
258
    // Do not use $node->label() here, because $node comes from the database.
259
    $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());
260
    $num_rows = TRUE;
Dries's avatar
 
Dries committed
261 262
  }

263
  return $num_rows ? array('#theme' => 'item_list__node', '#items' => $items, '#title' => $title) : FALSE;
Dries's avatar
 
Dries committed
264 265
}

Dries's avatar
 
Dries committed
266
/**
267
 * Determines the type of marker to be displayed for a given node.
Dries's avatar
 
Dries committed
268
 *
Dries's avatar
 
Dries committed
269 270 271 272
 * @param $nid
 *   Node ID whose history supplies the "last viewed" timestamp.
 * @param $timestamp
 *   Time which is compared against node's "last viewed" timestamp.
273
 *
274 275
 * @return
 *   One of the MARK constants.
Dries's avatar
 
Dries committed
276
 */
277
function node_mark($nid, $timestamp) {
278

279
  $cache = &drupal_static(__FUNCTION__, array());
Dries's avatar
 
Dries committed
280

281
  if (\Drupal::currentUser()->isAnonymous() || !\Drupal::moduleHandler()->moduleExists('history')) {
282 283
    return MARK_READ;
  }
Dries's avatar
Dries committed
284
  if (!isset($cache[$nid])) {
285
    $cache[$nid] = history_read($nid);
Dries's avatar
 
Dries committed
286
  }
287
  if ($cache[$nid] == 0 && $timestamp > HISTORY_READ_LIMIT) {
288 289
    return MARK_NEW;
  }
290
  elseif ($timestamp > $cache[$nid] && $timestamp > HISTORY_READ_LIMIT) {
291 292 293
    return MARK_UPDATED;
  }
  return MARK_READ;
Dries's avatar
 
Dries committed
294 295
}

296 297 298
/**
 * Returns a list of all the available node types.
 *
299 300
 * This list can include types that are queued for addition or deletion.
 *
301 302
 * @return array
 *   An array of node type entities, keyed by ID.
303
 *
304
 * @see node_type_load()
305 306
 */
function node_type_get_types() {
307
  return entity_load_multiple('node_type');
308 309 310
}

/**
311 312 313
 * Returns a list of available node type names.
 *
 * This list can include types that are queued for addition or deletion.
314
 *
315
 * @return array
316
 *   An array of node type labels, keyed by the node type name.
317 318
 */
function node_type_get_names() {
319
  $cid = 'node_type:names:' . \Drupal::languageManager()->getCurrentLanguage()->id;
320
  if ($cache = \Drupal::cache()->get($cid)) {
321 322 323
    return $cache->data;
  }
  // Not using node_type_get_types() or entity_load_multiple() here, to allow
324
  // this function being used in hook_entity_type_build() implementations.
325
  // @todo Consider to convert this into a generic config entity helper.
326
  $config_names = \Drupal::configFactory()->listAll('node.type.');
327 328
  $names = array();
  foreach ($config_names as $config_name) {
329
    $config = \Drupal::config($config_name);
330 331
    $names[$config->get('type')] = $config->get('name');
  }
332
  \Drupal::cache()->set($cid, $names, Cache::PERMANENT, array(
333 334 335 336
    'node_type' => array_keys($names),
    'node_types' => TRUE,
  ));
  return $names;
337 338 339 340 341
}

/**
 * Returns the node type label for the passed node.
 *
342
 * @param \Drupal\node\NodeInterface $node
343 344 345 346
 *   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.
347
 *
348 349
 * @todo Add this as generic helper method for config entities representing
 *   entity bundles.
350
 */
351
function node_get_type_label(NodeInterface $node) {
352 353
  $type = entity_load('node_type', $node->bundle());
  return $type ? $type->label() : FALSE;
354 355 356 357 358
}

/**
 * Description callback: Returns the node type description.
 *
359
 * @param \Drupal\node\NodeTypeInterface $node_type
360 361
 *   The node type object.
 *
362
 * @return string
363 364
 *   The node type description.
 */
365
function node_type_get_description(NodeTypeInterface $node_type) {
366 367 368
  return $node_type->description;
}

369
/**
370
 * Menu argument loader: Loads a node type by string.
371 372
 *
 * @param $name
373
 *   The machine name of a node type to load.
374
 *
375
 * @return \Drupal\node\NodeTypeInterface
376
 *   A node type object or NULL if $name does not exist.
377 378
 */
function node_type_load($name) {
379
  return entity_load('node_type', $name);
380
}
381

382
/**
383
 * Adds the default body field to a node type.
384
 *
385
 * @param \Drupal\node\NodeTypeInterface $type
386
 *   A node type object.
387
 * @param $label
388
 *   (optional) The label for the body instance.
389 390 391
 *
 * @return
 *   Body field instance.
392
 */
393
function node_add_body_field(NodeTypeInterface $type, $label = 'Body') {
394
   // Add or remove the body field, as needed.
395
  $field = field_info_field('node', 'body');
396
  $instance = field_info_instance('node', 'body', $type->id());
397
  if (empty($field)) {
398
    $field = entity_create('field_config', array(
399 400
      'name' => 'body',
      'entity_type' => 'node',
401
      'type' => 'text_with_summary',
402 403
    ));
    $field->save();
404
  }
405
  if (empty($instance)) {
406
    $instance = entity_create('field_instance_config', array(
407 408
      'field_name' => 'body',
      'entity_type' => 'node',
409
      'bundle' => $type->id(),
410 411
      'label' => $label,
      'settings' => array('display_summary' => TRUE),
412 413
    ));
    $instance->save();
414

415 416
    // Assign widget settings for the 'default' form mode.
    entity_get_form_display('node', $type->type, 'default')
417
      ->setComponent('body', array(
418 419 420 421
        'type' => 'text_textarea_with_summary',
      ))
      ->save();

422 423
    // Assign display settings for the 'default' and 'teaser' view modes.
    entity_get_display('node', $type->type, 'default')
424
      ->setComponent('body', array(
425 426 427 428
        'label' => 'hidden',
        'type' => 'text_default',
      ))
      ->save();
429 430 431 432 433 434 435 436 437 438 439 440

    // The teaser view mode is created by the Standard profile and therefore
    // might not exist.
    $view_modes = entity_get_view_modes('node');
    if (isset($view_modes['teaser'])) {
      entity_get_display('node', $type->type, 'teaser')
        ->setComponent('body', array(
          'label' => 'hidden',
          'type' => 'text_summary_or_trimmed',
        ))
        ->save();
    }
441
  }
442

443
  return $instance;
444
}
445

446 447 448 449 450
/**
 * Implements hook_field_extra_fields().
 */
function node_field_extra_fields() {
  $extra = array();
451
  $module_language_enabled = \Drupal::moduleHandler()->moduleExists('language');
452 453 454
  $description = t('Node module element');

  foreach (node_type_get_types() as $bundle) {
455 456
    // Add the 'language' select if Language module is enabled and the bundle
    // has multilingual support.
457
    // Visibility of the ordering of the language selector is the same as on the
458 459 460
    // node/add form.
    if ($module_language_enabled) {
      $configuration = language_get_default_configuration('node', $bundle->type);
461
      if ($configuration['language_show']) {
462
        $extra['node'][$bundle->type]['form']['langcode'] = array(
463 464 465 466 467
          'label' => t('Language'),
          'description' => $description,
          'weight' => 0,
        );
      }
468
    }
469
    $extra['node'][$bundle->type]['display']['langcode'] = array(
470 471 472 473 474
      'label' => t('Language'),
      'description' => $description,
      'weight' => 0,
      'visible' => FALSE,
    );
475
  }
476 477

  return $extra;
478 479
}

480
/**
481 482
 * Updates all nodes of one type to be of another type.
 *
483
 * @param string $old_id
484
 *   The current node type of the nodes.
485
 * @param string $new_id
486
 *   The new node type of the nodes.
487 488
 *
 * @return
489
 *   The number of nodes whose node type field was modified.
490
 */
491
function node_type_update_nodes($old_id, $new_id) {
492
  return db_update('node')
493 494
    ->fields(array('type' => $new_id))
    ->condition('type', $old_id)
495
    ->execute();
Dries's avatar
 
Dries committed
496
}
Dries's avatar
 
Dries committed
497

498
/**
499
 * Loads node entities from the database.
500 501
 *
 * This function should be used whenever you need to load more than one node
502 503
 * from the database. Nodes are loaded into memory and will not require database
 * access if loaded again during the same page request.
Dries's avatar
 
Dries committed
504
 *
505 506
 * @param array $nids
 *   (optional) An array of entity IDs. If omitted, all entities are loaded.
507
 * @param bool $reset
508 509
 *   (optional) Whether to reset the internal node_load() cache.  Defaults to
 *   FALSE.
Dries's avatar
 
Dries committed
510
 *
511
 * @return \Drupal\node\NodeInterface[]
512
 *   An array of node entities indexed by nid.
513
 *
514
 * @see entity_load_multiple()
515
 * @see \Drupal\Core\Entity\Query\EntityQueryInterface
Dries's avatar
 
Dries committed
516
 */
517
function node_load_multiple(array $nids = NULL, $reset = FALSE) {
518
  return entity_load_multiple('node', $nids, $reset);
519 520 521
}

/**
522
 * Loads a node entity from the database.
523
 *
524
 * @param int $nid
525
 *   The node ID.
526
 * @param bool $reset
527 528
 *   (optional) Whether to reset the node_load_multiple() cache. Defaults to
 *   FALSE.
529
 *
530 531
 * @return \Drupal\node\NodeInterface|null
 *   A fully-populated node entity, or NULL if the node is not found.
532
 */
533
function node_load($nid = NULL, $reset = FALSE) {
534
  return entity_load('node', $nid, $reset);
535 536 537 538 539
}

/**
 * Loads a node revision from the database.
 *
540
 * @param int $vid
541 542
 *   The node revision id.
 *
543 544
 * @return \Drupal\node\NodeInterface|null
 *   A fully-populated node entity, or NULL if the node is not found.
545 546 547
 */
function node_revision_load($vid = NULL) {
  return entity_revision_load('node', $vid);
Dries's avatar
 
Dries committed
548 549
}

550
/**
551
 * Deletes a node revision.
552 553 554
 *
 * @param $revision_id
 *   The revision ID to delete.
555 556
 *
 * @return
557
 *   TRUE if the revision deletion was successful; otherwise, FALSE.
558 559
 */
function node_revision_delete($revision_id) {
560
  entity_revision_delete('node', $revision_id);
561 562
}

563
/**
564
 * Checks whether the current page is the full page view of the passed-in node.
565
 *
566
 * @param \Drupal\node\NodeInterface $node
567
 *   A node entity.
568 569 570
 *
 * @return
 *   The ID of the node if this is a full page view, otherwise FALSE.
571
 */
572
function node_is_page(NodeInterface $node) {
573 574 575 576
  $request = \Drupal::request();
  if ($request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'node.view') {
    $page_node = $request->attributes->get('node');
  }
577
  return (!empty($page_node) ? $page_node->id() == $node->id() : FALSE);
578 579
}

580 581 582 583 584
/**
 * 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.
585
  if (($node = \Drupal::request()->attributes->get('node')) && $node instanceof NodeInterface) {
586 587 588 589
    $variables['attributes']['class'][] = drupal_html_class('node-type-' . $node->getType());
  }
}

590
/**
591
 * Implements hook_preprocess_HOOK() for block templates.
592 593
 */
function node_preprocess_block(&$variables) {
594 595
  if ($variables['configuration']['module'] == 'node') {
    switch ($variables['elements']['#plugin_id']) {
596
      case 'node_syndicate_block':
597
        $variables['attributes']['role'] = 'complementary';
598 599 600 601 602
        break;
    }
  }
}

603 604 605 606 607 608 609 610 611 612 613 614 615
/**
 * Implements hook_theme_suggestions_HOOK().
 */
function node_theme_suggestions_node(array $variables) {
  $suggestions = array();
  $node = $variables['elements']['#node'];

  $suggestions[] = 'node__' . $node->bundle();
  $suggestions[] = 'node__' . $node->id();

  return $suggestions;
}

616
/**
617
 * Prepares variables for node templates.
618
 *
619
 * Default template: node.html.twig.
620
 *
621 622 623 624 625
 * 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
626 627 628 629
 *   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'...
630 631
 */
function template_preprocess_node(&$variables) {
632
  $variables['view_mode'] = $variables['elements']['#view_mode'];
633
  // Provide a distinct $teaser boolean.
634
  $variables['teaser'] = $variables['view_mode'] == 'teaser';
635
  $variables['node'] = $variables['elements']['#node'];
636
  /** @var \Drupal\node\NodeInterface $node */
637 638
  $node = $variables['node'];

639
  $variables['date'] = format_date($node->getCreatedTime());
640 641
  $username = array(
    '#theme' => 'username',
642
    '#account' => $node->getOwner(),
643
    '#link_options' => array('attributes' => array('rel' => 'author')),
644 645
  );
  $variables['name'] = drupal_render($username);
646

647
  $variables['node_url'] = $node->url();
648 649
  $variables['label'] = $variables['elements']['title'];
  unset($variables['elements']['title']);
650
  $variables['page'] = $variables['view_mode'] == 'full' && node_is_page($node);
651

652
  // Helpful $content variable for templates.
653
  $variables += array('content' => array());
654 655 656
  foreach (element_children($variables['elements']) as $key) {
    $variables['content'][$key] = $variables['elements'][$key];
  }
657 658

  // Display post information only on certain node types.
659 660 661 662 663
  // Avoid loading the entire node type config entity here that may not exist.
  $node_type_config = \Drupal::config('node.type.' . $node->bundle());
  // Display submitted by default.
  $variables['display_submitted'] = $node_type_config->isNew() || $node_type_config->get('settings.node.submitted');
  if ($variables['display_submitted']) {
664
    $variables['submitted'] = t('Submitted by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
665
    if (theme_get_setting('features.node_user_picture')) {
666
      // To change user picture settings (e.g. image style), edit the 'compact'
667 668
      // 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.
669
      $variables['user_picture'] = user_view($node->getOwner(), 'compact');
670 671 672 673
    }
    else {
      $variables['user_picture'] = array();
    }
674 675
  }
  else {
676
    $variables['submitted'] = '';
677
    $variables['user_picture'] = '';
678
  }
679

680
  // Add article ARIA role.
681
  $variables['attributes']['role'] = 'article';
682

683
  // Gather node classes.
684
  $variables['attributes']['class'][] = 'node';
685 686
  $variables['attributes']['class'][] = drupal_html_class('node-' . $node->bundle());
  if ($node->isPromoted()) {
687
    $variables['attributes']['class'][] = 'promoted';
688
  }
689
  if ($node->isSticky()) {
690
    $variables['attributes']['class'][] = 'sticky';
691
  }
692
  if (!$node->isPublished()) {
693
    $variables['attributes']['class'][] = 'unpublished';
694
  }
695
  if ($variables['view_mode']) {
696
    $variables['attributes']['class'][] = drupal_html_class('view-mode-' . $variables['view_mode']);
697 698
  }
  if (isset($variables['preview'])) {
699
    $variables['attributes']['class'][] = 'preview';
700
  }
701
  $variables['content_attributes']['class'][] = 'content';
702 703
}

704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
/**
 * Returns HTML for the node title field.
 *
 * This is an override of theme_field() for the node title field. See that
 * function for documentation about its details and overrides.
 *
 * @param array $variables
 *   An associative array. See theme_field() for details.
 *
 * @see theme_field()
 *
 * @ingroup themeable
 */
function theme_field__node__title($variables) {
  return '<span' . $variables['attributes'] . '>' . drupal_render($variables['items']) . '</span>';
}

Dries's avatar
 
Dries committed
721
/**