Пример #1
0
 /**
  * {@inheritdoc}
  */
 public function save(EntityInterface $entity)
 {
     // We return SAVED_UPDATED by default because the logic below might not
     // update the entity if its values haven't changed, so returning FALSE
     // would be confusing in that situation.
     $return = SAVED_UPDATED;
     $transaction = $this->database->startTransaction();
     try {
         // Load the stored entity, if any.
         if (!$entity->isNew() && !isset($entity->original)) {
             $id = $entity->id();
             if ($entity->getOriginalId() !== NULL) {
                 $id = $entity->getOriginalId();
             }
             $entity->original = $this->loadUnchanged($id);
         }
         if ($entity->isNew()) {
             $entity->mlid = $this->database->insert($this->entityType->getBaseTable())->fields(array('menu_name' => $entity->menu_name))->execute();
             $entity->enforceIsNew();
         }
         // Unlike the save() method from EntityDatabaseStorage, we invoke the
         // 'presave' hook first because we want to allow modules to alter the
         // entity before all the logic from our preSave() method.
         $this->invokeHook('presave', $entity);
         $entity->preSave($this);
         // If every value in $entity->original is the same in the $entity, there
         // is no reason to run the update queries or clear the caches. We use
         // array_intersect_key() with the $entity as the first parameter because
         // $entity may have additional keys left over from building a router entry.
         // The intersect removes the extra keys, allowing a meaningful comparison.
         if ($entity->isNew() || array_intersect_key(get_object_vars($entity), get_object_vars($entity->original)) != get_object_vars($entity->original)) {
             $return = drupal_write_record($this->entityType->getBaseTable(), $entity, $this->idKey);
             if ($return) {
                 if (!$entity->isNew()) {
                     $this->resetCache(array($entity->{$this->idKey}));
                     $entity->postSave($this, TRUE);
                     $this->invokeHook('update', $entity);
                 } else {
                     $return = SAVED_NEW;
                     $this->resetCache();
                     $entity->enforceIsNew(FALSE);
                     $entity->postSave($this, FALSE);
                     $this->invokeHook('insert', $entity);
                 }
             }
         }
         // Ignore replica server temporarily.
         db_ignore_replica();
         unset($entity->original);
         return $return;
     } catch (\Exception $e) {
         $transaction->rollback();
         watchdog_exception($this->entityTypeId, $e);
         throw new EntityStorageException($e->getMessage(), $e->getCode(), $e);
     }
 }
 /**
  * Form constructor.
  *
  * @param array $form
  *   An associative array containing the structure of the form.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param \Drupal\Core\Entity\EntityInterface $node
  *   The node being previews
  *
  * @return array
  *   The form structure.
  */
 public function buildForm(array $form, FormStateInterface $form_state, EntityInterface $node = NULL)
 {
     $view_mode = $node->preview_view_mode;
     $query_options = $node->isNew() ? array('query' => array('uuid' => $node->uuid())) : array();
     $form['backlink'] = array('#type' => 'link', '#title' => $this->t('Back to content editing'), '#url' => $node->isNew() ? Url::fromRoute('node.add', ['node_type' => $node->bundle()]) : $node->urlInfo('edit-form'), '#options' => array('attributes' => array('class' => array('node-preview-backlink'))) + $query_options);
     $view_mode_options = $this->getViewModeOptions($node);
     $form['uuid'] = array('#type' => 'value', '#value' => $node->uuid());
     $form['view_mode'] = array('#type' => 'select', '#title' => $this->t('View mode'), '#options' => $view_mode_options, '#default_value' => $view_mode, '#attributes' => array('data-drupal-autosubmit' => TRUE));
     $form['submit'] = array('#type' => 'submit', '#value' => $this->t('Switch'), '#attributes' => array('class' => array('js-hide')));
     return $form;
 }
 /**
  * {@inheritdoc}
  */
 public function save(array $form, FormStateInterface $form_state)
 {
     // Save as a new revision if requested to do so.
     if (!$form_state->isValueEmpty('revision')) {
         $this->entity->setNewRevision();
     }
     $insert = $this->entity->isNew();
     $this->entity->save();
     $context = ['@type' => $this->entity->bundle(), '%info' => $this->entity->label()];
     $logger = $this->logger($this->entity->id());
     $bundle_entity = $this->getBundleEntity();
     $t_args = ['@type' => $bundle_entity ? $bundle_entity->label() : 'None', '%info' => $this->entity->label()];
     if ($insert) {
         $logger->notice('@type: added %info.', $context);
         drupal_set_message($this->t('@type %info has been created.', $t_args));
     } else {
         $logger->notice('@type: updated %info.', $context);
         drupal_set_message($this->t('@type %info has been updated.', $t_args));
     }
     if ($this->entity->id()) {
         $form_state->setValue('id', $this->entity->id());
         $form_state->set('id', $this->entity->id());
         if ($this->entity->getEntityType()->hasLinkTemplate('collection')) {
             $form_state->setRedirectUrl($this->entity->toUrl('collection'));
         } else {
             $form_state->setRedirectUrl($this->entity->toUrl('canonical'));
         }
     } else {
         // In the unlikely case something went wrong on save, the entity will be
         // rebuilt and entity form redisplayed.
         drupal_set_message($this->t('The entity could not be saved.'), 'error');
         $form_state->setRebuild();
     }
 }
 /**
  * Creates entity path alias.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity that should get an alias.
  * @param string $alias
  *   The alias to be created.
  */
 protected function doExecute(EntityInterface $entity, $alias)
 {
     // We need to save the entity before we can get its internal path.
     if ($entity->isNew()) {
         $entity->save();
     }
     $path = $entity->urlInfo()->getInternalPath();
     $langcode = $entity->language()->getId();
     $this->aliasStorage->save($path, $alias, $langcode);
 }
 /**
  * {@inheritdoc}
  */
 protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account)
 {
     switch ($operation) {
         case 'view':
             // There is no direct view.
             return FALSE;
         case 'update':
             // If there is a URL, this is an external link so always accessible.
             return $account->hasPermission('administer menu') && ($entity->getUrl() || $this->accessManager->checkNamedRoute($entity->getRouteName(), $entity->getRouteParameters(), $account));
         case 'delete':
             return !$entity->isNew() && $account->hasPermission('administer menu');
     }
 }
