/** * Returns the entity_form_display object used to build an entity form. * * Depending on the configuration of the form mode for the entity bundle, this * can be either the display object associated to the form mode, or the * 'default' display. * * This method should only be used internally when rendering an entity form. * When assigning suggested display options for a component in a given form * mode, entity_get_form_display() should be used instead, in order to avoid * inadvertently modifying the output of other form modes that might happen to * use the 'default' display too. Those options will then be effectively * applied only if the form mode is configured to use them. * * hook_entity_form_display_alter() is invoked on each display, allowing 3rd * party code to alter the display options held in the display before they are * used to generate render arrays. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity for which the form is being built. * @param string $form_mode * The form mode. * * @return \Drupal\Core\Entity\Display\EntityFormDisplayInterface * The display object that should be used to build the entity form. * * @see entity_get_form_display() * @see hook_entity_form_display_alter() */ public static function collectRenderDisplay(ContentEntityInterface $entity, $form_mode) { $entity_type = $entity->getEntityTypeId(); $bundle = $entity->bundle(); // Check the existence and status of: // - the display for the form mode, // - the 'default' display. if ($form_mode != 'default') { $candidate_ids[] = $entity_type . '.' . $bundle . '.' . $form_mode; } $candidate_ids[] = $entity_type . '.' . $bundle . '.default'; $results = \Drupal::entityQuery('entity_form_display')->condition('id', $candidate_ids)->condition('status', TRUE)->execute(); // Load the first valid candidate display, if any. $storage = \Drupal::entityManager()->getStorage('entity_form_display'); foreach ($candidate_ids as $candidate_id) { if (isset($results[$candidate_id])) { $display = $storage->load($candidate_id); break; } } // Else create a fresh runtime object. if (empty($display)) { $display = $storage->create(array('targetEntityType' => $entity_type, 'bundle' => $bundle, 'mode' => $form_mode, 'status' => TRUE)); } // Let the display know which form mode was originally requested. $display->originalMode = $form_mode; // Let modules alter the display. $display_context = array('entity_type' => $entity_type, 'bundle' => $bundle, 'form_mode' => $form_mode); \Drupal::moduleHandler()->alter('entity_form_display', $display, $display_context); return $display; }
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", 'list' => "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", 'list' => "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 submitForm(array &$form, FormStateInterface $form_state) { $this->entityManager->getStorage($this->entityRevision->getEntityTypeId())->deleteRevision($this->entityRevision->getRevisionId()); $this->logger('content')->notice('@type: deleted %title revision %revision.', ['@type' => $this->entityRevision->bundle(), '%title' => $this->entityRevision->label(), '%revision' => $this->entityRevision->getRevisionId()]); drupal_set_message(t('@type %title has been deleted', ['@type' => $this->entityRevision->{$this->entityRevision->getEntityType()->getKey('bundle')}->entity->label(), '%title' => $this->entityRevision->label()])); $form_state->setRedirectUrl($this->entityRevision->urlInfo('version-history')); }
protected function getAddLink(ContentEntityInterface $host) { $link = ''; if ($host->access('update', \Drupal::currentUser())) { $link = '<ul class="action-links action-links-field-collection-add"><li>'; $link .= \Drupal::l(t('Add'), Url::FromRoute('field_collection_item.add_page', ['field_collection' => $this->fieldDefinition->getName(), 'host_type' => $host->getEntityTypeId(), 'host_id' => $host->id()])); $link .= '</li></ul>'; } return $link; }
/** * {@inheritdoc} */ public function synchronizeFields(ContentEntityInterface $entity, $sync_langcode, $original_langcode = NULL) { $translations = $entity->getTranslationLanguages(); $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); // If we have no information about what to sync to, if we are creating a new // entity, if we have no translations for the current entity and we are not // creating one, then there is nothing to synchronize. if (empty($sync_langcode) || $entity->isNew() || count($translations) < 2) { return; } // If the entity language is being changed there is nothing to synchronize. $entity_type = $entity->getEntityTypeId(); $entity_unchanged = isset($entity->original) ? $entity->original : $this->entityManager->getStorage($entity_type)->loadUnchanged($entity->id()); if ($entity->getUntranslated()->language()->getId() != $entity_unchanged->getUntranslated()->language()->getId()) { return; } /** @var \Drupal\Core\Field\FieldItemListInterface $items */ foreach ($entity as $field_name => $items) { $field_definition = $items->getFieldDefinition(); $field_type_definition = $field_type_manager->getDefinition($field_definition->getType()); $column_groups = $field_type_definition['column_groups']; // Sync if the field is translatable, not empty, and the synchronization // setting is enabled. if ($field_definition instanceof ThirdPartySettingsInterface && $field_definition->isTranslatable() && !$items->isEmpty() && ($translation_sync = $field_definition->getThirdPartySetting('content_translation', 'translation_sync'))) { // Retrieve all the untranslatable column groups and merge them into // single list. $groups = array_keys(array_diff($translation_sync, array_filter($translation_sync))); if (!empty($groups)) { $columns = array(); foreach ($groups as $group) { $info = $column_groups[$group]; // A missing 'columns' key indicates we have a single-column group. $columns = array_merge($columns, isset($info['columns']) ? $info['columns'] : array($group)); } if (!empty($columns)) { $values = array(); foreach ($translations as $langcode => $language) { $values[$langcode] = $entity->getTranslation($langcode)->get($field_name)->getValue(); } // If a translation is being created, the original values should be // used as the unchanged items. In fact there are no unchanged items // to check against. $langcode = $original_langcode ?: $sync_langcode; $unchanged_items = $entity_unchanged->getTranslation($langcode)->get($field_name)->getValue(); $this->synchronizeItems($values, $unchanged_items, $sync_langcode, array_keys($translations), $columns); foreach ($translations as $langcode => $language) { $entity->getTranslation($langcode)->get($field_name)->setValue($values[$langcode]); } } } } } }
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]; }
/** * Checks whether field languages are correctly stored for the given entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity fields are attached to. * @param string $message * (optional) A message to display with the assertion. */ protected function assertFieldStorageLangcode(ContentEntityInterface $entity, $message = '') { $status = TRUE; $entity_type = $entity->getEntityTypeId(); $id = $entity->id(); $langcode = $entity->getUntranslated()->language()->id; $fields = array($this->field_name, $this->untranslatable_field_name); foreach ($fields as $field_name) { $field_storage = FieldStorageConfig::loadByName($entity_type, $field_name); $tables = array(ContentEntityDatabaseStorage::_fieldTableName($field_storage), ContentEntityDatabaseStorage::_fieldRevisionTableName($field_storage)); foreach ($tables as $table) { $record = \Drupal::database()->select($table, 'f')->fields('f')->condition('f.entity_id', $id)->condition('f.revision_id', $id)->execute()->fetchObject(); if ($record->langcode != $langcode) { $status = FALSE; break; } } } return $this->assertTrue($status, $message); }
/** * Transforms an entity into an array of strings. * * Parses an entity's fields and for every field it builds an array of string * to be compared. Basically this function transforms an entity into an array * of strings. * * @param ContentEntityInterface $entity * An entity containing fields. * * @return array * Array of strings resulted by parsing the entity. */ public function parseEntity(ContentEntityInterface $entity) { $result = array(); $entity_type_id = $entity->getEntityTypeId(); // Load all entity base fields. $entity_base_fields = $this->entityManager->getBaseFieldDefinitions($entity_type_id); // Loop through entity fields and transform every FieldItemList object // into an array of strings according to field type specific settings. foreach ($entity as $field_items) { $field_type = $field_items->getFieldDefinition()->getType(); $plugin_config = $this->pluginsConfig->get('field_types.' . $field_type); $plugin = NULL; if ($plugin_config && $plugin_config['type'] != 'hidden') { $plugin = $this->diffBuilderManager->createInstance($plugin_config['type'], $plugin_config['settings']); } if ($plugin) { // Configurable field. It is the responsibility of the class extending // this class to hide some configurable fields from comparison. This // class compares all configurable fields. if (!array_key_exists($field_items->getName(), $entity_base_fields)) { $build = $plugin->build($field_items); if (!empty($build)) { $result[$field_items->getName()] = $build; } } else { // Check if this field needs to be compared. $config_key = 'entity.' . $entity_type_id . '.' . $field_items->getName(); $enabled = $this->config->get($config_key); if ($enabled) { $build = $plugin->build($field_items); if (!empty($build)) { $result[$field_items->getName()] = $build; } } } } } return $result; }
/** * Build the render array for the block library. * * @param EntityLayoutInterface $entity_layout * The entity layout to show allowed blocks for. * @param ContentEntityInterface $content_entity * The content entity the block is being added to. * * @return array * The render array. */ protected function buildBlockLibrary(EntityLayoutInterface $entity_layout, ContentEntityInterface $content_entity = NULL) { $build['filter'] = ['#type' => 'search', '#title' => $this->t('Filter'), '#title_display' => 'invisible', '#size' => 30, '#placeholder' => $this->t('Filter by block name'), '#attributes' => ['class' => ['context-table-filter'], 'data-element' => '.block-add-table', 'title' => $this->t('Enter a part of the block name to filter by.')]]; $headers = [$this->t('Block'), $this->t('Category'), $this->t('Operations')]; $build['blocks'] = ['#type' => 'table', '#header' => $headers, '#rows' => [], '#empty' => $this->t('No blocks available for placement.'), '#attributes' => ['class' => ['block-add-table']]]; $blocks = $this->entityLayoutService->getSystemBlocks(); // Add each block definition to the table. foreach ($blocks as $block_id => $block) { if (!$entity_layout->blockIsAllowed($block_id)) { continue; } $bundle_entity_type = $this->entityLayoutService->getTargetBundleEntityType($entity_layout); // Use different routes depending on what kind of entity were adding // blocks for. if ($content_entity) { $entity_type_id = $content_entity->getEntityTypeId(); $block_add_url = Url::fromRoute("entity_layout.{$entity_type_id}.content.block.add", ['block_id' => $block_id, $entity_type_id => $content_entity->id()]); } else { $entity_type_id = $entity_layout->getTargetEntityType(); $block_add_url = Url::fromRoute("entity_layout.{$entity_type_id}.block.add", ['block_id' => $block_id, $bundle_entity_type => $entity_layout->getTargetBundle()]); } $links = ['add' => ['title' => $this->t('Place block'), 'url' => $block_add_url, 'attributes' => ['class' => ['use-ajax'], 'data-dialog-type' => 'modal', 'data-dialog-options' => Json::encode(['width' => 700])]]]; $build['blocks']['#rows'][] = ['title' => ['data' => ['#type' => 'inline_template', '#template' => '<div class="context-table-filter-text-source">{{ label }}</div>', '#context' => ['label' => $block['admin_label']]]], 'category' => ['data' => $block['category']], 'operations' => ['data' => ['#type' => 'operations', '#links' => $links]]]; } // @todo Create a filter behaviour for the table. //$build['#attached']['library'][] = 'context_ui/admin'; return $build; }
/** * 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; }
/** * Builds the entity translation for the provided translation data. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity for which the translation should be returned. * @param array $data * The translation data for the fields. * @param string $target_langcode * The target language. * * @return \Drupal\Core\Entity\ContentEntityInterface $translation * Translation data. */ protected function makePreview(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 = $this->config('tmgmt_content.settings')->get('embedded_fields'); $translation = $entity->getTranslation($target_langcode); 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->makePreview($translation->get($name)->offsetGet($delta)->{$property}, $property_data, $target_langcode); } } } } return $translation; }
/** * @param \Drupal\Core\Entity\ContentEntityInterface $entity * @return array */ protected function buildRecord(ContentEntityInterface $entity) { return array('entity_type_id' => $entity->getEntityTypeId(), 'entity_id' => $entity->id(), 'entity_uuid' => $entity->uuid(), 'revision_id' => $entity->getRevisionId(), 'deleted' => $entity->_deleted->value, 'rev' => $entity->_rev->value, 'seq' => $this->multiversionManager->newSequenceId(), 'local' => (bool) $entity->getEntityType()->get('local'), 'is_stub' => (bool) $entity->_rev->is_stub); }
/** * Generates an overview table of older revisions of an entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * An entity object. * * @return array * An array as expected by drupal_render(). */ public function revisionOverview(ContentEntityInterface $entity) { $langcode = $this->languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); /** @var \Drupal\content_entity_base\Entity\Storage\RevisionableStorageInterface $entity_storage */ $entity_storage = $this->entityManager()->getStorage($entity->getEntityTypeId()); $header = array($this->t('Revision'), $this->t('Operations')); $rows = []; $vids = $entity_storage->revisionIds($entity); $entity_revisions = array_combine($vids, array_map(function ($vid) use($entity_storage) { return $entity_storage->loadRevision($vid); }, $vids)); $latest_revision = TRUE; foreach (array_reverse($vids) as $vid) { $row = []; /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ $revision = $entity_revisions[$vid]; if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) { if ($latest_revision) { $row[] = $this->getRevisionDescription($revision, TRUE); $row[] = ['data' => ['#prefix' => '<em>', '#markup' => $this->t('Current revision'), '#suffix' => '</em>']]; foreach ($row as &$current) { $current['class'] = ['revision-current']; } $latest_revision = FALSE; } else { $row[] = $this->getRevisionDescription($revision, FALSE); $links = $this->getOperationLinks($revision); $row[] = ['data' => ['#type' => 'operations', '#links' => $links]]; } } $rows[] = $row; } $build[$entity->getEntityTypeId() . '_revisions_table'] = array('#theme' => 'table', '#rows' => $rows, '#header' => $header); // We have no clue about caching yet. $build['#cache']['max-age'] = 0; return $build; }
/** * Transfer the supplied blocks to the specified content entity. * * @param EntityLayoutInterface $entity_layout * The entity layout the entity layout blocks belongs to. * @param ContentEntityInterface $content_entity * The content entity to transfer blocks to. * @param BlockPluginCollection $block_collection * The blocks to transfer. * * @return EntityLayoutBlockInterface[] */ public function transferBlocks(EntityLayoutInterface $entity_layout, ContentEntityInterface $content_entity, BlockPluginCollection $block_collection) { $block_entities = []; // Create a entity layout block entity from each default block // configuration and create a new UUID. /** @var BlockPluginInterface $block */ foreach ($block_collection as $block) { $configuration = $block->getConfiguration(); $block_entity = EntityLayoutBlock::create(['layout' => $entity_layout->id(), 'config' => $configuration, 'entity_id' => $content_entity, 'entity_type' => $content_entity->getEntityTypeId()]); $block_entities[] = $block_entity; } /** @var ContentEntityInterface $block_entity */ foreach ($block_entities as $block_entity) { $block_entity->save(); } return $block_entities; }
/** * Create a new entity layout block. * * @param EntityLayoutInterface $entity_layout * The entity layout object to create a block for. * @param ContentEntityInterface $content_entity * The content entity to associate the block with. * @param array $configuration * The block configuration. * * @return EntityLayoutBlockInterface|static */ public function createContentBlock(EntityLayoutInterface $entity_layout, ContentEntityInterface $content_entity, array $configuration) { $entity_layout_block = EntityLayoutBlock::create(['layout' => $entity_layout->id(), 'config' => $configuration, 'entity_id' => $content_entity, 'entity_type' => $content_entity->getEntityTypeId()]); return $entity_layout_block; }
public function invokeEntityUpdate(ContentEntityInterface $entity) { $runners = $this->getUpdateTypeRunners(); foreach ($runners as $runner) { if ($runner instanceof EntityMonitorUpdateRunnerInterface && $runner->updateEntityType() == $entity->getEntityTypeId()) { // Check to see if update reference fields have changed. if ($this->entityUpdatesChanged($entity, $runner)) { $runner->onEntityUpdate($entity); break; } } } }
/** * Retrieves all indexes that are configured to index the given entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity for which to check. * * @return \Drupal\search_api\IndexInterface[] * All indexes that are configured to index the given entity (using this * datasource class). */ public static function getIndexesForEntity(ContentEntityInterface $entity) { $entity_type = $entity->getEntityTypeId(); $datasource_id = 'entity:' . $entity_type; $entity_bundle = $entity->bundle(); $index_names = \Drupal::entityQuery('search_api_index')->condition('datasources.*', $datasource_id)->execute(); if (!$index_names) { return array(); } // Needed for PhpStorm. See https://youtrack.jetbrains.com/issue/WI-23395. /** @var \Drupal\search_api\IndexInterface[] $indexes */ $indexes = Index::loadMultiple($index_names); // If the datasource's entity type supports bundles, we have to filter the // indexes for whether they also include the specific bundle of the given // entity. Otherwise, we are done. if ($entity_type !== $entity_bundle) { foreach ($indexes as $index_id => $index) { try { $config = $index->getDatasource($datasource_id)->getConfiguration(); $default = !empty($config['default']); $bundle_set = !empty($config['bundles'][$entity_bundle]); if ($default == $bundle_set) { unset($indexes[$index_id]); } } catch (SearchApiException $e) { unset($indexes[$index_id]); } } } return $indexes; }
/** * Get all previous revisions that have updates of the attached type. * * This function would be easier and more performant if this core issue with * Entity Query was fixed: https://www.drupal.org/node/2649268 Without this * fix can't filter query on type of update and whether they are active. So * therefore all previous revisions have to be loaded. * * @todo Help get that core issue fixed or rewrite this function query table * fields directly. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * * @return \Drupal\Core\Entity\ContentEntityInterface[] */ protected function getPreviousRevisionsWithUpdates(ContentEntityInterface $entity) { /** @var ContentEntityInterface[] $revisions */ $revisions = []; $type = $entity->getEntityType(); $query = $this->entityTypeManager->getStorage($entity->getEntityTypeId())->getQuery(); $query->allRevisions()->condition($type->getKey('id'), $entity->id())->condition($type->getKey('revision'), $entity->getRevisionId(), '<')->sort($type->getKey('revision'), 'DESC'); if ($revision_ids = $query->execute()) { $revision_ids = array_keys($revision_ids); $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId()); foreach ($revision_ids as $revision_id) { /** @var ContentEntityInterface $revision */ $revision = $storage->loadRevision($revision_id); if ($update_ids = $this->getUpdateIdsOnEntity($revision)) { $revisions[$revision_id] = $revision; } } } return $revisions; }
/** * {@inheritdoc} */ public function isLatestRevision(ContentEntityInterface $entity) { return $entity->getRevisionId() == $this->getLatestRevisionId($entity->getEntityTypeId(), $entity->id()); }
/** * 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(); }
/** * Generate a filename from the entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The content entity to generate the filename. * * @return string * The cleaned filename from the entity label. */ protected function generateFilename(ContentEntityInterface $entity) { $filename = preg_replace("/[^A-Za-z0-9 ]/", '', $entity->label()); // If for some bizarre reason there isn't a valid character in the entity // title or the entity doesn't provide a label then we use the entity type. if (!$filename) { $filename = $entity->getEntityTypeId(); } return $filename . '.pdf'; }
/** * Get the directly previous revision. * * $entity->original will not ALWAYS be the previous revision. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * * @return \Drupal\Core\Entity\EntityInterface|null */ public function getPreviousRevision(ContentEntityInterface $entity) { $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId()); $query = $storage->getQuery(); $type = $entity->getEntityType(); $query->allRevisions() ->condition($type->getKey('id'), $entity->id()) ->condition($type->getKey('revision'), $entity->getRevisionId(), '<') ->sort($type->getKey('revision'), 'DESC') ->pager(1); $revision_ids = $query->execute(); if ($revision_ids) { $revision_id = array_keys($revision_ids)[0]; return $storage->loadRevision($revision_id); } return NULL; }
/** * {@inheritdoc} */ public function create(ContentEntityInterface $entity, $fields) { $query = $this->database->insert('comment_entity_statistics')->fields(array('entity_id', 'entity_type', 'field_name', 'cid', 'last_comment_timestamp', 'last_comment_name', 'last_comment_uid', 'comment_count')); foreach ($fields as $field_name => $detail) { // Skip fields that entity does not have. if (!$entity->hasField($field_name)) { continue; } // Get the user ID from the entity if it's set, or default to the // currently logged in user. $last_comment_uid = 0; if ($entity instanceof EntityOwnerInterface) { $last_comment_uid = $entity->getOwnerId(); } if (!isset($last_comment_uid)) { // Default to current user when entity does not implement // EntityOwnerInterface or author is not set. $last_comment_uid = $this->currentUser->id(); } // Default to REQUEST_TIME when entity does not have a changed property. $last_comment_timestamp = REQUEST_TIME; if ($entity instanceof EntityChangedInterface) { $last_comment_timestamp = $entity->getChangedTime(); } $query->values(array('entity_id' => $entity->id(), 'entity_type' => $entity->getEntityTypeId(), 'field_name' => $field_name, 'cid' => 0, 'last_comment_timestamp' => $last_comment_timestamp, 'last_comment_name' => NULL, 'last_comment_uid' => $last_comment_uid, 'comment_count' => 0)); } $query->execute(); }
/** * Generates an overview table of older revisions of an entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * An entity object. * * @return array * A render array. */ protected function revisionOverview(ContentEntityInterface $entity) { $langcode = $this->languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); $entity_storage = $this->entityTypeManager()->getStorage($entity->getEntityTypeId()); $header = [$this->t('Revision'), $this->t('Operations')]; $rows = []; $revision_ids = $this->revisionIds($entity); // @todo Expand the entity storage to load multiple revisions. $entity_revisions = array_combine($revision_ids, array_map(function ($vid) use($entity_storage) { return $entity_storage->loadRevision($vid); }, $revision_ids)); foreach ($entity_revisions as $revision) { $row = []; /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ if ($revision->hasTranslation($langcode) && $revision->getTranslation($langcode)->isRevisionTranslationAffected()) { $row[] = $this->getRevisionDescription($revision, $revision->isDefaultRevision()); if ($revision->isDefaultRevision()) { $row[] = ['data' => ['#prefix' => '<em>', '#markup' => $this->t('Current revision'), '#suffix' => '</em>']]; foreach ($row as &$current) { $current['class'] = ['revision-current']; } } else { $row[] = $this->getOperationLinks($revision); } } $rows[] = $row; } $build[$entity->getEntityTypeId() . '_revisions_table'] = ['#theme' => 'table', '#rows' => $rows, '#header' => $header]; // We have no clue about caching yet. $build['#cache']['max-age'] = 0; return $build; }
/** * Getter for the type. * * @return string * The type of the entity. */ public function getType() { return $this->entity->getEntityTypeId(); }
/** * Evaluate if the user has access to the field of an entity. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity to check access on. * @param string $field * The name of the field to check access on. * @param string $operation * The operation access should be checked for. Usually one of "view" or * "edit". * @param \Drupal\Core\Session\AccountInterface $user * The user account to test access against. * * @return bool * TRUE if the user has access to the field on the entity, FALSE otherwise. */ protected function doEvaluate(ContentEntityInterface $entity, $field, $operation, AccountInterface $user) { if (!$entity->hasField($field)) { return FALSE; } $access = $this->entityTypeManager->getAccessControlHandler($entity->getEntityTypeId()); if (!$access->access($entity, $operation, $user)) { return FALSE; } $definition = $entity->getFieldDefinition($field); $items = $entity->get($field); return $access->fieldAccess($operation, $definition, $user, $items); }
/** * Deletes values of fields in dedicated tables for all revisions. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The entity. It must have a revision ID. */ protected function deleteRevisionFromDedicatedTables(ContentEntityInterface $entity) { $vid = $entity->getRevisionId(); if (isset($vid)) { $table_mapping = $this->getTableMapping(); foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) { $storage_definition = $field_definition->getFieldStorageDefinition(); if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) { continue; } $revision_name = $table_mapping->getDedicatedRevisionTableName($storage_definition); $this->database->delete($revision_name)->condition('entity_id', $entity->id())->condition('revision_id', $vid)->execute(); } } }
/** * {@inheritdoc} */ public function getNewCommentPageNumber($total_comments, $new_comments, ContentEntityInterface $entity, $field_name = 'comment') { $instance = $entity->getFieldDefinition($field_name); $comments_per_page = $instance->getSetting('per_page'); if ($total_comments <= $comments_per_page) { // Only one page of comments. $count = 0; } elseif ($instance->getSetting('default_mode') == CommentManagerInterface::COMMENT_MODE_FLAT) { // Flat comments. $count = $total_comments - $new_comments; } else { // Threaded comments. // 1. Find all the threads with a new comment. $unread_threads_query = $this->database->select('comment_field_data', 'comment')->fields('comment', array('thread'))->condition('entity_id', $entity->id())->condition('entity_type', $entity->getEntityTypeId())->condition('field_name', $field_name)->condition('status', CommentInterface::PUBLISHED)->condition('default_langcode', 1)->orderBy('created', 'DESC')->orderBy('cid', 'DESC')->range(0, $new_comments); // 2. Find the first thread. $first_thread_query = $this->database->select($unread_threads_query, 'thread'); $first_thread_query->addExpression('SUBSTRING(thread, 1, (LENGTH(thread) - 1))', 'torder'); $first_thread = $first_thread_query->fields('thread', array('thread'))->orderBy('torder')->range(0, 1)->execute()->fetchField(); // Remove the final '/'. $first_thread = substr($first_thread, 0, -1); // Find the number of the first comment of the first unread thread. $count = $this->database->query('SELECT COUNT(*) FROM {comment_field_data} WHERE entity_id = :entity_id AND entity_type = :entity_type AND field_name = :field_name AND status = :status AND SUBSTRING(thread, 1, (LENGTH(thread) - 1)) < :thread AND default_langcode = 1', array(':status' => CommentInterface::PUBLISHED, ':entity_id' => $entity->id(), ':field_name' => $field_name, ':entity_type' => $entity->getEntityTypeId(), ':thread' => $first_thread))->fetchField(); } return $comments_per_page > 0 ? (int) ($count / $comments_per_page) : 0; }