protected function checkAccess(ContentEntityInterface $entity, AccountInterface $account, $operation = 'view') { $entity_type = $entity->getEntityType(); $entity_type_id = $entity->getEntityTypeId(); $entity_access = $this->entityTypeManager->getAccessControlHandler($entity_type_id); /** @var \Drupal\Core\Entity\EntityStorageInterface $entity_storage */ $entity_storage = $this->entityTypeManager->getStorage($entity_type_id); $map = ['view' => "view all {$entity_type_id} revisions", 'update' => "revert all {$entity_type_id} revisions", 'delete' => "delete all {$entity_type_id} revisions"]; $bundle = $entity->bundle(); $type_map = ['view' => "view {$entity_type_id} {$bundle} revisions", 'update' => "revert {$entity_type_id} {$bundle} revisions", 'delete' => "delete {$entity_type_id} {$bundle} revisions"]; if (!$entity || !isset($map[$operation]) || !isset($type_map[$operation])) { // If there was no node to check against, or the $op was not one of the // supported ones, we return access denied. return FALSE; } // Statically cache access by revision ID, language code, user account ID, // and operation. $langcode = $entity->language()->getId(); $cid = $entity->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $operation; if (!isset($this->accessCache[$cid])) { // Perform basic permission checks first. if (!$account->hasPermission($map[$operation]) && !$account->hasPermission($type_map[$operation]) && !$account->hasPermission('administer nodes')) { $this->accessCache[$cid] = FALSE; return FALSE; } if (($admin_permission = $entity_type->getAdminPermission()) && $account->hasPermission($admin_permission)) { $this->accessCache[$cid] = TRUE; } else { // First check the access to the default revision and finally, if the // node passed in is not the default revision then access to that, too. $this->accessCache[$cid] = $entity_access->access($entity_storage->load($entity->id()), $operation, $account) && ($entity->isDefaultRevision() || $entity_access->access($entity, $operation, $account)); } } return $this->accessCache[$cid]; }
/** * {@inheritdoc} */ public function onPresave(ContentEntityInterface $entity, $default_revision, $published_state) { // This is *probably* not necessary if configuration is setup correctly, // but it can't hurt. $entity->setNewRevision(TRUE); // A newly-created revision is always the default revision, or else // it gets lost. $entity->isDefaultRevision($entity->isNew() || $default_revision); }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { // The revision timestamp will be updated when the revision is saved. Keep // the original one for the confirmation message. $this->entityRevision->setNewRevision(); $this->entityRevision->isDefaultRevision(TRUE); if ($this->entityRevision instanceof EntityRevisionLogInterface) { if ($this->entityRevision instanceof TimestampedRevisionInterface) { $original_revision_timestamp = $this->entityRevision->getRevisionCreationTime(); $this->entityRevision->revision_log = t('Copy of the revision from %date.', ['%date' => $this->dateFormatter->format($original_revision_timestamp)]); } else { $this->entityRevision->revision_log = t('Copy of the revision'); } } $this->entityRevision->save(); $this->logger('content')->notice('@type: reverted %title revision %revision.', ['@type' => $this->entityRevision->bundle(), '%title' => $this->entityRevision->label(), '%revision' => $this->entityRevision->getRevisionId()]); drupal_set_message(t('@type %title has been reverted to the revision', ['@type' => $this->entityRevision->{$this->entityRevision->getEntityType()->getKey('bundle')}->entity->label(), '%title' => $this->entityRevision->label()])); $form_state->setRedirectUrl($this->entityRevision->urlInfo('version-history')); }
/** * Saves an entity revision. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity object. * * @return int * The revision id. */ protected function saveRevision(ContentEntityInterface $entity) { $record = $this->mapToStorageRecord($entity->getUntranslated(), $this->revisionTable); $entity->preSaveRevision($this, $record); if ($entity->isNewRevision()) { $insert_id = $this->database->insert($this->revisionTable, array('return' => Database::RETURN_INSERT_ID))->fields((array) $record)->execute(); // Even if this is a new revision, the revision ID key might have been // set in which case we should not override the provided revision ID. if (!isset($record->{$this->revisionKey})) { $record->{$this->revisionKey} = $insert_id; } if ($entity->isDefaultRevision()) { $this->database->update($this->entityType->getBaseTable())->fields(array($this->revisionKey => $record->{$this->revisionKey}))->condition($this->idKey, $record->{$this->idKey})->execute(); } } else { $this->database->update($this->revisionTable)->fields((array) $record)->condition($this->revisionKey, $record->{$this->revisionKey})->execute(); } // Make sure to update the new revision key for the entity. $entity->{$this->revisionKey}->value = $record->{$this->revisionKey}; return $record->{$this->revisionKey}; }
/** * Saves values of fields that use dedicated tables. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity. * @param bool $update * TRUE if the entity is being updated, FALSE if it is being inserted. */ protected function saveToDedicatedTables(ContentEntityInterface $entity, $update = TRUE) { $vid = $entity->getRevisionId(); $id = $entity->id(); $bundle = $entity->bundle(); $entity_type = $entity->getEntityTypeId(); $default_langcode = $entity->getUntranslated()->language()->getId(); $translation_langcodes = array_keys($entity->getTranslationLanguages()); $table_mapping = $this->getTableMapping(); if (!isset($vid)) { $vid = $id; } foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { continue; } $table_name = $table_mapping->getDedicatedDataTableName($storage_definition); $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition); // Delete and insert, rather than update, in case a value was added. if ($update) { // Only overwrite the field's base table if saving the default revision // of an entity. if ($entity->isDefaultRevision()) { $this->database->delete($table_name)->condition('entity_id', $id)->execute(); } if ($this->entityType->isRevisionable()) { $this->database->delete($revision_name)->condition('entity_id', $id)->condition('revision_id', $vid)->execute(); } } // Prepare the multi-insert query. $do_insert = FALSE; $columns = array('entity_id', 'revision_id', 'bundle', 'delta', 'langcode'); foreach ($storage_definition->getColumns() as $column => $attributes) { $columns[] = $table_mapping->getFieldColumnName($storage_definition, $column); } $query = $this->database->insert($table_name)->fields($columns); if ($this->entityType->isRevisionable()) { $revision_query = $this->database->insert($revision_name)->fields($columns); } $langcodes = $field_definition->isTranslatable() ? $translation_langcodes : array($default_langcode); foreach ($langcodes as $langcode) { $delta_count = 0; $items = $entity->getTranslation($langcode)->get($field_name); $items->filterEmptyItems(); foreach ($items as $delta => $item) { // We now know we have something to insert. $do_insert = TRUE; $record = array('entity_id' => $id, 'revision_id' => $vid, 'bundle' => $bundle, 'delta' => $delta, 'langcode' => $langcode); foreach ($storage_definition->getColumns() as $column => $attributes) { $column_name = $table_mapping->getFieldColumnName($storage_definition, $column); // Serialize the value if specified in the column schema. $record[$column_name] = !empty($attributes['serialize']) ? serialize($item->{$column}) : $item->{$column}; } $query->values($record); if ($this->entityType->isRevisionable()) { $revision_query->values($record); } if ($storage_definition->getCardinality() != FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED && ++$delta_count == $storage_definition->getCardinality()) { break; } } } // Execute the query if we have values to insert. if ($do_insert) { // Only overwrite the field's base table if saving the default revision // of an entity. if ($entity->isDefaultRevision()) { $query->execute(); } if ($this->entityType->isRevisionable()) { $revision_query->execute(); } } } }
protected function checkAccess(ContentEntityInterface $entity, AccountInterface $account, $operation = 'view') { $entity_type_id = $entity->getEntityTypeId(); $entity_access = $this->entityManager->getAccessControlHandler($entity_type_id); /** @var \Drupal\content_entity_base\Entity\Storage\RevisionableStorageInterface|\Drupal\Core\Entity\EntityStorageInterface $entity_storage */ $entity_storage = $this->entityManager->getStorage($entity_type_id); if (!$entity_storage instanceof RevisionableStorageInterface) { throw new \InvalidArgumentException('The entity storage has to implement \\Drupal\\content_entity_base\\Entity\\Storage\\RevisionableStorageInterface'); } $map = ['view' => "view all {$entity_type_id} revisions", 'update' => "revert all {$entity_type_id} revisions", 'delete' => "delete all {$entity_type_id} revisions"]; $bundle = $entity->bundle(); $type_map = ['view' => "view {$entity_type_id} {$bundle} revisions", 'update' => "revert {$entity_type_id} {$bundle} revisions", 'delete' => "delete {$entity_type_id} {$bundle} revisions"]; if (!$entity || !isset($map[$operation]) || !isset($type_map[$operation])) { // If there was no node to check against, or the $op was not one of the // supported ones, we return access denied. return FALSE; } // Statically cache access by revision ID, language code, user account ID, // and operation. $langcode = $entity->language()->getId(); $cid = $entity->getRevisionId() . ':' . $langcode . ':' . $account->id() . ':' . $operation; if (!isset($this->access[$cid])) { // Perform basic permission checks first. if (!$account->hasPermission($map[$operation]) && !$account->hasPermission($type_map[$operation]) && !$account->hasPermission('administer nodes')) { $this->access[$cid] = FALSE; return FALSE; } // There should be at least two revisions. If the vid of the given node // and the vid of the default revision differ, then we already have two // different revisions so there is no need for a separate database check. // Also, if you try to revert to or delete the default revision, that's // not good. if ($entity->isDefaultRevision() && ($entity_storage->countDefaultLanguageRevisions($entity) == 1 || $operation == 'update' || $operation == 'delete')) { $this->access[$cid] = FALSE; } elseif ($account->hasPermission('administer ' . $entity_type_id)) { $this->access[$cid] = TRUE; } else { // First check the access to the default revision and finally, if the // node passed in is not the default revision then access to that, too. $this->access[$cid] = $entity_access->access($entity_storage->load($entity->id()), $operation, $account) && ($entity->isDefaultRevision() || $entity_access->access($entity, $operation, $account)); } } return $this->access[$cid]; }
/** * {@inheritdoc} */ public function onPresave(ContentEntityInterface $entity, $default_revision, $published_state) { // This is probably not necessary if configuration is setup correctly. $entity->setNewRevision(TRUE); $entity->isDefaultRevision($default_revision); }