Пример #6
0
 /**
  * Returns an array of supported actions for the current entity form.
  *
  * @todo Consider introducing a 'preview' action here, since it is used by
  *   many entity types.
  */
 protected function actions(array $form, FormStateInterface $form_state)
 {
     // @todo Rename the action key from submit to save.
     $actions['submit'] = array('#type' => 'submit', '#value' => $this->t('Save'), '#validate' => array(array($this, 'validate')), '#submit' => array(array($this, 'submit'), array($this, 'save')));
     if (!$this->entity->isNew() && $this->entity->hasLinkTemplate('delete-form')) {
         $route_info = $this->entity->urlInfo('delete-form');
         if ($this->getRequest()->query->has('destination')) {
             $query = $route_info->getOption('query');
             $query['destination'] = $this->getRequest()->query->get('destination');
             $route_info->setOption('query', $query);
         }
         $actions['delete'] = array('#type' => 'link', '#title' => $this->t('Delete'), '#access' => $this->entity->access('delete'), '#attributes' => array('class' => array('button', 'button--danger')));
         $actions['delete'] += $route_info->toRenderArray();
     }
     return $actions;
 }
Пример #7
0
 /**
  * Returns an array of supported actions for the current entity form.
  *
  * @todo Consider introducing a 'preview' action here, since it is used by
  *   many entity types.
  */
 protected function actions(array $form, FormStateInterface $form_state)
 {
     // @todo Consider renaming the action key from submit to save. The impacts
     //   are hard to predict. For example, see
     //   \Drupal\language\Element\LanguageConfiguration::processLanguageConfiguration().
     $actions['submit'] = array('#type' => 'submit', '#value' => $this->t('Save'), '#validate' => array('::validate'), '#submit' => array('::submitForm', '::save'));
     if (!$this->entity->isNew() && $this->entity->hasLinkTemplate('delete-form')) {
         $route_info = $this->entity->urlInfo('delete-form');
         if ($this->getRequest()->query->has('destination')) {
             $query = $route_info->getOption('query');
             $query['destination'] = $this->getRequest()->query->get('destination');
             $route_info->setOption('query', $query);
         }
         $actions['delete'] = array('#type' => 'link', '#title' => $this->t('Delete'), '#access' => $this->entity->access('delete'), '#attributes' => array('class' => array('button', 'button--danger')));
         $actions['delete']['#url'] = $route_info;
     }
     return $actions;
 }
