Security update for Core, with self-updated composer
[yaffs-website] / web / core / lib / Drupal / Core / Entity / EntityType.php
1 <?php
2
3 namespace Drupal\Core\Entity;
4
5 use Drupal\Component\Plugin\Definition\PluginDefinition;
6 use Drupal\Component\Utility\Unicode;
7 use Drupal\Core\Entity\Exception\EntityTypeIdLengthException;
8 use Drupal\Core\StringTranslation\StringTranslationTrait;
9 use Drupal\Core\StringTranslation\TranslatableMarkup;
10
11 /**
12  * Provides an implementation of an entity type and its metadata.
13  *
14  * @ingroup entity_api
15  */
16 class EntityType extends PluginDefinition implements EntityTypeInterface {
17
18   use StringTranslationTrait;
19
20   /**
21    * Indicates whether entities should be statically cached.
22    *
23    * @var bool
24    */
25   protected $static_cache = TRUE;
26
27   /**
28    * Indicates whether the rendered output of entities should be cached.
29    *
30    * @var bool
31    */
32   protected $render_cache = TRUE;
33
34   /**
35    * Indicates if the persistent cache of field data should be used.
36    *
37    * @var bool
38    */
39   protected $persistent_cache = TRUE;
40
41   /**
42    * An array of entity keys.
43    *
44    * @var array
45    */
46   protected $entity_keys = [];
47
48   /**
49    * The unique identifier of this entity type.
50    *
51    * @var string
52    */
53   protected $id;
54
55   /**
56    * The name of the original entity type class.
57    *
58    * This is only set if the class name is changed.
59    *
60    * @var string
61    */
62   protected $originalClass;
63
64   /**
65    * An array of handlers.
66    *
67    * @var array
68    */
69   protected $handlers = [];
70
71   /**
72    * The name of the default administrative permission.
73    *
74    * @var string
75    */
76   protected $admin_permission;
77
78   /**
79    * The permission granularity level.
80    *
81    * The allowed values are respectively "entity_type" or "bundle".
82    *
83    * @var string
84    */
85   protected $permission_granularity = 'entity_type';
86   /**
87    * Link templates using the URI template syntax.
88    *
89    * @var array
90    */
91   protected $links = [];
92
93   /**
94    * The name of a callback that returns the label of the entity.
95    *
96    * @var callable|null
97    *
98    * @deprecated in Drupal 8.0.x-dev and will be removed before Drupal 9.0.0.
99    *   Use Drupal\Core\Entity\EntityInterface::label() for complex label
100    *   generation as needed.
101    *
102    * @see \Drupal\Core\Entity\EntityInterface::label()
103    *
104    * @todo Remove usages of label_callback https://www.drupal.org/node/2450793.
105    */
106   protected $label_callback = NULL;
107
108   /**
109    * The name of the entity type which provides bundles.
110    *
111    * @var string
112    */
113   protected $bundle_entity_type = NULL;
114
115   /**
116    * The name of the entity type for which bundles are provided.
117    *
118    * @var string|null
119    */
120   protected $bundle_of = NULL;
121
122   /**
123    * The human-readable name of the entity bundles, e.g. Vocabulary.
124    *
125    * @var string|null
126    */
127   protected $bundle_label = NULL;
128
129   /**
130    * The name of the entity type's base table.
131    *
132    * @var string|null
133    */
134   protected $base_table = NULL;
135
136   /**
137    * The name of the entity type's revision data table.
138    *
139    * @var string|null
140    */
141   protected $revision_data_table = NULL;
142
143   /**
144    * The name of the entity type's revision table.
145    *
146    * @var string|null
147    */
148   protected $revision_table = NULL;
149
150   /**
151    * The name of the entity type's data table.
152    *
153    * @var string|null
154    */
155   protected $data_table = NULL;
156
157   /**
158    * Indicates whether entities of this type have multilingual support.
159    *
160    * @var bool
161    */
162   protected $translatable = FALSE;
163
164   /**
165    * Indicates whether the revision form fields should be added to the form.
166    *
167    * @var bool
168    */
169   protected $show_revision_ui = FALSE;
170
171   /**
172    * The human-readable name of the type.
173    *
174    * @var string
175    *
176    * @see \Drupal\Core\Entity\EntityTypeInterface::getLabel()
177    */
178   protected $label = '';
179
180   /**
181    * The human-readable label for a collection of entities of the type.
182    *
183    * @var string
184    *
185    * @see \Drupal\Core\Entity\EntityTypeInterface::getCollectionLabel()
186    */
187   protected $label_collection = '';
188
189   /**
190    * The indefinite singular name of the type.
191    *
192    * @var string
193    *
194    * @see \Drupal\Core\Entity\EntityTypeInterface::getSingularLabel()
195    */
196   protected $label_singular = '';
197
198   /**
199    * The indefinite plural name of the type.
200    *
201    * @var string
202    *
203    * @see \Drupal\Core\Entity\EntityTypeInterface::getPluralLabel()
204    */
205   protected $label_plural = '';
206
207   /**
208    * A definite singular/plural name of the type.
209    *
210    * Needed keys: "singular" and "plural".
211    *
212    * @var string[]
213    *
214    * @see \Drupal\Core\Entity\EntityTypeInterface::getCountLabel()
215    */
216   protected $label_count = [];
217
218   /**
219    * A callable that can be used to provide the entity URI.
220    *
221    * @var callable|null
222    */
223   protected $uri_callback = NULL;
224
225   /**
226    * The machine name of the entity type group.
227    */
228   protected $group;
229
230   /**
231    * The human-readable name of the entity type group.
232    */
233   protected $group_label;
234
235   /**
236    * The route name used by field UI to attach its management pages.
237    *
238    * @var string
239    */
240   protected $field_ui_base_route;
241
242   /**
243    * Indicates whether this entity type is commonly used as a reference target.
244    *
245    * This is used by the Entity reference field to promote an entity type in the
246    * add new field select list in Field UI.
247    *
248    * @var bool
249    */
250   protected $common_reference_target = FALSE;
251
252   /**
253    * The list cache contexts for this entity type.
254    *
255    * @var string[]
256    */
257   protected $list_cache_contexts = [];
258
259   /**
260    * The list cache tags for this entity type.
261    *
262    * @var string[]
263    */
264   protected $list_cache_tags = [];
265
266   /**
267    * Entity constraint definitions.
268    *
269    * @var array[]
270    */
271   protected $constraints = [];
272
273   /**
274    * Any additional properties and values.
275    *
276    * @var array
277    */
278   protected $additional = [];
279
280   /**
281    * Constructs a new EntityType.
282    *
283    * @param array $definition
284    *   An array of values from the annotation.
285    *
286    * @throws \Drupal\Core\Entity\Exception\EntityTypeIdLengthException
287    *   Thrown when attempting to instantiate an entity type with too long ID.
288    */
289   public function __construct($definition) {
290     // Throw an exception if the entity type ID is longer than 32 characters.
291     if (Unicode::strlen($definition['id']) > static::ID_MAX_LENGTH) {
292       throw new EntityTypeIdLengthException('Attempt to create an entity type with an ID longer than ' . static::ID_MAX_LENGTH . " characters: {$definition['id']}.");
293     }
294
295     foreach ($definition as $property => $value) {
296       $this->set($property, $value);
297     }
298
299     // Ensure defaults.
300     $this->entity_keys += [
301       'revision' => '',
302       'bundle' => '',
303       'langcode' => '',
304       'default_langcode' => 'default_langcode',
305       'revision_translation_affected' => 'revision_translation_affected',
306     ];
307     $this->handlers += [
308       'access' => 'Drupal\Core\Entity\EntityAccessControlHandler',
309     ];
310     if (isset($this->handlers['storage'])) {
311       $this->checkStorageClass($this->handlers['storage']);
312     }
313
314     // Automatically add the EntityChanged constraint if the entity type tracks
315     // the changed time.
316     if ($this->entityClassImplements(EntityChangedInterface::class)) {
317       $this->addConstraint('EntityChanged');
318     }
319
320     // Ensure a default list cache tag is set.
321     if (empty($this->list_cache_tags)) {
322       $this->list_cache_tags = [$definition['id'] . '_list'];
323     }
324   }
325
326   /**
327    * {@inheritdoc}
328    */
329   public function get($property) {
330     if (property_exists($this, $property)) {
331       $value = isset($this->{$property}) ? $this->{$property} : NULL;
332     }
333     else {
334       $value = isset($this->additional[$property]) ? $this->additional[$property] : NULL;
335     }
336     return $value;
337   }
338
339   /**
340    * {@inheritdoc}
341    */
342   public function set($property, $value) {
343     if (property_exists($this, $property)) {
344       $this->{$property} = $value;
345     }
346     else {
347       $this->additional[$property] = $value;
348     }
349     return $this;
350   }
351
352   /**
353    * {@inheritdoc}
354    */
355   public function isStaticallyCacheable() {
356     return $this->static_cache;
357   }
358
359   /**
360    * {@inheritdoc}
361    */
362   public function isRenderCacheable() {
363     return $this->render_cache;
364   }
365
366   /**
367    * {@inheritdoc}
368    */
369   public function isPersistentlyCacheable() {
370     return $this->persistent_cache;
371   }
372
373   /**
374    * {@inheritdoc}
375    */
376   public function getKeys() {
377     return $this->entity_keys;
378   }
379
380   /**
381    * {@inheritdoc}
382    */
383   public function getKey($key) {
384     $keys = $this->getKeys();
385     return isset($keys[$key]) ? $keys[$key] : FALSE;
386   }
387
388   /**
389    * {@inheritdoc}
390    */
391   public function hasKey($key) {
392     $keys = $this->getKeys();
393     return !empty($keys[$key]);
394   }
395
396   /**
397    * {@inheritdoc}
398    */
399   public function getOriginalClass() {
400     return $this->originalClass ?: $this->class;
401   }
402
403   /**
404    * {@inheritdoc}
405    */
406   public function setClass($class) {
407     if (!$this->originalClass && $this->class) {
408       // If the original class is currently not set, set it to the current
409       // class, assume that is the original class name.
410       $this->originalClass = $this->class;
411     }
412
413     return parent::setClass($class);
414   }
415
416   /**
417    * {@inheritdoc}
418    */
419   public function entityClassImplements($interface) {
420     return is_subclass_of($this->getClass(), $interface);
421   }
422
423   /**
424    * {@inheritdoc}
425    */
426   public function isSubclassOf($class) {
427     return $this->entityClassImplements($class);
428   }
429
430   /**
431    * {@inheritdoc}
432    */
433   public function getHandlerClasses() {
434     return $this->handlers;
435   }
436
437   /**
438    * {@inheritdoc}
439    */
440   public function getHandlerClass($handler_type, $nested = FALSE) {
441     if ($this->hasHandlerClass($handler_type, $nested)) {
442       $handlers = $this->getHandlerClasses();
443       return $nested ? $handlers[$handler_type][$nested] : $handlers[$handler_type];
444     }
445   }
446
447   /**
448    * {@inheritdoc}
449    */
450   public function setHandlerClass($handler_type, $value) {
451     $this->handlers[$handler_type] = $value;
452     return $this;
453   }
454
455   /**
456    * {@inheritdoc}
457    */
458   public function hasHandlerClass($handler_type, $nested = FALSE) {
459     $handlers = $this->getHandlerClasses();
460     if (!isset($handlers[$handler_type]) || ($nested && !isset($handlers[$handler_type][$nested]))) {
461       return FALSE;
462     }
463     $handler = $handlers[$handler_type];
464     if ($nested) {
465       $handler = $handler[$nested];
466     }
467     return class_exists($handler);
468   }
469
470   /**
471    * {@inheritdoc}
472    */
473   public function getStorageClass() {
474     return $this->getHandlerClass('storage');
475   }
476
477   /**
478    * {@inheritdoc}
479    */
480   public function setStorageClass($class) {
481     $this->checkStorageClass($class);
482     $this->handlers['storage'] = $class;
483     return $this;
484   }
485
486   /**
487    * Checks that the provided class is compatible with the current entity type.
488    *
489    * @param string $class
490    *   The class to check.
491    */
492   protected function checkStorageClass($class) {
493     // Nothing to check by default.
494   }
495
496   /**
497    * {@inheritdoc}
498    */
499   public function getFormClass($operation) {
500     return $this->getHandlerClass('form', $operation);
501   }
502
503   /**
504    * {@inheritdoc}
505    */
506   public function setFormClass($operation, $class) {
507     $this->handlers['form'][$operation] = $class;
508     return $this;
509   }
510
511   /**
512    * {@inheritdoc}
513    */
514   public function hasFormClasses() {
515     return !empty($this->handlers['form']);
516   }
517
518   /**
519    * {@inheritdoc}
520    */
521   public function hasRouteProviders() {
522     return !empty($this->handlers['route_provider']);
523   }
524
525   /**
526    * {@inheritdoc}
527    */
528   public function getListBuilderClass() {
529     return $this->getHandlerClass('list_builder');
530   }
531
532   /**
533    * {@inheritdoc}
534    */
535   public function setListBuilderClass($class) {
536     $this->handlers['list_builder'] = $class;
537     return $this;
538   }
539
540   /**
541    * {@inheritdoc}
542    */
543   public function hasListBuilderClass() {
544     return $this->hasHandlerClass('list_builder');
545   }
546
547   /**
548    * {@inheritdoc}
549    */
550   public function getViewBuilderClass() {
551     return $this->getHandlerClass('view_builder');
552   }
553
554   /**
555    * {@inheritdoc}
556    */
557   public function setViewBuilderClass($class) {
558     $this->handlers['view_builder'] = $class;
559     return $this;
560   }
561
562   /**
563    * {@inheritdoc}
564    */
565   public function hasViewBuilderClass() {
566     return $this->hasHandlerClass('view_builder');
567   }
568
569   /**
570    * {@inheritdoc}
571    */
572   public function getRouteProviderClasses() {
573     return !empty($this->handlers['route_provider']) ? $this->handlers['route_provider'] : [];
574   }
575
576   /**
577    * {@inheritdoc}
578    */
579   public function getAccessControlClass() {
580     return $this->getHandlerClass('access');
581   }
582
583   /**
584    * {@inheritdoc}
585    */
586   public function setAccessClass($class) {
587     $this->handlers['access'] = $class;
588     return $this;
589   }
590
591   /**
592    * {@inheritdoc}
593    */
594   public function getAdminPermission() {
595     return $this->admin_permission ?: FALSE;
596   }
597
598   /**
599    * {@inheritdoc}
600    */
601   public function getPermissionGranularity() {
602     return $this->permission_granularity;
603   }
604
605   /**
606    * {@inheritdoc}
607    */
608   public function getLinkTemplates() {
609     return $this->links;
610   }
611
612   /**
613    * {@inheritdoc}
614    */
615   public function getLinkTemplate($key) {
616     $links = $this->getLinkTemplates();
617     return isset($links[$key]) ? $links[$key] : FALSE;
618   }
619
620   /**
621    * {@inheritdoc}
622    */
623   public function hasLinkTemplate($key) {
624     $links = $this->getLinkTemplates();
625     return isset($links[$key]);
626   }
627
628   /**
629    * {@inheritdoc}
630    */
631   public function setLinkTemplate($key, $path) {
632     if ($path[0] !== '/') {
633       throw new \InvalidArgumentException('Link templates accepts paths, which have to start with a leading slash.');
634     }
635
636     $this->links[$key] = $path;
637     return $this;
638   }
639
640   /**
641    * {@inheritdoc}
642    */
643   public function getLabelCallback() {
644     return $this->label_callback;
645   }
646
647   /**
648    * {@inheritdoc}
649    */
650   public function setLabelCallback($callback) {
651     $this->label_callback = $callback;
652     return $this;
653   }
654
655   /**
656    * {@inheritdoc}
657    */
658   public function hasLabelCallback() {
659     return isset($this->label_callback);
660   }
661
662   /**
663    * {@inheritdoc}
664    */
665   public function getBundleEntityType() {
666     return $this->bundle_entity_type;
667   }
668
669   /**
670    * {@inheritdoc}
671    */
672   public function getBundleOf() {
673     return $this->bundle_of;
674   }
675
676   /**
677    * {@inheritdoc}
678    */
679   public function getBundleLabel() {
680     return (string) $this->bundle_label;
681   }
682
683   /**
684    * {@inheritdoc}
685    */
686   public function getBaseTable() {
687     return $this->base_table;
688   }
689
690   /**
691    * {@inheritdoc}
692    */
693   public function showRevisionUi() {
694     return $this->isRevisionable() && $this->show_revision_ui;
695   }
696
697   /**
698    * {@inheritdoc}
699    */
700   public function isTranslatable() {
701     return !empty($this->translatable);
702   }
703
704   /**
705    * {@inheritdoc}
706    */
707   public function isRevisionable() {
708     // Entity types are revisionable if a revision key has been specified.
709     return $this->hasKey('revision');
710   }
711
712   /**
713    * {@inheritdoc}
714    */
715   public function getRevisionDataTable() {
716     return $this->revision_data_table;
717   }
718
719   /**
720    * {@inheritdoc}
721    */
722   public function getRevisionTable() {
723     return $this->revision_table;
724   }
725
726   /**
727    * {@inheritdoc}
728    */
729   public function getDataTable() {
730     return $this->data_table;
731   }
732
733   /**
734    * {@inheritdoc}
735    */
736   public function getLabel() {
737     return $this->label;
738   }
739
740   /**
741    * {@inheritdoc}
742    */
743   public function getLowercaseLabel() {
744     return Unicode::strtolower($this->getLabel());
745   }
746
747   /**
748    * {@inheritdoc}
749    */
750   public function getCollectionLabel() {
751     if (empty($this->label_collection)) {
752       $label = $this->getLabel();
753       $this->label_collection = new TranslatableMarkup('@label entities', ['@label' => $label], [], $this->getStringTranslation());
754     }
755     return $this->label_collection;
756   }
757
758   /**
759    * {@inheritdoc}
760    */
761   public function getSingularLabel() {
762     if (empty($this->label_singular)) {
763       $lowercase_label = $this->getLowercaseLabel();
764       $this->label_singular = $lowercase_label;
765     }
766     return $this->label_singular;
767   }
768
769   /**
770    * {@inheritdoc}
771    */
772   public function getPluralLabel() {
773     if (empty($this->label_plural)) {
774       $lowercase_label = $this->getLowercaseLabel();
775       $this->label_plural = new TranslatableMarkup('@label entities', ['@label' => $lowercase_label], [], $this->getStringTranslation());
776     }
777     return $this->label_plural;
778   }
779
780   /**
781    * {@inheritdoc}
782    */
783   public function getCountLabel($count) {
784     if (empty($this->label_count)) {
785       return $this->formatPlural($count, '@count @label', '@count @label entities', ['@label' => $this->getLowercaseLabel()], ['context' => 'Entity type label']);
786     }
787     $context = isset($this->label_count['context']) ? $this->label_count['context'] : 'Entity type label';
788     return $this->formatPlural($count, $this->label_count['singular'], $this->label_count['plural'], ['context' => $context]);
789   }
790
791   /**
792    * {@inheritdoc}
793    */
794   public function getUriCallback() {
795     return $this->uri_callback;
796   }
797
798   /**
799    * {@inheritdoc}
800    */
801   public function setUriCallback($callback) {
802     $this->uri_callback = $callback;
803     return $this;
804   }
805
806   /**
807    * {@inheritdoc}
808    */
809   public function getGroup() {
810     return $this->group;
811   }
812
813
814   /**
815    * {@inheritdoc}
816    */
817   public function getGroupLabel() {
818     return !empty($this->group_label) ? $this->group_label : $this->t('Other', [], ['context' => 'Entity type group']);
819   }
820
821   /**
822    * {@inheritdoc}
823    */
824   public function getListCacheContexts() {
825     return $this->list_cache_contexts;
826   }
827
828   /**
829    * {@inheritdoc}
830    */
831   public function getListCacheTags() {
832     return $this->list_cache_tags;
833   }
834
835   /**
836    * {@inheritdoc}
837    */
838   public function getConfigDependencyKey() {
839     // Return 'content' for the default implementation as important distinction
840     // is that dependencies on other configuration entities are hard
841     // dependencies and have to exist before creating the dependent entity.
842     return 'content';
843   }
844
845   /**
846    * {@inheritdoc}
847    */
848   public function isCommonReferenceTarget() {
849     return $this->common_reference_target;
850   }
851
852   /**
853    * {@inheritdoc}
854    */
855   public function getConstraints() {
856     return $this->constraints;
857   }
858
859   /**
860    * {@inheritdoc}
861    */
862   public function setConstraints(array $constraints) {
863     $this->constraints = $constraints;
864     return $this;
865   }
866
867   /**
868    * {@inheritdoc}
869    */
870   public function addConstraint($constraint_name, $options = NULL) {
871     $this->constraints[$constraint_name] = $options;
872     return $this;
873   }
874
875   /**
876    * {@inheritdoc}
877    */
878   public function getBundleConfigDependency($bundle) {
879     // If this entity type uses entities to manage its bundles then depend on
880     // the bundle entity.
881     if ($bundle_entity_type_id = $this->getBundleEntityType()) {
882       if (!$bundle_entity = \Drupal::entityManager()->getStorage($bundle_entity_type_id)->load($bundle)) {
883         throw new \LogicException(sprintf('Missing bundle entity, entity type %s, entity id %s.', $bundle_entity_type_id, $bundle));
884       }
885       $config_dependency = [
886         'type' => 'config',
887         'name' => $bundle_entity->getConfigDependencyName(),
888       ];
889     }
890     else {
891       // Depend on the provider of the entity type.
892       $config_dependency = [
893         'type' => 'module',
894         'name' => $this->getProvider(),
895       ];
896     }
897
898     return $config_dependency;
899   }
900
901 }