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]; }
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]; }
/** * Invokes a method on the Field objects within an entity. * * Any argument passed will be forwarded to the invoked method. * * @param string $method * The name of the method to be invoked. * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity object. * * @return array * A multidimensional associative array of results, keyed by entity * translation language code and field name. */ protected function invokeFieldMethod($method, ContentEntityInterface $entity) { $result = []; $args = array_slice(func_get_args(), 2); $langcodes = array_keys($entity->getTranslationLanguages()); // Ensure that the field method is invoked as first on the current entity // translation and then on all other translations. $current_entity_langcode = $entity->language()->getId(); if (reset($langcodes) != $current_entity_langcode) { $langcodes = array_diff($langcodes, [$current_entity_langcode]); array_unshift($langcodes, $current_entity_langcode); } foreach ($langcodes as $langcode) { $translation = $entity->getTranslation($langcode); // For non translatable fields, there is only one field object instance // across all translations and it has as parent entity the entity in the // default entity translation. Therefore field methods on non translatable // fields should be invoked only on the default entity translation. $fields = $translation->isDefaultTranslation() ? $translation->getFields() : $translation->getTranslatableFields(); foreach ($fields as $name => $items) { // call_user_func_array() is way slower than a direct call so we avoid // using it if have no parameters. $result[$langcode][$name] = $args ? call_user_func_array([$items, $method], $args) : $items->{$method}(); } } // We need to call the delete method for field items of removed // translations. if ($method == 'postSave' && !empty($entity->original)) { $original_langcodes = array_keys($entity->original->getTranslationLanguages()); foreach (array_diff($original_langcodes, $langcodes) as $removed_langcode) { $translation = $entity->original->getTranslation($removed_langcode); $fields = $translation->getTranslatableFields(); foreach ($fields as $name => $items) { $items->delete(); } } } return $result; }
/** * Saves translation data in an entity translation. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity for which the translation should be saved. * @param array $data * The translation data for the fields. * @param string $target_langcode * The target language. */ protected function doSaveTranslations(ContentEntityInterface $entity, array $data, $target_langcode) { // If the translation for this language does not exist yet, initialize it. if (!$entity->hasTranslation($target_langcode)) { $entity->addTranslation($target_langcode, $entity->toArray()); } $embeded_fields = \Drupal::config('tmgmt_content.settings')->get('embedded_fields'); $translation = $entity->getTranslation($target_langcode); $manager = \Drupal::service('content_translation.manager'); $manager->getTranslationMetadata($translation)->setSource($entity->language()->getId()); foreach ($data as $name => $field_data) { foreach (Element::children($field_data) as $delta) { $field_item = $field_data[$delta]; foreach (Element::children($field_item) as $property) { $property_data = $field_item[$property]; // If there is translation data for the field property, save it. if (isset($property_data['#translation']['#text']) && $property_data['#translate']) { $translation->get($name)->offsetGet($delta)->set($property, $property_data['#translation']['#text']); } elseif (isset($embeded_fields[$entity->getEntityTypeId()][$name])) { $this->doSaveTranslations($translation->get($name)->offsetGet($delta)->{$property}, $property_data, $target_langcode); } } } } $translation->save(); }
/** * Builds a table row for overview form. * * @param array ContentEntityInterface $entity * Data needed to build the list row. * @param array $bundles * The array of bundles. * * @return array */ public function overviewRow(ContentEntityInterface $entity, array $bundles) { $label = $entity->label() ?: $this->t('@type: @id', array('@type' => $entity->getEntityTypeId(), '@id' => $entity->id())); // Get existing translations and current job items for the entity // to determine translation statuses $translations = $entity->getTranslationLanguages(); $source_lang = $entity->language()->getId(); $current_job_items = tmgmt_job_item_load_latest('content', $entity->getEntityTypeId(), $entity->id(), $source_lang); $row = array('id' => $entity->id(), 'title' => $entity->hasLinkTemplate('canonical') ? $entity->toLink($label, 'canonical')->toString() : ($entity->label() ?: $entity->id())); if (count($bundles) > 1) { $row['bundle'] = isset($bundles[$entity->bundle()]) ? $bundles[$entity->bundle()] : t('Unknown'); } // Load entity translation specific data. $manager = \Drupal::service('content_translation.manager'); foreach (\Drupal::languageManager()->getLanguages() as $langcode => $language) { $translation_status = 'current'; if ($langcode == $source_lang) { $translation_status = 'original'; } elseif (!isset($translations[$langcode])) { $translation_status = 'missing'; } elseif ($translation = $entity->getTranslation($langcode)) { $metadata = $manager->getTranslationMetadata($translation); if ($metadata->isOutdated()) { $translation_status = 'outofdate'; } } $build = $this->buildTranslationStatus($translation_status, isset($current_job_items[$langcode]) ? $current_job_items[$langcode] : NULL); $row['langcode-' . $langcode] = ['data' => \Drupal::service('renderer')->render($build), 'class' => array('langstatus-' . $langcode)]; } return $row; }