Пример #8
0
 /**
  * {@inheritdoc}
  */
 protected function checkAccess(EntityInterface $feed, $operation, $langcode, AccountInterface $account)
 {
     $has_perm = $account->hasPermission('administer feeds') || $account->hasPermission("{$operation} {$feed->bundle()} feeds");
     switch ($operation) {
         case 'view':
         case 'create':
         case 'update':
             return AccessResult::allowedIf($has_perm);
         case 'import':
         case 'clear':
             return AccessResult::allowedIf($has_perm && !$feed->isLocked());
         case 'unlock':
             return AccessResult::allowedIf($has_perm && $feed->isLocked());
         case 'delete':
             return AccessResult::allowedIf($has_perm && !$feed->isLocked() && !$feed->getItemCount() && !$feed->isNew());
         default:
             return AccessResult::neutral();
     }
 }
Пример #9
0
 /**
  * Responds to entity POST requests and saves the new entity.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity.
  *
  * @return \Drupal\rest\ResourceResponse
  *   The HTTP response object.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\HttpException
  */
 public function post(EntityInterface $entity = NULL)
 {
     if ($entity == NULL) {
         throw new BadRequestHttpException('No entity content received.');
     }
     if (!$entity->access('create')) {
         throw new AccessDeniedHttpException();
     }
     $definition = $this->getPluginDefinition();
     // Verify that the deserialized entity is of the type that we expect to
     // prevent security issues.
     if ($entity->getEntityTypeId() != $definition['entity_type']) {
         throw new BadRequestHttpException('Invalid entity type');
     }
     // POSTed entities must not have an ID set, because we always want to create
     // new entities here.
     if (!$entity->isNew()) {
         throw new BadRequestHttpException('Only new entities can be created');
     }
     // Only check 'edit' permissions for fields that were actually
     // submitted by the user. Field access makes no difference between 'create'
     // and 'update', so the 'edit' operation is used here.
     foreach ($entity->_restSubmittedFields as $key => $field_name) {
         if (!$entity->get($field_name)->access('edit')) {
             throw new AccessDeniedHttpException(String::format('Access denied on creating field @field', array('@field' => $field_name)));
         }
     }
     // Validate the received data before saving.
     $this->validate($entity);
     try {
         $entity->save();
         $this->logger->notice('Created entity %type with ID %id.', array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id()));
         // 201 Created responses have an empty body.
         return new ResourceResponse(NULL, 201, array('Location' => $entity->url('canonical', ['absolute' => TRUE])));
     } catch (EntityStorageException $e) {
         throw new HttpException(500, 'Internal Server Error', $e);
     }
 }
Пример #10
0
 /**
  * Responds to entity POST requests and saves the new entity.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity.
  *
  * @return \Drupal\rest\ResourceResponse
  *   The HTTP response object.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\HttpException
  */
 public function post(EntityInterface $entity = NULL)
 {
     if ($entity == NULL) {
         throw new BadRequestHttpException(t('No entity content received.'));
     }
     if (!$entity->access('create')) {
         throw new AccessDeniedHttpException();
     }
     $definition = $this->getPluginDefinition();
     // Verify that the deserialized entity is of the type that we expect to
     // prevent security issues.
     if ($entity->getEntityTypeId() != $definition['entity_type']) {
         throw new BadRequestHttpException(t('Invalid entity type'));
     }
     // POSTed entities must not have an ID set, because we always want to create
     // new entities here.
     if (!$entity->isNew()) {
         throw new BadRequestHttpException(t('Only new entities can be created'));
     }
     foreach ($entity as $field_name => $field) {
         if (!$field->access('create')) {
             throw new AccessDeniedHttpException(t('Access denied on creating field @field.', array('@field' => $field_name)));
         }
     }
     // Validate the received data before saving.
     $this->validate($entity);
     try {
         $entity->save();
         $this->logger->notice('Created entity %type with ID %id.', array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id()));
         $url = url(strtr($this->pluginId, ':', '/') . '/' . $entity->id(), array('absolute' => TRUE));
         // 201 Created responses have an empty body.
         return new ResourceResponse(NULL, 201, array('Location' => $url));
     } catch (EntityStorageException $e) {
         throw new HttpException(500, t('Internal Server Error'), $e);
     }
 }
