Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
| Total | |
0.00% |
0 / 1 |
|
5.41% |
2 / 37 |
CRAP | |
15.71% |
33 / 210 |
| Comment | |
0.00% |
0 / 1 |
|
5.41% |
2 / 37 |
2439.53 | |
15.71% |
33 / 210 |
| preSave | |
0.00% |
0 / 1 |
15.10 | |
67.65% |
23 / 34 |
|||
| postSave | |
100.00% |
1 / 1 |
2 | |
100.00% |
6 / 6 |
|||
| releaseThreadLock | |
100.00% |
1 / 1 |
2 | |
100.00% |
4 / 4 |
|||
| postDelete | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 6 |
|||
| referencedEntities | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
| permalink | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 4 |
|||
| baseFieldDefinitions | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 90 |
|||
| bundleFieldDefinitions | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 5 |
|||
| hasParentComment | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getParentComment | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getCommentedEntity | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getCommentedEntityId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getCommentedEntityTypeId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| setFieldName | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| getFieldName | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getSubject | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| setSubject | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| getAuthorName | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 3 |
|||
| setAuthorName | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| getAuthorEmail | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
| getHomepage | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| setHomepage | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| getHostname | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| setHostname | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| getCreatedTime | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 3 |
|||
| setCreatedTime | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| isPublished | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| getStatus | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| setPublished | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 2 |
|||
| getThread | |
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 4 |
|||
| setThread | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| preCreate | |
0.00% |
0 / 1 |
20 | |
0.00% |
0 / 4 |
|||
| getOwner | |
0.00% |
0 / 1 |
12 | |
0.00% |
0 / 6 |
|||
| getOwnerId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| setOwnerId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| setOwner | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 2 |
|||
| getTypeId | |
0.00% |
0 / 1 |
2 | |
0.00% |
0 / 1 |
|||
| <?php | |
| /** | |
| * @file | |
| * Contains \Drupal\comment\Entity\Comment. | |
| */ | |
| namespace Drupal\comment\Entity; | |
| use Drupal\Component\Utility\Number; | |
| use Drupal\Core\Cache\Cache; | |
| use Drupal\Core\Entity\ContentEntityBase; | |
| use Drupal\comment\CommentInterface; | |
| use Drupal\Core\Entity\EntityChangedTrait; | |
| use Drupal\Core\Entity\EntityStorageInterface; | |
| use Drupal\Core\Entity\EntityTypeInterface; | |
| use Drupal\Core\Field\BaseFieldDefinition; | |
| use Drupal\field\Entity\FieldStorageConfig; | |
| use Drupal\user\Entity\User; | |
| use Drupal\user\UserInterface; | |
| /** | |
| * Defines the comment entity class. | |
| * | |
| * @ContentEntityType( | |
| * id = "comment", | |
| * label = @Translation("Comment"), | |
| * bundle_label = @Translation("Content type"), | |
| * handlers = { | |
| * "storage" = "Drupal\comment\CommentStorage", | |
| * "storage_schema" = "Drupal\comment\CommentStorageSchema", | |
| * "access" = "Drupal\comment\CommentAccessControlHandler", | |
| * "list_builder" = "Drupal\Core\Entity\EntityListBuilder", | |
| * "view_builder" = "Drupal\comment\CommentViewBuilder", | |
| * "views_data" = "Drupal\comment\CommentViewsData", | |
| * "form" = { | |
| * "default" = "Drupal\comment\CommentForm", | |
| * "delete" = "Drupal\comment\Form\DeleteForm" | |
| * }, | |
| * "translation" = "Drupal\comment\CommentTranslationHandler" | |
| * }, | |
| * base_table = "comment", | |
| * data_table = "comment_field_data", | |
| * uri_callback = "comment_uri", | |
| * translatable = TRUE, | |
| * entity_keys = { | |
| * "id" = "cid", | |
| * "bundle" = "comment_type", | |
| * "label" = "subject", | |
| * "langcode" = "langcode", | |
| * "uuid" = "uuid" | |
| * }, | |
| * links = { | |
| * "canonical" = "/comment/{comment}", | |
| * "delete-form" = "/comment/{comment}/delete", | |
| * "edit-form" = "/comment/{comment}/edit", | |
| * }, | |
| * bundle_entity_type = "comment_type", | |
| * field_ui_base_route = "entity.comment_type.edit_form", | |
| * constraints = { | |
| * "CommentName" = {} | |
| * } | |
| * ) | |
| */ | |
| class Comment extends ContentEntityBase implements CommentInterface { | |
| use EntityChangedTrait; | |
| /** | |
| * The thread for which a lock was acquired. | |
| */ | |
| protected $threadLock = ''; | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function preSave(EntityStorageInterface $storage) { | |
| parent::preSave($storage); | |
| if (is_null($this->get('status')->value)) { | |
| $published = \Drupal::currentUser()->hasPermission('skip comment approval') ? CommentInterface::PUBLISHED : CommentInterface::NOT_PUBLISHED; | |
| $this->setPublished($published); | |
| } | |
| if ($this->isNew()) { | |
| // Add the comment to database. This next section builds the thread field. | |
| // @see \Drupal\comment\CommentViewBuilder::buildComponents() | |
| $thread = $this->getThread(); | |
| if (empty($thread)) { | |
| if ($this->threadLock) { | |
| // Thread lock was not released after being set previously. | |
| // This suggests there's a bug in code using this class. | |
| throw new \LogicException('preSave() is called again without calling postSave() or releaseThreadLock()'); | |
| } | |
| if (!$this->hasParentComment()) { | |
| // This is a comment with no parent comment (depth 0): we start | |
| // by retrieving the maximum thread level. | |
| $max = $storage->getMaxThread($this); | |
| // Strip the "/" from the end of the thread. | |
| $max = rtrim($max, '/'); | |
| // We need to get the value at the correct depth. | |
| $parts = explode('.', $max); | |
| $n = Number::alphadecimalToInt($parts[0]); | |
| $prefix = ''; | |
| } | |
| else { | |
| // This is a comment with a parent comment, so increase the part of | |
| // the thread value at the proper depth. | |
| // Get the parent comment: | |
| $parent = $this->getParentComment(); | |
| // Strip the "/" from the end of the parent thread. | |
| $parent->setThread((string) rtrim((string) $parent->getThread(), '/')); | |
| $prefix = $parent->getThread() . '.'; | |
| // Get the max value in *this* thread. | |
| $max = $storage->getMaxThreadPerThread($this); | |
| if ($max == '') { | |
| // First child of this parent. As the other two cases do an | |
| // increment of the thread number before creating the thread | |
| // string set this to -1 so it requires an increment too. | |
| $n = -1; | |
| } | |
| else { | |
| // Strip the "/" at the end of the thread. | |
| $max = rtrim($max, '/'); | |
| // Get the value at the correct depth. | |
| $parts = explode('.', $max); | |
| $parent_depth = count(explode('.', $parent->getThread())); | |
| $n = Number::alphadecimalToInt($parts[$parent_depth]); | |
| } | |
| } | |
| // Finally, build the thread field for this new comment. To avoid | |
| // race conditions, get a lock on the thread. If another process already | |
| // has the lock, just move to the next integer. | |
| do { | |
| $thread = $prefix . Number::intToAlphadecimal(++$n) . '/'; | |
| $lock_name = "comment:{$this->getCommentedEntityId()}:$thread"; | |
| } while (!\Drupal::lock()->acquire($lock_name)); | |
| $this->threadLock = $lock_name; | |
| } | |
| // We test the value with '===' because we need to modify anonymous | |
| // users as well. | |
| if ($this->getOwnerId() === \Drupal::currentUser()->id() && \Drupal::currentUser()->isAuthenticated()) { | |
| $this->setAuthorName(\Drupal::currentUser()->getUsername()); | |
| } | |
| // Add the values which aren't passed into the function. | |
| $this->setThread($thread); | |
| $this->setHostname(\Drupal::request()->getClientIP()); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function postSave(EntityStorageInterface $storage, $update = TRUE) { | |
| parent::postSave($storage, $update); | |
| // Always invalidate the cache tag for the commented entity. | |
| if ($commented_entity = $this->getCommentedEntity()) { | |
| Cache::invalidateTags($commented_entity->getCacheTagsToInvalidate()); | |
| } | |
| $this->releaseThreadLock(); | |
| // Update the {comment_entity_statistics} table prior to executing the hook. | |
| \Drupal::service('comment.statistics')->update($this); | |
| } | |
| /** | |
| * Release the lock acquired for the thread in preSave(). | |
| */ | |
| protected function releaseThreadLock() { | |
| if ($this->threadLock) { | |
| \Drupal::lock()->release($this->threadLock); | |
| $this->threadLock = ''; | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function postDelete(EntityStorageInterface $storage, array $entities) { | |
| parent::postDelete($storage, $entities); | |
| $child_cids = $storage->getChildCids($entities); | |
| entity_delete_multiple('comment', $child_cids); | |
| foreach ($entities as $id => $entity) { | |
| \Drupal::service('comment.statistics')->update($entity); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function referencedEntities() { | |
| $referenced_entities = parent::referencedEntities(); | |
| if ($this->getCommentedEntityId()) { | |
| $referenced_entities[] = $this->getCommentedEntity(); | |
| } | |
| return $referenced_entities; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function permalink() { | |
| $entity = $this->getCommentedEntity(); | |
| $uri = $entity->urlInfo(); | |
| $uri->setOption('fragment', 'comment-' . $this->id()); | |
| return $uri; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { | |
| $fields['cid'] = BaseFieldDefinition::create('integer') | |
| ->setLabel(t('Comment ID')) | |
| ->setDescription(t('The comment ID.')) | |
| ->setReadOnly(TRUE) | |
| ->setSetting('unsigned', TRUE); | |
| $fields['uuid'] = BaseFieldDefinition::create('uuid') | |
| ->setLabel(t('UUID')) | |
| ->setDescription(t('The comment UUID.')) | |
| ->setReadOnly(TRUE); | |
| $fields['pid'] = BaseFieldDefinition::create('entity_reference') | |
| ->setLabel(t('Parent ID')) | |
| ->setDescription(t('The parent comment ID if this is a reply to a comment.')) | |
| ->setSetting('target_type', 'comment'); | |
| $fields['entity_id'] = BaseFieldDefinition::create('entity_reference') | |
| ->setLabel(t('Entity ID')) | |
| ->setDescription(t('The ID of the entity of which this comment is a reply.')) | |
| ->setRequired(TRUE); | |
| $fields['langcode'] = BaseFieldDefinition::create('language') | |
| ->setLabel(t('Language')) | |
| ->setDescription(t('The comment language code.')) | |
| ->setTranslatable(TRUE) | |
| ->setDisplayOptions('view', array( | |
| 'type' => 'hidden', | |
| )) | |
| ->setDisplayOptions('form', array( | |
| 'type' => 'language_select', | |
| 'weight' => 2, | |
| )); | |
| $fields['subject'] = BaseFieldDefinition::create('string') | |
| ->setLabel(t('Subject')) | |
| ->setTranslatable(TRUE) | |
| ->setSetting('max_length', 64) | |
| ->setDisplayOptions('form', array( | |
| 'type' => 'string_textfield', | |
| // Default comment body field has weight 20. | |
| 'weight' => 10, | |
| )) | |
| ->setDisplayConfigurable('form', TRUE); | |
| $fields['uid'] = BaseFieldDefinition::create('entity_reference') | |
| ->setLabel(t('User ID')) | |
| ->setDescription(t('The user ID of the comment author.')) | |
| ->setTranslatable(TRUE) | |
| ->setSetting('target_type', 'user') | |
| ->setDefaultValue(0); | |
| $fields['name'] = BaseFieldDefinition::create('string') | |
| ->setLabel(t('Name')) | |
| ->setDescription(t("The comment author's name.")) | |
| ->setTranslatable(TRUE) | |
| ->setSetting('max_length', 60) | |
| ->setDefaultValue(''); | |
| $fields['mail'] = BaseFieldDefinition::create('email') | |
| ->setLabel(t('Email')) | |
| ->setDescription(t("The comment author's email address.")) | |
| ->setTranslatable(TRUE); | |
| $fields['homepage'] = BaseFieldDefinition::create('uri') | |
| ->setLabel(t('Homepage')) | |
| ->setDescription(t("The comment author's home page address.")) | |
| ->setTranslatable(TRUE) | |
| // URIs are not length limited by RFC 2616, but we can only store 255 | |
| // characters in our comment DB schema. | |
| ->setSetting('max_length', 255); | |
| $fields['hostname'] = BaseFieldDefinition::create('string') | |
| ->setLabel(t('Hostname')) | |
| ->setDescription(t("The comment author's hostname.")) | |
| ->setTranslatable(TRUE) | |
| ->setSetting('max_length', 128); | |
| $fields['created'] = BaseFieldDefinition::create('created') | |
| ->setLabel(t('Created')) | |
| ->setDescription(t('The time that the comment was created.')) | |
| ->setTranslatable(TRUE); | |
| $fields['changed'] = BaseFieldDefinition::create('changed') | |
| ->setLabel(t('Changed')) | |
| ->setDescription(t('The time that the comment was last edited.')) | |
| ->setTranslatable(TRUE); | |
| $fields['status'] = BaseFieldDefinition::create('boolean') | |
| ->setLabel(t('Publishing status')) | |
| ->setDescription(t('A boolean indicating whether the comment is published.')) | |
| ->setTranslatable(TRUE) | |
| ->setDefaultValue(TRUE); | |
| $fields['thread'] = BaseFieldDefinition::create('string') | |
| ->setLabel(t('Thread place')) | |
| ->setDescription(t("The alphadecimal representation of the comment's place in a thread, consisting of a base 36 string prefixed by an integer indicating its length.")) | |
| ->setSetting('max_length', 255); | |
| $fields['entity_type'] = BaseFieldDefinition::create('string') | |
| ->setLabel(t('Entity type')) | |
| ->setDescription(t('The entity type to which this comment is attached.')) | |
| ->setSetting('is_ascii', TRUE) | |
| ->setSetting('max_length', EntityTypeInterface::ID_MAX_LENGTH); | |
| $fields['comment_type'] = BaseFieldDefinition::create('entity_reference') | |
| ->setLabel(t('Comment Type')) | |
| ->setDescription(t('The comment type.')) | |
| ->setSetting('target_type', 'comment_type'); | |
| $fields['field_name'] = BaseFieldDefinition::create('string') | |
| ->setLabel(t('Comment field name')) | |
| ->setDescription(t('The field name through which this comment was added.')) | |
| ->setSetting('is_ascii', TRUE) | |
| ->setSetting('max_length', FieldStorageConfig::NAME_MAX_LENGTH); | |
| return $fields; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function bundleFieldDefinitions(EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { | |
| if ($comment_type = CommentType::load($bundle)) { | |
| $fields['entity_id'] = clone $base_field_definitions['entity_id']; | |
| $fields['entity_id']->setSetting('target_type', $comment_type->getTargetEntityTypeId()); | |
| return $fields; | |
| } | |
| return array(); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function hasParentComment() { | |
| return (bool) $this->get('pid')->target_id; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getParentComment() { | |
| return $this->get('pid')->entity; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getCommentedEntity() { | |
| return $this->get('entity_id')->entity; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getCommentedEntityId() { | |
| return $this->get('entity_id')->target_id; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getCommentedEntityTypeId() { | |
| return $this->get('entity_type')->value; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setFieldName($field_name) { | |
| $this->set('field_name', $field_name); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getFieldName() { | |
| return $this->get('field_name')->value; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getSubject() { | |
| return $this->get('subject')->value; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setSubject($subject) { | |
| $this->set('subject', $subject); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getAuthorName() { | |
| if ($this->get('uid')->target_id) { | |
| return $this->get('uid')->entity->label(); | |
| } | |
| return $this->get('name')->value ?: \Drupal::config('user.settings')->get('anonymous'); | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setAuthorName($name) { | |
| $this->set('name', $name); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getAuthorEmail() { | |
| $mail = $this->get('mail')->value; | |
| if ($this->get('uid')->target_id != 0) { | |
| $mail = $this->get('uid')->entity->getEmail(); | |
| } | |
| return $mail; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getHomepage() { | |
| return $this->get('homepage')->value; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setHomepage($homepage) { | |
| $this->set('homepage', $homepage); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getHostname() { | |
| return $this->get('hostname')->value; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setHostname($hostname) { | |
| $this->set('hostname', $hostname); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getCreatedTime() { | |
| if (isset($this->get('created')->value)) { | |
| return $this->get('created')->value; | |
| } | |
| return NULL; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setCreatedTime($created) { | |
| $this->set('created', $created); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function isPublished() { | |
| return $this->get('status')->value == CommentInterface::PUBLISHED; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getStatus() { | |
| return $this->get('status')->value; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setPublished($status) { | |
| $this->set('status', $status ? CommentInterface::PUBLISHED : CommentInterface::NOT_PUBLISHED); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getThread() { | |
| $thread = $this->get('thread'); | |
| if (!empty($thread->value)) { | |
| return $thread->value; | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setThread($thread) { | |
| $this->set('thread', $thread); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public static function preCreate(EntityStorageInterface $storage, array &$values) { | |
| if (empty($values['comment_type']) && !empty($values['field_name']) && !empty($values['entity_type'])) { | |
| $field_storage = FieldStorageConfig::loadByName($values['entity_type'], $values['field_name']); | |
| $values['comment_type'] = $field_storage->getSetting('comment_type'); | |
| } | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getOwner() { | |
| $user = $this->get('uid')->entity; | |
| if (!$user || $user->isAnonymous()) { | |
| $user = User::getAnonymousUser(); | |
| $user->name = $this->getAuthorName(); | |
| $user->homepage = $this->getHomepage(); | |
| } | |
| return $user; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function getOwnerId() { | |
| return $this->get('uid')->target_id; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setOwnerId($uid) { | |
| $this->set('uid', $uid); | |
| return $this; | |
| } | |
| /** | |
| * {@inheritdoc} | |
| */ | |
| public function setOwner(UserInterface $account) { | |
| $this->set('uid', $account->id()); | |
| return $this; | |
| } | |
| /** | |
| * Get the comment type ID for this comment. | |
| * | |
| * @return string | |
| * The ID of the comment type. | |
| */ | |
| public function getTypeId() { | |
| return $this->bundle(); | |
| } | |
| } |