Security update for Core, with self-updated composer
[yaffs-website] / web / core / modules / node / src / Entity / Node.php
1 <?php
2
3 namespace Drupal\node\Entity;
4
5 use Drupal\Core\Entity\EditorialContentEntityBase;
6 use Drupal\Core\Entity\EntityStorageInterface;
7 use Drupal\Core\Entity\EntityTypeInterface;
8 use Drupal\Core\Field\BaseFieldDefinition;
9 use Drupal\Core\Session\AccountInterface;
10 use Drupal\node\NodeInterface;
11 use Drupal\user\UserInterface;
12
13 /**
14  * Defines the node entity class.
15  *
16  * @ContentEntityType(
17  *   id = "node",
18  *   label = @Translation("Content"),
19  *   label_collection = @Translation("Content"),
20  *   label_singular = @Translation("content item"),
21  *   label_plural = @Translation("content items"),
22  *   label_count = @PluralTranslation(
23  *     singular = "@count content item",
24  *     plural = "@count content items"
25  *   ),
26  *   bundle_label = @Translation("Content type"),
27  *   handlers = {
28  *     "storage" = "Drupal\node\NodeStorage",
29  *     "storage_schema" = "Drupal\node\NodeStorageSchema",
30  *     "view_builder" = "Drupal\node\NodeViewBuilder",
31  *     "access" = "Drupal\node\NodeAccessControlHandler",
32  *     "views_data" = "Drupal\node\NodeViewsData",
33  *     "form" = {
34  *       "default" = "Drupal\node\NodeForm",
35  *       "delete" = "Drupal\node\Form\NodeDeleteForm",
36  *       "edit" = "Drupal\node\NodeForm"
37  *     },
38  *     "route_provider" = {
39  *       "html" = "Drupal\node\Entity\NodeRouteProvider",
40  *     },
41  *     "list_builder" = "Drupal\node\NodeListBuilder",
42  *     "translation" = "Drupal\node\NodeTranslationHandler"
43  *   },
44  *   base_table = "node",
45  *   data_table = "node_field_data",
46  *   revision_table = "node_revision",
47  *   revision_data_table = "node_field_revision",
48  *   show_revision_ui = TRUE,
49  *   translatable = TRUE,
50  *   list_cache_contexts = { "user.node_grants:view" },
51  *   entity_keys = {
52  *     "id" = "nid",
53  *     "revision" = "vid",
54  *     "bundle" = "type",
55  *     "label" = "title",
56  *     "langcode" = "langcode",
57  *     "uuid" = "uuid",
58  *     "status" = "status",
59  *     "published" = "status",
60  *     "uid" = "uid",
61  *   },
62  *   revision_metadata_keys = {
63  *     "revision_user" = "revision_uid",
64  *     "revision_created" = "revision_timestamp",
65  *     "revision_log_message" = "revision_log"
66  *   },
67  *   bundle_entity_type = "node_type",
68  *   field_ui_base_route = "entity.node_type.edit_form",
69  *   common_reference_target = TRUE,
70  *   permission_granularity = "bundle",
71  *   links = {
72  *     "canonical" = "/node/{node}",
73  *     "delete-form" = "/node/{node}/delete",
74  *     "edit-form" = "/node/{node}/edit",
75  *     "version-history" = "/node/{node}/revisions",
76  *     "revision" = "/node/{node}/revisions/{node_revision}/view",
77  *     "create" = "/node",
78  *   }
79  * )
80  */
81 class Node extends EditorialContentEntityBase implements NodeInterface {
82
83   /**
84    * Whether the node is being previewed or not.
85    *
86    * The variable is set to public as it will give a considerable performance
87    * improvement. See https://www.drupal.org/node/2498919.
88    *
89    * @var true|null
90    *   TRUE if the node is being previewed and NULL if it is not.
91    */
92   public $in_preview = NULL;
93
94   /**
95    * {@inheritdoc}
96    */
97   public function preSave(EntityStorageInterface $storage) {
98     parent::preSave($storage);
99
100     foreach (array_keys($this->getTranslationLanguages()) as $langcode) {
101       $translation = $this->getTranslation($langcode);
102
103       // If no owner has been set explicitly, make the anonymous user the owner.
104       if (!$translation->getOwner()) {
105         $translation->setOwnerId(0);
106       }
107     }
108
109     // If no revision author has been set explicitly, make the node owner the
110     // revision author.
111     if (!$this->getRevisionUser()) {
112       $this->setRevisionUserId($this->getOwnerId());
113     }
114   }
115
116   /**
117    * {@inheritdoc}
118    */
119   public function preSaveRevision(EntityStorageInterface $storage, \stdClass $record) {
120     parent::preSaveRevision($storage, $record);
121
122     if (!$this->isNewRevision() && isset($this->original) && (!isset($record->revision_log) || $record->revision_log === '')) {
123       // If we are updating an existing node without adding a new revision, we
124       // need to make sure $entity->revision_log is reset whenever it is empty.
125       // Therefore, this code allows us to avoid clobbering an existing log
126       // entry with an empty one.
127       $record->revision_log = $this->original->revision_log->value;
128     }
129   }
130
131   /**
132    * {@inheritdoc}
133    */
134   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
135     parent::postSave($storage, $update);
136
137     // Update the node access table for this node, but only if it is the
138     // default revision. There's no need to delete existing records if the node
139     // is new.
140     if ($this->isDefaultRevision()) {
141       /** @var \Drupal\node\NodeAccessControlHandlerInterface $access_control_handler */
142       $access_control_handler = \Drupal::entityManager()->getAccessControlHandler('node');
143       $grants = $access_control_handler->acquireGrants($this);
144       \Drupal::service('node.grant_storage')->write($this, $grants, NULL, $update);
145     }
146
147     // Reindex the node when it is updated. The node is automatically indexed
148     // when it is added, simply by being added to the node table.
149     if ($update) {
150       node_reindex_node_search($this->id());
151     }
152   }
153
154   /**
155    * {@inheritdoc}
156    */
157   public static function preDelete(EntityStorageInterface $storage, array $entities) {
158     parent::preDelete($storage, $entities);
159
160     // Ensure that all nodes deleted are removed from the search index.
161     if (\Drupal::moduleHandler()->moduleExists('search')) {
162       foreach ($entities as $entity) {
163         search_index_clear('node_search', $entity->nid->value);
164       }
165     }
166   }
167
168   /**
169    * {@inheritdoc}
170    */
171   public static function postDelete(EntityStorageInterface $storage, array $nodes) {
172     parent::postDelete($storage, $nodes);
173     \Drupal::service('node.grant_storage')->deleteNodeRecords(array_keys($nodes));
174   }
175
176   /**
177    * {@inheritdoc}
178    */
179   public function getType() {
180     return $this->bundle();
181   }
182
183   /**
184    * {@inheritdoc}
185    */
186   public function access($operation = 'view', AccountInterface $account = NULL, $return_as_object = FALSE) {
187     // This override exists to set the operation to the default value "view".
188     return parent::access($operation, $account, $return_as_object);
189   }
190
191   /**
192    * {@inheritdoc}
193    */
194   public function getTitle() {
195     return $this->get('title')->value;
196   }
197
198   /**
199    * {@inheritdoc}
200    */
201   public function setTitle($title) {
202     $this->set('title', $title);
203     return $this;
204   }
205
206   /**
207    * {@inheritdoc}
208    */
209   public function getCreatedTime() {
210     return $this->get('created')->value;
211   }
212
213
214   /**
215    * {@inheritdoc}
216    */
217   public function setCreatedTime($timestamp) {
218     $this->set('created', $timestamp);
219     return $this;
220   }
221
222   /**
223    * {@inheritdoc}
224    */
225   public function isPromoted() {
226     return (bool) $this->get('promote')->value;
227   }
228
229   /**
230    * {@inheritdoc}
231    */
232   public function setPromoted($promoted) {
233     $this->set('promote', $promoted ? NodeInterface::PROMOTED : NodeInterface::NOT_PROMOTED);
234     return $this;
235   }
236
237   /**
238    * {@inheritdoc}
239    */
240   public function isSticky() {
241     return (bool) $this->get('sticky')->value;
242   }
243
244   /**
245    * {@inheritdoc}
246    */
247   public function setSticky($sticky) {
248     $this->set('sticky', $sticky ? NodeInterface::STICKY : NodeInterface::NOT_STICKY);
249     return $this;
250   }
251
252   /**
253    * {@inheritdoc}
254    */
255   public function getOwner() {
256     return $this->get('uid')->entity;
257   }
258
259   /**
260    * {@inheritdoc}
261    */
262   public function getOwnerId() {
263     return $this->getEntityKey('uid');
264   }
265
266   /**
267    * {@inheritdoc}
268    */
269   public function setOwnerId($uid) {
270     $this->set('uid', $uid);
271     return $this;
272   }
273
274   /**
275    * {@inheritdoc}
276    */
277   public function setOwner(UserInterface $account) {
278     $this->set('uid', $account->id());
279     return $this;
280   }
281
282   /**
283    * {@inheritdoc}
284    */
285   public function getRevisionAuthor() {
286     return $this->getRevisionUser();
287   }
288
289   /**
290    * {@inheritdoc}
291    */
292   public function setRevisionAuthorId($uid) {
293     $this->setRevisionUserId($uid);
294     return $this;
295   }
296
297   /**
298    * {@inheritdoc}
299    */
300   public static function baseFieldDefinitions(EntityTypeInterface $entity_type) {
301     $fields = parent::baseFieldDefinitions($entity_type);
302
303     $fields['title'] = BaseFieldDefinition::create('string')
304       ->setLabel(t('Title'))
305       ->setRequired(TRUE)
306       ->setTranslatable(TRUE)
307       ->setRevisionable(TRUE)
308       ->setSetting('max_length', 255)
309       ->setDisplayOptions('view', [
310         'label' => 'hidden',
311         'type' => 'string',
312         'weight' => -5,
313       ])
314       ->setDisplayOptions('form', [
315         'type' => 'string_textfield',
316         'weight' => -5,
317       ])
318       ->setDisplayConfigurable('form', TRUE);
319
320     $fields['uid'] = BaseFieldDefinition::create('entity_reference')
321       ->setLabel(t('Authored by'))
322       ->setDescription(t('The username of the content author.'))
323       ->setRevisionable(TRUE)
324       ->setSetting('target_type', 'user')
325       ->setDefaultValueCallback('Drupal\node\Entity\Node::getCurrentUserId')
326       ->setTranslatable(TRUE)
327       ->setDisplayOptions('view', [
328         'label' => 'hidden',
329         'type' => 'author',
330         'weight' => 0,
331       ])
332       ->setDisplayOptions('form', [
333         'type' => 'entity_reference_autocomplete',
334         'weight' => 5,
335         'settings' => [
336           'match_operator' => 'CONTAINS',
337           'size' => '60',
338           'placeholder' => '',
339         ],
340       ])
341       ->setDisplayConfigurable('form', TRUE);
342
343     $fields['status']
344       ->setDisplayOptions('form', [
345         'type' => 'boolean_checkbox',
346         'settings' => [
347           'display_label' => TRUE,
348         ],
349         'weight' => 120,
350       ])
351       ->setDisplayConfigurable('form', TRUE);
352
353     $fields['created'] = BaseFieldDefinition::create('created')
354       ->setLabel(t('Authored on'))
355       ->setDescription(t('The time that the node was created.'))
356       ->setRevisionable(TRUE)
357       ->setTranslatable(TRUE)
358       ->setDisplayOptions('view', [
359         'label' => 'hidden',
360         'type' => 'timestamp',
361         'weight' => 0,
362       ])
363       ->setDisplayOptions('form', [
364         'type' => 'datetime_timestamp',
365         'weight' => 10,
366       ])
367       ->setDisplayConfigurable('form', TRUE);
368
369     $fields['changed'] = BaseFieldDefinition::create('changed')
370       ->setLabel(t('Changed'))
371       ->setDescription(t('The time that the node was last edited.'))
372       ->setRevisionable(TRUE)
373       ->setTranslatable(TRUE);
374
375     $fields['promote'] = BaseFieldDefinition::create('boolean')
376       ->setLabel(t('Promoted to front page'))
377       ->setRevisionable(TRUE)
378       ->setTranslatable(TRUE)
379       ->setDefaultValue(TRUE)
380       ->setDisplayOptions('form', [
381         'type' => 'boolean_checkbox',
382         'settings' => [
383           'display_label' => TRUE,
384         ],
385         'weight' => 15,
386       ])
387       ->setDisplayConfigurable('form', TRUE);
388
389     $fields['sticky'] = BaseFieldDefinition::create('boolean')
390       ->setLabel(t('Sticky at top of lists'))
391       ->setRevisionable(TRUE)
392       ->setTranslatable(TRUE)
393       ->setDefaultValue(FALSE)
394       ->setDisplayOptions('form', [
395         'type' => 'boolean_checkbox',
396         'settings' => [
397           'display_label' => TRUE,
398         ],
399         'weight' => 16,
400       ])
401       ->setDisplayConfigurable('form', TRUE);
402
403     return $fields;
404   }
405
406   /**
407    * Default value callback for 'uid' base field definition.
408    *
409    * @see ::baseFieldDefinitions()
410    *
411    * @return array
412    *   An array of default values.
413    */
414   public static function getCurrentUserId() {
415     return [\Drupal::currentUser()->id()];
416   }
417
418 }