Пример #11
0
 /**
  * {@inheritdoc}
  */
 protected function entitySaveAccess(EntityInterface $entity)
 {
     if ($this->configuration['authorize'] && !empty($entity->uid->value)) {
         // If the uid was mapped directly, rather than by email or username, it
         // could be invalid.
         if (!($account = $entity->uid->entity)) {
             $message = 'User %uid is not a valid user.';
             throw new EntityAccessException(String::format($message, array('%uid' => $entity->uid->value)));
         }
         $op = $entity->isNew() ? 'create' : 'update';
         if (!$entity->access($op, $account)) {
             $args = array('%name' => $account->getUsername(), '%op' => $op, '@bundle' => Unicode::strtolower($this->bundleLabel()), '%bundle' => $entity->bundle());
             throw new EntityAccessException(String::format('User %name is not authorized to %op @bundle %bundle.', $args));
         }
     }
 }
Пример #12
0
 /**
  * {@inheritdoc}
  */
 protected function getFormSubmitSuffix(EntityInterface $entity, $langcode)
 {
     if (!$entity->isNew() && $entity->isTranslatable()) {
         $translations = $entity->getTranslationLanguages();
         if ((count($translations) > 1 || !isset($translations[$langcode])) && ($field = $entity->getFieldDefinition('status'))) {
             return ' ' . ($field->isTranslatable() ? t('(this translation)') : t('(all translations)'));
         }
     }
     return '';
 }
Пример #13
0
 /**
  * Constructs the entity URI.
  *
  * @param \Drupal\Core\Entity\EntityInterface
  *   The entity.
  * @return string
  *   The entity URI.
  */
 protected function getEntityUri(EntityInterface $entity)
 {
     // Some entity types don't provide a canonical link template, at least call
     // out to ->url().
     if ($entity->isNew() || !$entity->hasLinkTemplate('canonical')) {
         return $entity->url('canonical', []);
     }
     $url = $entity->urlInfo('canonical', ['absolute' => TRUE]);
     return $url->setRouteParameter('_format', 'hal_json')->toString();
 }
Пример #14
0
 /**
  * {@inheritdoc}
  */
 protected function doSave($id, EntityInterface $entity)
 {
     $is_new = $entity->isNew();
     $prefix = $this->getPrefix();
     $config_name = $prefix . $entity->id();
     if ($id !== $entity->id()) {
         // Renaming a config object needs to cater for:
         // - Storage needs to access the original object.
         // - The object needs to be renamed/copied in ConfigFactory and reloaded.
         // - All instances of the object need to be renamed.
         $this->configFactory->rename($prefix . $id, $config_name);
     }
     $config = $this->configFactory->getEditable($config_name);
     // Retrieve the desired properties and set them in config.
     $config->setData($this->mapToStorageRecord($entity));
     $config->save($entity->hasTrustedData());
     // Update the entity with the values stored in configuration. It is possible
     // that configuration schema has casted some of the values.
     if (!$entity->hasTrustedData()) {
         $data = $this->mapFromStorageRecords(array($config->get()));
         $updated_entity = current($data);
         foreach (array_keys($config->get()) as $property) {
             $value = $updated_entity->get($property);
             $entity->set($property, $value);
         }
     }
     return $is_new ? SAVED_NEW : SAVED_UPDATED;
 }
 /**
  * {@inheritdoc}
  */
 protected function doSave($id, EntityInterface $entity)
 {
     /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     if ($entity->isNew()) {
         // Ensure the entity is still seen as new after assigning it an id, while
         // storing its data.
         $entity->enforceIsNew();
         if ($this->entityType->isRevisionable()) {
             $entity->setNewRevision();
         }
         $return = SAVED_NEW;
     } else {
         // @todo Consider returning a different value when saving a non-default
         //   entity revision. See https://www.drupal.org/node/2509360.
         $return = $entity->isDefaultRevision() ? SAVED_UPDATED : FALSE;
     }
     $this->populateAffectedRevisionTranslations($entity);
     $this->doSaveFieldItems($entity);
     return $return;
 }
 /**
  * {@inheritdoc}
  *
  * @todo Revisit this logic with forward revisions in mind.
  */
 protected function doSave($id, EntityInterface $entity)
 {
     if ($entity->_rev->is_stub) {
         $entity->isDefaultRevision(TRUE);
     } else {
         // Enforce new revision if any module messed with it in a hook.
         $entity->setNewRevision();
         // Decide whether or not this is the default revision.
         if (!$entity->isNew()) {
             $default_rev = \Drupal::service('entity.index.rev.tree')->getDefaultRevision($entity->uuid());
             if ($entity->_rev->value == $default_rev) {
                 $entity->isDefaultRevision(TRUE);
             } else {
                 $entity->isDefaultRevision(FALSE);
             }
         }
     }
     return parent::doSave($id, $entity);
 }
