diff --git a/core/lib/Drupal/Core/Entity/EntityAccessController.php b/core/lib/Drupal/Core/Entity/EntityAccessController.php index 5583aec..f172c5b 100644 --- a/core/lib/Drupal/Core/Entity/EntityAccessController.php +++ b/core/lib/Drupal/Core/Entity/EntityAccessController.php @@ -8,6 +8,8 @@ namespace Drupal\Core\Entity; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; +use Drupal\Core\Entity\Field\FieldInterface; use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountInterface; @@ -268,4 +270,45 @@ public function setModuleHandler(ModuleHandlerInterface $module_handler) { return $this; } + /** + * {@inheritdoc} + */ + public function fieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account = NULL, FieldInterface $field = NULL) { + global $user; + if (!isset($account)) { + $account = $user; + } + + // Get the default access restriction that lives within this field. + $default = $field ? $field->defaultAccess($operation, $account) : TRUE; + + // Invoke hook and collect grants/denies for field access from other + // modules. Our default access flag is masked under the ':default' key. + $grants = array(':default' => $default); + $hook_implementations = \Drupal::moduleHandler()->getImplementations('entity_field_access'); + foreach ($hook_implementations as $module) { + $grants = array_merge($grants, array($module => module_invoke($module, 'entity_field_access', $operation, $field_definition, $account, $field))); + } + + // Also allow modules to alter the returned grants/denies. + $context = array( + 'operation' => $operation, + 'field_definition' => $field_definition, + 'field' => $field, + 'account' => $account, + ); + drupal_alter('entity_field_access', $grants, $context); + + // One grant being FALSE is enough to deny access immediately. + if (in_array(FALSE, $grants, TRUE)) { + return FALSE; + } + // At least one grant has the explicit opinion to allow access. + if (in_array(TRUE, $grants, TRUE)) { + return TRUE; + } + // All grants are NULL and have no opinion - deny access in that case. + return FALSE; + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php index 749b9a0..a6c7834 100644 --- a/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityAccessControllerInterface.php @@ -8,6 +8,8 @@ namespace Drupal\Core\Entity; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; +use Drupal\Core\Entity\Field\FieldInterface; use Drupal\Core\Language\Language; use Drupal\Core\Session\AccountInterface; @@ -70,4 +72,22 @@ public function resetCache(); */ public function setModuleHandler(ModuleHandlerInterface $module_handler); + /** + * Checks access to an operation on a given entity field. + * + * @param string $operation + * The operation access should be checked for. + * Usually one of "view" or "edit". + * @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition + * The field definition. + * @param \Drupal\Core\Session\AccountInterface $account + * (optional) The user session for which to check access, or NULL to check + * access for the current user. Defaults to NULL. + * @param \Drupal\Core\Entity\Field\FieldInterface $items + * (optional) The field values for which to check access, or NULL if access + * is checked for the field definition, without any specific value + * available. Defaults to NULL. + */ + public function fieldAccess($operation, FieldDefinitionInterface $field_definition, AccountInterface $account = NULL, FieldInterface $items = NULL); + } diff --git a/core/lib/Drupal/Core/Entity/Field/Field.php b/core/lib/Drupal/Core/Entity/Field/Field.php index 6f8382a..8731c08 100644 --- a/core/lib/Drupal/Core/Entity/Field/Field.php +++ b/core/lib/Drupal/Core/Entity/Field/Field.php @@ -49,6 +49,7 @@ class Field extends ItemList implements FieldInterface { */ public function __construct(array $definition, $name = NULL, TypedDataInterface $parent = NULL) { parent::__construct($definition, $name, $parent); + $this->definition['field_name'] = $name; // Always initialize one empty item as most times a value for at least one // item will be present. That way prototypes created by // \Drupal\Core\TypedData\TypedDataManager::getPropertyInstance() will @@ -81,8 +82,7 @@ public function getLangcode() { * {@inheritdoc} */ public function getFieldDefinition() { - // @todo https://drupal.org/node/1988612 - return NULL; + return new FieldDefinition($this->definition); } /** @@ -197,52 +197,15 @@ public function __unset($property_name) { } /** - * Implements \Drupal\Core\TypedData\AccessibleInterface::access(). + * {@inheritdoc} */ public function access($operation = 'view', AccountInterface $account = NULL) { - global $user; - if (!isset($account)) { - $account = $user; - } - // Get the default access restriction that lives within this field. - $access = $this->defaultAccess($operation, $account); - // Invoke hook and collect grants/denies for field access from other - // modules. Our default access flag is masked under the ':default' key. - $grants = array(':default' => $access); - $hook_implementations = \Drupal::moduleHandler()->getImplementations('entity_field_access'); - foreach ($hook_implementations as $module) { - $grants = array_merge($grants, array($module => module_invoke($module, 'entity_field_access', $operation, $this, $account))); - } - // Also allow modules to alter the returned grants/denies. - $context = array( - 'operation' => $operation, - 'field' => $this, - 'account' => $account, - ); - drupal_alter('entity_field_access', $grants, $context); - - // One grant being FALSE is enough to deny access immediately. - if (in_array(FALSE, $grants, TRUE)) { - return FALSE; - } - // At least one grant has the explicit opinion to allow access. - if (in_array(TRUE, $grants, TRUE)) { - return TRUE; - } - // All grants are NULL and have no opinion - deny access in that case. - return FALSE; + $access_controller = \Drupal::entityManager()->getAccessController($this->getParent()->entityType()); + return $access_controller->fieldAccess($operation, $this->getFieldDefinition(), $account, $this); } /** - * Contains the default access logic of this field. - * - * See \Drupal\Core\TypedData\AccessibleInterface::access() for the parameter - * doucmentation. This method can be overriden by field sub classes to provide - * a different default access logic. That allows them to inherit the complete - * access() method which contains the access hook invocation logic. - * - * @return bool - * TRUE if access to this field is allowed per default, FALSE otherwise. + * {@inheritdoc} */ public function defaultAccess($operation = 'view', AccountInterface $account = NULL) { // Grant access per default. diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php new file mode 100644 index 0000000..0f3a271 --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinition.php @@ -0,0 +1,220 @@ +definition = $definition; + } + + /** + * {@inheritdoc} + */ + public function getFieldName() { + return $this->definition['field_name']; + } + + /** + * Sets the field name. + * + * @param string $name + * The field name to set. + * + * @return \Drupal\Core\Entity\Field\FieldDefinition + * The object itself for chaining. + */ + public function setFieldName($name) { + $this->definition['field_name'] = $name; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFieldType() { + // Cut of the leading field_item: prefix from 'field_item:FIELD_TYPE'. + $parts = explode(':', $this->definition['type']); + return $parts[1]; + } + + /** + * Sets the field type. + * + * @param string $type + * The field type to set. + * + * @return \Drupal\Core\Entity\Field\FieldDefinition + * The object itself for chaining. + */ + public function setFieldType($type) { + $this->definition['type'] = 'field_item:' . $type; + return $this; + } + + /** + * Sets a field setting. + * + * @param string $type + * The field type to set. + * + * @return \Drupal\Core\Entity\Field\FieldDefinition + * The object itself for chaining. + */ + public function setFieldSetting($setting_name, $value) { + $this->definition['settings'][$setting_name] = $value; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFieldSettings() { + return $this->definition['settings']; + } + + /** + * {@inheritdoc} + */ + public function getFieldSetting($setting_name) { + return isset($this->definition['settings'][$setting_name]) ? $this->definition['settings'][$setting_name] : NULL; + } + + /** + * {@inheritdoc} + */ + public function getFieldPropertyNames() { + return array_keys(\Drupal::typedData()->create($this->definition['type'])->getPropertyDefinitions()); + } + + /** + * {@inheritdoc} + */ + public function isFieldTranslatable() { + return !empty($this->definition['translatable']); + } + + /** + * Sets whether the field is translatable. + * + * @param bool $translatable + * Whether the field is translatable. + * + * @return \Drupal\Core\Entity\Field\FieldDefinition + * The object itself for chaining. + */ + public function setTranslatable($translatable) { + $this->definition['translatable'] = $translatable; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFieldLabel() { + return $this->definition['label']; + } + + /** + * Sets the field label. + * + * @param string $label + * The field label to set. + * + * @return \Drupal\Core\Entity\Field\FieldDefinition + * The object itself for chaining. + */ + public function setFieldLabel($label) { + $this->definition['label'] = $label; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFieldDescription() { + return $this->definition['description']; + } + + /** + * Sets the field label. + * + * @param string $description + * The field label to set. + * + * @return \Drupal\Core\Entity\Field\FieldDefinition + * The object itself for chaining. + */ + public function setFieldDescription($description) { + $this->definition['description'] = $description; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getFieldCardinality() { + // @todo: Allow to control this. + return isset($this->definition['cardinality']) ? $this->definition['cardinality'] : 1; + } + + /** + * {@inheritdoc} + */ + public function isFieldRequired() { + return !empty($this->definition['required']); + } + + /** + * Sets constraints for a given field item property. + * + * @param string $name + * The name of the property to set constraints for. + * @param array $constraints + * The constraints to set. + * + * @return \Drupal\Core\Entity\Field\FieldDefinition + * The object itself for chaining. + */ + public function setPropertyConstraints($name, array $constraints) { + $this->definition['item_definition']['constraints']['ComplexData'][$name] = $constraints; + return $this; + } + + /** + * {@inheritdoc} + */ + public function isFieldConfigurable() { + return FALSE; + } + + /** + * {@inheritdoc} + */ + public function getFieldDefaultValue(EntityInterface $entity) { + return $this->getFieldSetting('default_value'); + } + +} diff --git a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php index b8d1389..8799a0e 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldDefinitionInterface.php @@ -68,10 +68,9 @@ public function getFieldName(); * Returns the field type. * * @return string - * The field type. + * The field type, i.e. the id of a field type plugin. For example 'text'. * - * @todo Provide more information about field types after - * https://drupal.org/node/1969728 is implemented. + * @see \Drupal\Core\Entity\Field\FieldTypePluginManager */ public function getFieldType(); @@ -123,6 +122,13 @@ public function getFieldPropertyNames(); public function isFieldTranslatable(); /** + * Determines whether the field is configurable via field.module. + * + * @return bool + */ + public function isFieldConfigurable(); + + /** * Returns the human-readable label for the field. * * @return string diff --git a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php index 4708418..fe1243c 100644 --- a/core/lib/Drupal/Core/Entity/Field/FieldInterface.php +++ b/core/lib/Drupal/Core/Entity/Field/FieldInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Entity\Field; +use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\AccessibleInterface; use Drupal\Core\TypedData\ListInterface; @@ -60,6 +61,17 @@ public function getLangcode(); public function getFieldDefinition(); /** + * Contains the default access logic of this field. + * + * See \Drupal\Core\TypedData\AccessibleInterface::access() for the parameter + * doucmentation. + * + * @return bool + * TRUE if access to this field is allowed per default, FALSE otherwise. + */ + public function defaultAccess($operation = 'view', AccountInterface $account = NULL); + + /** * Filters out empty field items and re-numbers the item deltas. */ public function filterEmptyValues(); diff --git a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php index 3a77f73..ddecebf 100644 --- a/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php +++ b/core/modules/edit/lib/Drupal/edit/Access/EditEntityFieldAccessCheck.php @@ -71,8 +71,7 @@ public function access(Route $route, Request $request) { * {@inheritdoc} */ public function accessEditEntityField(EntityInterface $entity, $field_name) { - $entity_type = $entity->entityType(); - return $entity->access('update') && ($field = $this->fieldInfo->getField($entity_type, $field_name)) && field_access('edit', $field, $entity_type, $entity); + return $entity->access('update') && $entity->get($field_name)->access('edit'); } /** diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php b/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php index 426af2c..b1dc3cd 100644 --- a/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php +++ b/core/modules/entity_reference/lib/Drupal/entity_reference/EntityReferenceController.php @@ -12,6 +12,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Controller\ControllerInterface; +use Drupal\Core\Entity\EntityManager; /** * Defines route controller for entity reference. @@ -26,13 +28,23 @@ class EntityReferenceController implements ContainerInjectionInterface { protected $entityReferenceAutocomplete; /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManager + */ + protected $entityManager; + + /** * Constructs a EntityReferenceController object. * * @param \Drupal\entity_reference\EntityReferenceAutocomplete $entity_reference_autcompletion - * The autocompletion helper for entity references + * The autocompletion helper for entity references. + * @param \Drupal\Core\Entity\EntityManager ĂȘntity_manager + * The entity manager. */ - public function __construct(EntityReferenceAutocomplete $entity_reference_autcompletion) { + public function __construct(EntityReferenceAutocomplete $entity_reference_autcompletion, EntityManager $entity_manager) { $this->entityReferenceAutocomplete = $entity_reference_autcompletion; + $this->entityManager = $entity_manager; } /** @@ -40,7 +52,8 @@ public function __construct(EntityReferenceAutocomplete $entity_reference_autcom */ public static function create(ContainerInterface $container) { return new static( - $container->get('entity_reference.autocomplete') + $container->get('entity_reference.autocomplete'), + $container->get('entity.manager') ); } @@ -77,7 +90,8 @@ public function handleAutocomplete(Request $request, $type, $field_name, $entity throw new AccessDeniedHttpException(); } - if ($field['type'] != 'entity_reference' || !field_access('edit', $field, $entity_type)) { + $access_controller = $this->entityManager->getAccessController($entity_type); + if ($field['type'] != 'entity_reference' || !$access_controller->fieldAccess('edit', $instance)) { throw new AccessDeniedHttpException(); } diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php index 50b5edb..e0d2d13 100644 --- a/core/modules/field/field.api.php +++ b/core/modules/field/field.api.php @@ -573,32 +573,5 @@ function hook_field_purge_instance($instance) { */ /** - * Determine whether the user has access to a given field. - * - * This hook is invoked from field_access() to let modules block access to - * operations on fields. If no module returns FALSE, the operation is allowed. - * - * @param $op - * The operation to be performed. Possible values: 'edit', 'view'. - * @param \Drupal\field\FieldInterface $field - * The field on which the operation is to be performed. - * @param $entity_type - * The type of $entity; for example, 'node' or 'user'. - * @param $entity - * (optional) The entity for the operation. - * @param $account - * (optional) The account to check; if not given use currently logged in user. - * - * @return - * TRUE if the operation is allowed, and FALSE if the operation is denied. - */ -function hook_field_access($op, \Drupal\field\FieldInterface $field, $entity_type, $entity, $account) { - if ($field['field_name'] == 'field_of_interest' && $op == 'edit') { - return $account->hasPermission('edit field of interest'); - } - return TRUE; -} - -/** * @} End of "addtogroup hooks". */ diff --git a/core/modules/field/field.deprecated.inc b/core/modules/field/field.deprecated.inc index e33b150..c77c294 100644 --- a/core/modules/field/field.deprecated.inc +++ b/core/modules/field/field.deprecated.inc @@ -829,3 +829,32 @@ function field_get_items(EntityInterface $entity, $field_name, $langcode = NULL) function field_get_default_value(EntityInterface $entity, $field, $instance, $langcode = NULL) { return $instance->getFieldDefaultValue($entity); } + +/** + * Determines whether the user has access to a given field. + * + * @param string $op + * The operation to be performed. Possible values: + * - edit + * - view + * @param \Drupal\field\FieldInterface $field + * The field on which the operation is to be performed. + * @param $entity_type + * The type of $entity; for example, 'node' or 'user'. + * @param $entity + * (optional) The entity for the operation. + * @param $account + * (optional) The account to check, if not given use currently logged in user. + * + * @return + * TRUE if the operation is allowed; FALSE if the operation is denied. + * + * @deprecated as of Drupal 8.0. Use + * Drupal\Core\Entity\EntityAccessControllerInterface::fieldAccess() + */ +function field_access($op, FieldInterface $field, $entity_type, $entity = NULL, $account = NULL) { + $access_controller = \Drupal::entityManager()->getAccessController($entity_type); + + $items = $entity ? $entity->get($field->id()) : NULL; + return $access_controller->fieldAccess($op, $field, $account, $items); +} diff --git a/core/modules/field/field.module b/core/modules/field/field.module index 681fc63..7d0a597 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -719,42 +719,6 @@ function field_view_field(EntityInterface $entity, $field_name, $display_options } /** - * Determines whether the user has access to a given field. - * - * @param string $op - * The operation to be performed. Possible values: - * - edit - * - view - * @param \Drupal\field\FieldInterface $field - * The field on which the operation is to be performed. - * @param $entity_type - * The type of $entity; for example, 'node' or 'user'. - * @param $entity - * (optional) The entity for the operation. - * @param $account - * (optional) The account to check, if not given use currently logged in user. - * - * @return - * TRUE if the operation is allowed; FALSE if the operation is denied. - */ -function field_access($op, FieldInterface $field, $entity_type, $entity = NULL, $account = NULL) { - global $user; - - if (!isset($account)) { - $account = $user; - } - - foreach (\Drupal::moduleHandler()->getImplementations('field_access') as $module) { - $function = $module . '_field_access'; - $access = $function($op, $field, $entity_type, $entity, $account); - if ($access === FALSE) { - return FALSE; - } - } - return TRUE; -} - -/** * Extracts the bundle name from a bundle object. * * @param $entity_type diff --git a/core/modules/field/lib/Drupal/field/Entity/Field.php b/core/modules/field/lib/Drupal/field/Entity/Field.php index 3421461..31cea7b 100644 --- a/core/modules/field/lib/Drupal/field/Entity/Field.php +++ b/core/modules/field/lib/Drupal/field/Entity/Field.php @@ -718,4 +718,17 @@ public function __wakeup() { $this->__construct($values); } + /** + * {@inheritdoc} + */ + public function isFieldConfigurable() { + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function isFieldQueryable() { + return TRUE; + } } diff --git a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php index 8f391ad..954b6b6 100644 --- a/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php +++ b/core/modules/field/lib/Drupal/field/Entity/FieldInstance.php @@ -610,6 +610,20 @@ public function allowBundleRename() { /** * {@inheritdoc} */ + public function isFieldConfigurable() { + return TRUE; + } + + /** + * {@inheritdoc} + */ + public function isFieldQueryable() { + return TRUE; + } + + /** + * {@inheritdoc} + */ public function offsetExists($offset) { return (isset($this->{$offset}) || $offset == 'field_id' || $offset == 'field_name'); } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php index 34cadae..b67ae5a 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterBase.php @@ -84,7 +84,7 @@ public function view(FieldInterface $items) { $info = array( '#theme' => 'field', '#title' => $this->fieldDefinition->getFieldLabel(), - '#access' => $this->checkFieldAccess('view', $entity), + '#access' => $items->access('view'), '#label_display' => $this->label, '#view_mode' => $this->viewMode, '#language' => $items->getLangcode(), @@ -137,22 +137,6 @@ public function settingsSummary() { public function prepareView(array $entities_items) { } /** - * Returns whether the currently logged in user has access to the field. - * - * @todo Remove this once Field API access is unified with entity field - * access: http://drupal.org/node/1994140. - */ - protected function checkFieldAccess($op, $entity) { - if ($this->fieldDefinition instanceof FieldInstanceInterface) { - $field = $this->fieldDefinition->getField(); - return field_access($op, $field, $entity->entityType(), $entity); - } - else { - return FALSE; - } - } - - /** * Returns the array of field settings. * * @return array diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php index a84c94f..c81cf10 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php +++ b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetBase.php @@ -128,7 +128,7 @@ public function form(FieldInterface $items, array &$form, array &$form_state, $g 'field-widget-' . drupal_html_class($this->getPluginId()), ), ), - '#access' => $this->checkFieldAccess('edit', $items->getEntity()), + '#access' => $items->access('edit'), 'widget' => $elements, ), ); @@ -427,22 +427,6 @@ protected function sortItems(FieldInterface $items) { } /** - * Returns whether the currently logged in user has access to the field. - * - * @todo Remove this once Field API access is unified with entity field - * access: http://drupal.org/node/1994140. - */ - protected function checkFieldAccess($op, $entity) { - if ($this->fieldDefinition instanceof FieldInstanceInterface) { - $field = $this->fieldDefinition->getField(); - return field_access($op, $field, $entity->entityType(), $entity); - } - else { - return FALSE; - } - } - - /** * Returns the array of field settings. * * @return array diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php index ea74a94..8750cb6 100644 --- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php +++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php @@ -11,6 +11,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Core\Language\Language; +use Drupal\Core\Entity\EntityManager; use Drupal\field\Plugin\Type\Formatter\FormatterPluginManager; use Drupal\views\ViewExecutable; use Drupal\views\Plugin\views\display\DisplayPluginBase; @@ -71,6 +72,13 @@ class Field extends FieldPluginBase { protected $formatterOptions; /** + * The entity manager. + * + * @var \Drupal\Core\Entity\EntityManager + */ + protected $entityManager; + + /** * The field formatter plugin manager. * * @var \Drupal\field\Plugin\Type\Formatter\FormatterPluginManager @@ -86,12 +94,15 @@ class Field extends FieldPluginBase { * The plugin_id for the plugin instance. * @param array $plugin_definition * The plugin implementation definition. + * @param \Drupal\Core\Entity\EntityManager $entity_manager + * The field formatter plugin manager. * @param \Drupal\field\Plugin\Type\Formatter\FormatterPluginManager $formatter_plugin_manager * The field formatter plugin manager. */ - public function __construct(array $configuration, $plugin_id, array $plugin_definition, FormatterPluginManager $formatter_plugin_manager) { + public function __construct(array $configuration, $plugin_id, array $plugin_definition, EntityManager $entity_manager, FormatterPluginManager $formatter_plugin_manager) { parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->entityManager = $entity_manager; $this->formatterPluginManager = $formatter_plugin_manager; } @@ -103,6 +114,7 @@ public static function create(ContainerInterface $container, array $configuratio $configuration, $plugin_id, $plugin_definition, + $container->get('entity.manager'), $container->get('plugin.manager.field.formatter') ); } @@ -147,7 +159,8 @@ public function init(ViewExecutable $view, DisplayPluginBase $display, array &$o */ public function access() { $base_table = $this->get_base_table(); - return field_access('view', $this->field_info, $this->definition['entity_tables'][$base_table]); + $access_controller = $this->entityManager->getAccessController($this->definition['entity_tables'][$base_table]); + return $access_controller->fieldAccess('view', $this->field_info); } /** diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php index 0ce394d..81eb005 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldAccessTest.php @@ -81,7 +81,7 @@ function setUp() { } /** - * Test that hook_field_access() is called. + * Test that hook_entity_field_access() is called. */ function testFieldAccess() { @@ -90,7 +90,7 @@ function testFieldAccess() { $this->assertText($this->test_view_field_value); // Assert the text is not visible for anonymous users. - // The field_test module implements hook_field_access() which will + // The field_test module implements hook_entity_field_access() which will // specifically target the 'test_view_field' field. $this->drupalLogout(); $this->drupalGet('node/' . $this->node->id()); diff --git a/core/modules/field/lib/Drupal/field/Tests/FormTest.php b/core/modules/field/lib/Drupal/field/Tests/FormTest.php index 4919115..4090764 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FormTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FormTest.php @@ -505,7 +505,8 @@ function testFieldFormAccess() { ->setComponent($field_name) ->save(); - // Create a field with no edit access - see field_test_field_access(). + // Create a field with no edit access. See + // field_test_entity_field_access(). $field_no_access = array( 'name' => 'field_no_edit_access', 'entity_type' => $entity_type, diff --git a/core/modules/field/tests/modules/field_test/field_test.field.inc b/core/modules/field/tests/modules/field_test/field_test.field.inc index 3ac75fa..ffd926d 100644 --- a/core/modules/field/tests/modules/field_test/field_test.field.inc +++ b/core/modules/field/tests/modules/field_test/field_test.field.inc @@ -6,8 +6,10 @@ */ use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; +use Drupal\Core\Entity\Field\FieldInterface; +use Drupal\Core\Session\AccountInterface; use Drupal\field\FieldException; -use Drupal\field\FieldInterface; /** * Implements hook_field_info(). @@ -199,16 +201,16 @@ function field_test_default_value(EntityInterface $entity, $field, $instance) { } /** - * Implements hook_field_access(). + * Implements hook_entity_field_access(). */ -function field_test_field_access($op, FieldInterface $field, $entity_type, $entity, $account) { - if ($field['field_name'] == "field_no_{$op}_access") { +function field_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldInterface $field = NULL) { + if ($field_definition->getFieldName() == "field_no_{$operation}_access") { return FALSE; } // Only grant view access to test_view_field fields when the user has // 'view test_view_field content' permission. - if ($field['field_name'] == 'test_view_field' && $op == 'view' && !$account->hasPermission('view test_view_field content')) { + if ($field_definition->getFieldName() == 'test_view_field' && $operation == 'view' && !$account->hasPermission('view test_view_field content')) { return FALSE; } diff --git a/core/modules/file/file.api.php b/core/modules/file/file.api.php index 2624623..eaf8ba4 100644 --- a/core/modules/file/file.api.php +++ b/core/modules/file/file.api.php @@ -216,7 +216,7 @@ function hook_file_delete(Drupal\file\FileInterface $file) { * that denial may be overridden by another entity controller, making this * grant permissive rather than restrictive. * - * @see hook_field_access(). + * @see hook_entity_field_access(). */ function hook_file_download_access($field, Drupal\Core\Entity\EntityInterface $entity, Drupal\file\FileInterface $file) { if ($entity->entityType() == 'node') { diff --git a/core/modules/file/file.module b/core/modules/file/file.module index 769d480..ef77817 100644 --- a/core/modules/file/file.module +++ b/core/modules/file/file.module @@ -643,7 +643,7 @@ function file_file_download($uri, $field_type = 'file') { foreach ($entities as $entity) { $field = field_info_field($entity_type, $field_name); // Check if access to this field is not disallowed. - if (!field_access('view', $field, $entity_type, $entity)) { + if (!$entity->get($field_name)->access('view')) { $denied = TRUE; continue; } diff --git a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php index afbeeba..e51ba64 100644 --- a/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php +++ b/core/modules/file/lib/Drupal/file/Tests/FilePrivateTest.php @@ -43,7 +43,8 @@ function testPrivateFile() { $field_name = strtolower($this->randomName()); $this->createFileField($field_name, 'node', $type_name, array('uri_scheme' => 'private')); - // Create a field with no view access - see field_test_field_access(). + // Create a field with no view access. See + // field_test_entity_field_access(). $no_access_field_name = 'field_no_view_access'; $this->createFileField($no_access_field_name, 'node', $type_name, array('uri_scheme' => 'private')); diff --git a/core/modules/system/entity.api.php b/core/modules/system/entity.api.php index 8c2c240..c11d44b 100644 --- a/core/modules/system/entity.api.php +++ b/core/modules/system/entity.api.php @@ -710,17 +710,20 @@ function hook_entity_operation_alter(array &$operations, \Drupal\Core\Entity\Ent * @param string $operation * The operation to be performed. See * \Drupal\Core\TypedData\AccessibleInterface::access() for possible values. - * @param \Drupal\Core\Entity\Field\Field $field - * The entity field object on which the operation is to be performed. + * @param \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition + * The field definition * @param \Drupal\Core\Session\AccountInterface $account * The user account to check. + * @param \Drupal\Core\Entity\Field\FieldInterface $items + * (optional) The entity field object on which the operation is to be + * performed. * * @return bool|NULL * TRUE if access should be allowed, FALSE if access should be denied and NULL * if the implementation has no opinion. */ -function hook_entity_field_access($operation, $field, \Drupal\Core\Session\AccountInterface $account) { - if ($field->getName() == 'field_of_interest' && $operation == 'update') { +function hook_entity_field_access($operation, \Drupal\Core\Entity\Field\FieldDefinitionInterface $field_definition, \Drupal\Core\Session\AccountInterface $account, \Drupal\Core\Entity\Field\FieldInterface $items) { + if ($field_definition->getFieldName() == 'field_of_interest' && $operation == 'update') { return user_access('update field of interest', $account); } } @@ -738,9 +741,12 @@ function hook_entity_field_access($operation, $field, \Drupal\Core\Session\Accou * @param array $context * Context array on the performed operation with the following keys: * - operation: The operation to be performed (string). - * - field: The entity field object (\Drupal\Core\Entity\Field\Field). + * - field_definition: The field definition object + * (\Drupal\Core\Entity\Field\FieldDefinitionInterface) * - account: The user account to check access for * (Drupal\user\Entity\User). + * - field: (optional) The entity field object + * (\Drupal\Core\Entity\Field\FieldInterface). */ function hook_entity_field_access_alter(array &$grants, array $context) { $field = $context['field']; diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index 9ca18e7..b781873 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -6,6 +6,8 @@ */ use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\Field\FieldDefinitionInterface; +use Drupal\Core\Entity\Field\FieldInterface; use Drupal\Core\Session\AccountInterface; use Drupal\entity\Entity\EntityFormDisplay; use Drupal\field\Entity\Field; @@ -389,13 +391,15 @@ function entity_test_label_callback($entity_type, $entity, $langcode = NULL) { * * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess() */ -function entity_test_entity_field_access($operation, $field, $account) { - if ($field->getName() == 'field_test_text') { - if ($field->value == 'no access value') { - return FALSE; - } - elseif ($operation == 'delete' && $field->value == 'no delete access value') { - return FALSE; +function entity_test_entity_field_access($operation, FieldDefinitionInterface $field_definition, AccountInterface $account, FieldInterface $field = NULL) { + if ($field_definition->getFieldName() == 'field_test_text') { + if ($field) { + if ($field->value == 'no access value') { + return FALSE; + } + elseif ($operation == 'delete' && $field->value == 'no delete access value') { + return FALSE; + } } } } @@ -406,8 +410,7 @@ function entity_test_entity_field_access($operation, $field, $account) { * @see \Drupal\system\Tests\Entity\FieldAccessTest::testFieldAccess() */ function entity_test_entity_field_access_alter(array &$grants, array $context) { - $field = $context['field']; - if ($field->getName() == 'field_test_text' && $field->value == 'access alter value') { + if ($context['field_definition']->getFieldName() == 'field_test_text' && $context['field']->value == 'access alter value') { $grants[':default'] = FALSE; } } diff --git a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php new file mode 100644 index 0000000..1dfc692 --- /dev/null +++ b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php @@ -0,0 +1,131 @@ + 'Field definition test', + 'description' => 'Unit test for FieldDefinition.', + 'group' => 'Entity' + ); + } + + /** + * Tests field name methods. + */ + public function testFieldName() { + $definition = new FieldDefinition(); + $field_name = $this->randomName(); + $definition->setFieldName($field_name); + $this->assertEquals($field_name, $definition->getFieldName()); + } + + /** + * Tests field label methods. + */ + public function testFieldLabel() { + $definition = new FieldDefinition(); + $label = $this->randomName(); + $definition->setFieldLabel($label); + $this->assertEquals($label, $definition->getFieldLabel()); + } + + /** + * Tests field label methods. + */ + public function testFieldDescription() { + $definition = new FieldDefinition(); + $description = $this->randomName(); + $definition->setFieldDescription($description); + $this->assertEquals($description, $definition->getFieldDescription()); + } + + /** + * Tests field type methods. + */ + public function testFieldType() { + $definition = new FieldDefinition(); + $field_name = $this->randomName(); + $definition->setFieldType($field_name); + $this->assertEquals($field_name, $definition->getFieldType()); + } + + /** + * Tests field settings methods. + */ + public function testFieldSettings() { + $definition = new FieldDefinition(); + $setting = $this->randomName(); + $value = $this->randomName(); + $definition->setFieldSetting($setting, $value); + $this->assertEquals($value, $definition->getFieldSetting($setting)); + $this->assertEquals(array($setting => $value), $definition->getFieldSettings()); + } + + /** + * Tests field settings methods. + */ + public function testFieldDefaultValue() { + $definition = new FieldDefinition(); + $setting = 'default_value'; + $value = $this->randomName(); + $definition->setFieldSetting($setting, $value); + $entity = $this->getMockBuilder('Drupal\Core\Entity\Entity') + ->disableOriginalConstructor() + ->getMock(); + $this->assertEquals($value, $definition->getFieldDefaultValue($entity)); + } + + /** + * Tests field translatable methods. + */ + public function testFieldTranslatable() { + $definition = new FieldDefinition(); + $this->assertFalse($definition->isFieldTranslatable()); + $definition->setTranslatable(TRUE); + $this->assertTrue($definition->isFieldTranslatable()); + $definition->setTranslatable(FALSE); + $this->assertFalse($definition->isFieldTranslatable()); + } + + /** + * Tests field cardinality. + */ + public function testFieldCardinality() { + $definition = new FieldDefinition(); + $this->assertEquals(1, $definition->getFieldCardinality()); + // @todo: Add more tests when this can be controlled. + } + + /** + * Tests required. + */ + public function testFieldRequired() { + $definition = new FieldDefinition(); + $this->assertFalse($definition->isFieldRequired()); + } + + /** + * Tests configurable. + */ + public function testFieldConfigurable() { + $definition = new FieldDefinition(); + $this->assertFalse($definition->isFieldConfigurable()); + } + +}