Пример #17
0
 /**
  * Check if the provided entity is new.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity to check.
  *
  * @return bool
  *   TRUE if the provided entity is new.
  */
 protected function doEvaluate(EntityInterface $entity)
 {
     return $entity->isNew();
 }
Пример #18
0
 /**
  * {@inheritdoc}
  */
 protected function doSave($id, EntityInterface $entity)
 {
     $is_new = $entity->isNew();
     $prefix = $this->getPrefix();
     if ($id !== $entity->id()) {
         // Renaming a config object needs to cater for:
         // - Storage needs to access the original object.
         // - The object needs to be renamed/copied in ConfigFactory and reloaded.
         // - All instances of the object need to be renamed.
         $config = $this->configFactory->rename($prefix . $id, $prefix . $entity->id());
     } else {
         $config = $this->configFactory->get($prefix . $id);
     }
     // Retrieve the desired properties and set them in config.
     $config->setData($this->mapToStorageRecord($entity));
     $config->save();
     return $is_new ? SAVED_NEW : SAVED_UPDATED;
 }
Пример #19
0
 /**
  * {@inheritdoc}
  */
 protected function buildValue(EntityInterface $entity)
 {
     !($is_new = $entity->isNew());
     $revision_id = $is_new ? 0 : $entity->getRevisionId();
     // We assign a temporary status to the revision since we are indexing it
     // pre save. It will be updated post save with the final status. This will
     // help identifying failures and exception scenarios during entity save.
     $status = 'indexed';
     if (!$is_new && $revision_id) {
         $status = $entity->_deleted->value ? 'deleted' : 'available';
     }
     return array('entity_type_id' => $entity->getEntityTypeId(), 'entity_id' => $is_new ? 0 : $entity->id(), 'revision_id' => $revision_id, 'uuid' => $entity->uuid(), 'rev' => $entity->_rev->value, 'is_stub' => $entity->_rev->is_stub, 'status' => $status);
 }
Пример #20
0
 /**
  * {@inheritdoc}
  */
 public function save(EntityInterface $entity)
 {
     // Track if this entity is new.
     $is_new = $entity->isNew();
     // Execute presave logic and invoke the related hooks.
     $id = $this->doPreSave($entity);
     // Perform the save and reset the static cache for the changed entity.
     $return = $this->doSave($id, $entity);
     // Execute post save logic and invoke the related hooks.
     $this->doPostSave($entity, !$is_new);
     return $return;
 }
Пример #21
0
 /**
  * {@inheritdoc}
  */
 protected function entitySaveAccess(EntityInterface $entity)
 {
     // No need to authorize.
     if (!$this->configuration['authorize'] || !$entity instanceof EntityOwnerInterface) {
         return;
     }
     // If the uid was mapped directly, rather than by email or username, it
     // could be invalid.
     if (!($account = $entity->getOwner())) {
         throw new EntityAccessException($this->t('Invalid user mapped to %label.', ['%label' => $entity->label()]));
     }
     // We don't check access for anonymous users.
     if ($account->isAnonymous()) {
         return;
     }
     $op = $entity->isNew() ? 'create' : 'update';
     // Access granted.
     if ($entity->access($op, $account)) {
         return;
     }
     $args = ['%name' => $account->getUsername(), '@op' => $op, '@bundle' => $this->getItemLabelPlural()];
     throw new EntityAccessException($this->t('User %name is not authorized to @op @bundle.', $args));
 }
Пример #22
0
 /**
  * {@inheritdoc}
  */
 protected function has($id, EntityInterface $entity)
 {
     return !$entity->isNew();
 }
 /**
  * Performs access checks.
  *
  * This method is supposed to be overwritten by extending classes that
  * do their own custom access checking.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity for which to check access.
  * @param string $operation
  *   The entity operation. Usually one of 'view', 'update' or 'delete'.
  * @param string $langcode
  *   The language code for which to check access.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The user for which to check access.
  *
  * @return \Drupal\Core\Access\AccessResultInterface
  *   The access result.
  */
 protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account)
 {
     if ($operation == 'delete' && $entity->isNew()) {
         return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
     }
     if ($admin_permission = $this->entityType->getAdminPermission()) {
         return AccessResult::allowedIfHasPermission($account, $this->entityType->getAdminPermission());
     } else {
         // No opinion.
         return AccessResult::neutral();
     }
 }
Пример #24
0
 /**
  * Responds to entity POST requests and saves the new entity.
  *
  * @Thruway(name = "add", type="procedure")
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\HttpException
  *
  * @return array
  *
  */
 public function post(EntityInterface $entity = NULL)
 {
     if ($entity == NULL) {
         throw new BadRequestHttpException(t('No entity content received.'));
     }
     if (!$entity->access('create')) {
         throw new AccessDeniedHttpException();
     }
     $definition = $this->getPluginDefinition();
     // Verify that the deserialized entity is of the type that we expect to
     // prevent security issues.
     if ($entity->getEntityTypeId() != $definition['entity_type']) {
         throw new BadRequestHttpException(t('Invalid entity type'));
     }
     // POSTed entities must not have an ID set, because we always want to create
     // new entities here.
     if (!$entity->isNew()) {
         throw new BadRequestHttpException(t('Only new entities can be created'));
     }
     foreach ($entity as $field_name => $field) {
         if (!$field->access('create')) {
             throw new AccessDeniedHttpException(t('Access denied on creating field @field.', array('@field' => $field_name)));
         }
     }
     // Validate the received data before saving.
     $this->validate($entity);
     try {
         $entity->save();
         //@todo fix this hack.  When you save, it doesn't load the field defaults, so we need to reload it
         return $entity::load($entity->id());
         //            $this->logger->notice(
         //                'Created entity %type with ID %id.',
         //                array('%type' => $entity->getEntityTypeId(), '%id' => $entity->id())
         //            );
     } catch (EntityStorageException $e) {
         throw new HttpException(500, t('Internal Server Error'), $e);
     }
 }
Пример #25
0
 /**
  * {@inheritdoc}
  */
 protected function doSave($id, EntityInterface $entity)
 {
     $is_new = $entity->isNew();
     // Save the entity data in the key value store.
     $this->keyValueStore->set($entity->id(), $entity->toArray());
     // If this is a rename, delete the original entity.
     if ($this->has($id, $entity) && $id !== $entity->id()) {
         $this->keyValueStore->delete($id);
     }
     return $is_new ? SAVED_NEW : SAVED_UPDATED;
 }
Пример #26
0
 /**
  * {@inheritdoc}
  */
 public function save(EntityInterface $entity)
 {
     $id = $entity->id();
     // Track the original ID.
     if ($entity->getOriginalId() !== NULL) {
         $id = $entity->getOriginalId();
     }
     // Track if this entity is new.
     $is_new = $entity->isNew();
     // Track if this entity exists already.
     $id_exists = $this->has($id, $entity);
     // A new entity should not already exist.
     if ($id_exists && $is_new) {
         throw new EntityStorageException(SafeMarkup::format('@type entity with ID @id already exists.', array('@type' => $this->entityTypeId, '@id' => $id)));
     }
     // Load the original entity, if any.
     if ($id_exists && !isset($entity->original)) {
         $entity->original = $this->loadUnchanged($id);
     }
     // Allow code to run before saving.
     $entity->preSave($this);
     $this->invokeHook('presave', $entity);
     // Perform the save and reset the static cache for the changed entity.
     $return = $this->doSave($id, $entity);
     $this->resetCache(array($id));
     // The entity is no longer new.
     $entity->enforceIsNew(FALSE);
     // Allow code to run after saving.
     $entity->postSave($this, !$is_new);
     $this->invokeHook($is_new ? 'insert' : 'update', $entity);
     // After saving, this is now the "original entity", and subsequent saves
     // will be updates instead of inserts, and updates must always be able to
     // correctly identify the original entity.
     $entity->setOriginalId($entity->id());
     unset($entity->original);
     return $return;
 }
 /**
  * Performs access checks.
  *
  * This method is supposed to be overwritten by extending classes that
  * do their own custom access checking.
  *
  * @param \Drupal\Core\Entity\EntityInterface $entity
  *   The entity for which to check 'create' access.
  * @param string $operation
  *   The entity operation. Usually one of 'view', 'update', 'create' or
  *   'delete'.
  * @param string $langcode
  *   The language code for which to check access.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The user for which to check access.
  *
  * @return bool|null
  *   TRUE if access was granted, FALSE if access was denied and NULL if access
  *   could not be determined.
  */
 protected function checkAccess(EntityInterface $entity, $operation, $langcode, AccountInterface $account)
 {
     if ($operation == 'delete' && $entity->isNew()) {
         return FALSE;
     }
     if ($admin_permission = $this->entityType->getAdminPermission()) {
         return $account->hasPermission($admin_permission);
     } else {
         return NULL;
     }
 }