security updated for entity api

This commit is contained in:
Bachir Soussi Chiadmi 2018-04-24 14:17:02 +02:00
parent 870831757c
commit 8fb74fdf95
24 changed files with 649 additions and 154 deletions

View File

@ -11,7 +11,15 @@ entity CRUD controller, which helps simplifying the creation of new entity types
This is an API module. You only need to enable it if a module depends on it or
you are interested in using it for development.
This README is for interested developers. If you are not interested in
Advanced usage:
---------------
You can optimize cache clearing performance by setting the variable
'entity_rebuild_on_flush' to FALSE. This skips rebuilding of feature
components and exported entities during cache flushing. Instead, it is triggered
by the features module only; e.g., when features are reverted.
The README below is for interested developers. If you are not interested in
developing, you may stop reading now.
--------------------------------------------------------------------------------

View File

@ -0,0 +1,153 @@
<?php
/**
* @file
* Plugin to provide an relationship handler for any entity property.
*/
/**
* Plugins are described by creating a $plugin array which will be used
* by the system that includes this file.
*/
$plugin = array(
'title' => t('Entity property or field (via Entity Metadata Wrapper)'),
'description' => t('Creates any kind of context for entity properties and fields.'),
'context' => 'entity_entity_property_context',
'required context' => new ctools_context_required(t('Entity'), 'entity'),
'edit form' => 'entity_entity_property_edit_form',
'edit form validate' => 'entity_entity_property_edit_form_validate',
'defaults' => array(
'selector' => '',
'target_context' => 'entity',
'concatenator' => ','
),
);
/**
* Return a new context based on an existing context.
*/
function entity_entity_property_context($context, $conf) {
$plugin = $conf['name'];
// If unset it wants a generic, unfilled context, which is just NULL.
if (empty($context->data)) {
return ctools_context_create_empty(isset($conf['target_context']) ? $conf['target_context'] : 'entity', NULL);
}
list($part1, $entity_type) = explode(':', $context->plugin);
if (isset($context->data) && $entity = $context->data) {
// Apply the selector.
$wrapper = entity_metadata_wrapper($entity_type, $entity);
$parts = explode(':', $conf['selector']);
try {
foreach ($parts as $part) {
if (!($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper)) {
$wrapper = NULL;
$value = NULL;
continue;
}
$wrapper = $wrapper->get($part);
}
}
catch (EntityMetadataWrapperException $e) {
$wrapper = NULL;
$value = NULL;
}
if (isset($wrapper)) {
$value = $wrapper->value();
}
// Massage list values.
if ($wrapper instanceof EntityListWrapper) {
$value = $wrapper[0]->value();
$argument = implode($conf['concatenator'], $wrapper->value(array('identifier' => TRUE)));
}
elseif ($wrapper instanceof EntityDrupalWrapper) {
$argument = $wrapper->getIdentifier();
}
// Bail out if we were unable to retrieve the value.
if (!isset($value)) {
return ctools_context_create_empty(isset($conf['target_context']) ? $conf['target_context'] : 'entity', NULL);
}
$context = ctools_context_create($conf['target_context'], $value);
// Provide a suiting argument if context is used as argument.
if (isset($argument)) {
$context->argument = $argument;
}
return $context;
}
}
function entity_entity_property_edit_form($form, &$form_state) {
$conf = $form_state['conf'];
$form['selector'] = array(
'#type' => 'textfield',
'#title' => t('Data selector'),
'#description' => t('Any valid data selector, e.g. "title" to select a node\'s title, or "field_tags:1" to select the second tag.'),
'#default_value' => $conf['selector'],
'#required' => TRUE,
);
$form['concatenator'] = array(
'#title' => t('Concatenator (if multiple)'),
'#type' => 'select',
'#options' => array(',' => ', (AND)', '+' => '+ (OR)'),
'#default_value' => $conf['concatenator'],
'#prefix' => '<div class="clearfix">',
'#suffix' => '</div>',
'#description' => t("When the resulting value is multiple valued and the context is passed on to a view as argument, the value can be concatenated in the form of 1+2+3 (for OR) or 1,2,3 (for AND)."),
);
return $form;
}
function entity_entity_property_edit_form_validate($form, &$form_state) {
$context_key = $form_state['values']['context'];
$context = $form_state['contexts'][$context_key];
$entity_type = $context->type[2];
try {
$all_properties = entity_get_all_property_info($entity_type);
$wrapper = entity_metadata_wrapper($entity_type, NULL, array('property info' => $all_properties));
$parts = explode(':', $form_state['values']['selector']);
foreach ($parts as $part) {
if (!($wrapper instanceof EntityStructureWrapper || $wrapper instanceof EntityListWrapper)) {
form_set_error('selector', t('Unable to apply the data selector part %key.'. array('%key' => $part)));
continue;
}
$wrapper = $wrapper->get($part);
}
$type = entity_entity_property_map_data_type($wrapper->type());
$form_state['conf']['target_context'] = $type;
}
catch (EntityMetadataWrapperException $e) {
form_set_error('selector', t('Unable to apply the data selector on entity type %type. @reason', array('@reason' => $e->getMessage(), '%type' => $entity_type)));
}
// Auto-generate a sane identifier.
if ($form_state['values']['identifier'] == $form['identifier']['#default_value']) {
$form_state['values']['identifier'] = 'Entity property ' . $entity_type . ':' . check_plain($form_state['values']['selector']);
}
}
/**
* Maps an entity-property data type to ctools types.
*/
function entity_entity_property_map_data_type($type) {
// Remove list<> wrappers from types.
while ($item_type = entity_property_list_extract_type($type)) {
$type = $item_type;
}
// Massage data type of entites to c
if (entity_get_info($type)) {
$type = "entity:$type";
}
if ($type == 'text') {
$type = 'string';
}
return $type;
}

View File

@ -7,6 +7,12 @@
/**
* Returns the configured entity features controller.
*
* @param string $type
* The entity type to get the controller for.
*
* @return EntityDefaultFeaturesController
* The configured entity features controller.
*/
function entity_features_get_controller($type) {
$static = &drupal_static(__FUNCTION__);
@ -85,6 +91,7 @@ class EntityDefaultFeaturesController {
$fields = field_info_instances($this->info['bundle of'], $entity->{$this->bundleKey});
foreach ($fields as $name => $field) {
$pipe['field'][] = "{$field['entity_type']}-{$field['bundle']}-{$field['field_name']}";
$pipe['field_instance'][] = "{$field['entity_type']}-{$field['bundle']}-{$field['field_name']}";
}
}
}
@ -125,9 +132,7 @@ class EntityDefaultFeaturesController {
*/
function revert($module = NULL) {
if ($defaults = features_get_default($this->type, $module)) {
foreach ($defaults as $name => $entity) {
entity_delete($this->type, $name);
}
entity_delete_multiple($this->type, array_keys($defaults));
}
}
}
@ -146,6 +151,8 @@ function entity_features_api() {
}
/**
* Implements hook_features_export_options().
*
* Features component callback.
*/
function entity_features_export_options($a1, $a2 = NULL) {
@ -157,6 +164,8 @@ function entity_features_export_options($a1, $a2 = NULL) {
}
/**
* Implements hook_features_export().
*
* Features component callback.
*/
function entity_features_export($data, &$export, $module_name = '', $entity_type) {
@ -164,6 +173,8 @@ function entity_features_export($data, &$export, $module_name = '', $entity_type
}
/**
* Implements hook_features_export_render().
*
* Features component callback.
*/
function entity_features_export_render($module, $data, $export = NULL, $entity_type) {
@ -171,8 +182,23 @@ function entity_features_export_render($module, $data, $export = NULL, $entity_t
}
/**
* Implements hook_features_revert().
*
* Features component callback.
*/
function entity_features_revert($module = NULL, $entity_type) {
return entity_features_get_controller($entity_type)->revert($module);
}
/**
* Implements hook_features_post_restore().
*
* Rebuild all defaults when a features rebuild is triggered - even the ones not
* handled by features itself.
*/
function entity_features_post_restore($op, $items = array()) {
if ($op == 'rebuild') {
// Use features rebuild to rebuild the features independent exports too.
entity_defaults_rebuild();
}
}

View File

@ -25,9 +25,9 @@ files[] = views/handlers/entity_views_handler_field_uri.inc
files[] = views/handlers/entity_views_handler_relationship_by_bundle.inc
files[] = views/handlers/entity_views_handler_relationship.inc
files[] = views/plugins/entity_views_plugin_row_entity_view.inc
; Information added by Drupal.org packaging script on 2015-02-25
version = "7.x-1.6"
; Information added by Drupal.org packaging script on 2018-02-14
version = "7.x-1.9"
core = "7.x"
project = "entity"
datestamp = "1424876582"
datestamp = "1518620551"

View File

@ -13,6 +13,14 @@ function entity_enable() {
entity_entitycache_installed_modules();
}
/**
* Implements hook_uninstall().
*/
function entity_uninstall() {
// Delete variables.
variable_del('entity_rebuild_on_flush');
}
/**
* The entity API modules have been merged into a single module.
*/

View File

@ -184,8 +184,17 @@ function entity_ui_entity_page_view($entity) {
* Gets the page title for the passed operation.
*/
function entity_ui_get_page_title($op, $entity_type, $entity = NULL) {
module_load_include('inc', 'entity', 'includes/entity.ui');
$label = entity_label($entity_type, $entity);
if (isset($entity)) {
module_load_include('inc', 'entity', 'includes/entity.ui');
$label = entity_label($entity_type, $entity);
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
}
else {
$info = entity_get_info($entity_type);
$label = $info['label'];
$bundle = NULL;
}
switch ($op) {
case 'view':
return $label;
@ -200,12 +209,7 @@ function entity_ui_get_page_title($op, $entity_type, $entity = NULL) {
case 'export':
return t('Export @label', array('@label' => $label));
}
if (isset($entity)) {
list(, , $bundle) = entity_extract_ids($entity_type, $entity);
}
else {
$bundle = NULL;
}
return entity_ui_get_action_title($op, $entity_type, $bundle);
}
@ -600,7 +604,7 @@ function entity_id($entity_type, $entity) {
* content language of the current request.
* @param $page
* (optional) If set will control if the entity is rendered: if TRUE
* the entity will be rendered without its title, so that it can be embeded
* the entity will be rendered without its title, so that it can be embedded
* in another context. If FALSE the entity will be displayed with its title
* in a mode suitable for lists.
* If unset, the page mode will be enabled if the current path is the URI
@ -1076,16 +1080,18 @@ function entity_flush_caches() {
// case of enabling or disabling modules we already rebuild defaults in
// entity_modules_enabled() and entity_modules_disabled(), so we do not need
// to do it again.
if (current_path() != 'admin/modules/list/confirm') {
// Also check if rebuilding on cache flush is explicitly disabled.
if (current_path() != 'admin/modules/list/confirm' && variable_get('entity_rebuild_on_flush', TRUE)) {
entity_defaults_rebuild();
}
// Care about entitycache tables.
if (module_exists('entitycache')) {
$tables = array();
foreach (entity_crud_get_info() as $entity_type => $entity_info) {
if (isset($entity_info['module']) && !empty($entity_info['entity cache'])) {
$tables[] = 'cache_entity_' . $entity_type;
$tables_created = variable_get('entity_cache_tables_created');
if (is_array($tables_created)) {
foreach ($tables_created as $module => $entity_cache_tables) {
$tables = array_merge($tables, $entity_cache_tables);
}
}
return $tables;
@ -1372,7 +1378,7 @@ function entity_get_extra_fields_controller($type = NULL) {
* Returns a property wrapper for the given data.
*
* If an entity is wrapped, the wrapper can be used to retrieve further wrappers
* for the entitity properties. For that the wrapper support chaining, e.g. you
* for the entity properties. For that the wrapper support chaining, e.g. you
* can use a node wrapper to get the node authors mail address:
*
* @code
@ -1563,7 +1569,7 @@ function _entity_info_add_metadata(&$entity_info) {
* Implements hook_ctools_plugin_directory().
*/
function entity_ctools_plugin_directory($module, $plugin) {
if ($module == 'ctools' && $plugin == 'content_types') {
return 'ctools/content_types';
if ($module == 'ctools') {
return "ctools/$plugin";
}
}

View File

@ -1342,6 +1342,74 @@ class EntityMetadataNodeRevisionAccessTestCase extends DrupalWebTestCase {
}
}
/**
* Tests basic entity_access() functionality for taxonomy terms.
*/
class EntityMetadataTaxonomyAccessTestCase extends EntityWebTestCase {
public static function getInfo() {
return array(
'name' => 'Entity Metadata Taxonomy Access',
'description' => 'Test entity_access() for taxonomy terms',
'group' => 'Entity API',
);
}
/**
* Asserts entity_access() correctly grants or denies access.
*/
function assertTaxonomyMetadataAccess($ops, $term, $account) {
foreach ($ops as $op => $result) {
$msg = t("entity_access() returns @result with operation '@op'.", array('@result' => $result ? 'TRUE' : 'FALSE', '@op' => $op));
$access = entity_access($op, 'taxonomy_term', $term, $account);
$this->assertEqual($result, $access, $msg);
}
}
/**
* @inheritdoc
*/
function setUp() {
parent::setUp('entity', 'taxonomy');
// Clear permissions for authenticated users.
db_delete('role_permission')
->condition('rid', DRUPAL_AUTHENTICATED_RID)
->execute();
}
/**
* Runs basic tests for entity_access() function.
*/
function testTaxonomyMetadataAccess() {
$vocab = $this->createVocabulary();
$term = entity_property_values_create_entity('taxonomy_term', array(
'name' => $this->randomName(),
'vocabulary' => $vocab,
))->save()->value();
// Clear permissions static cache to get new taxonomy permissions.
drupal_static_reset('checkPermissions');
// Check assignment of view permissions.
$user1 = $this->drupalCreateUser(array('access content'));
$this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => TRUE, 'update' => FALSE, 'delete' => FALSE), $term, $user1);
// Check assignment of edit permissions.
$user2 = $this->drupalCreateUser(array('edit terms in ' . $vocab->vid));
$this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => FALSE, 'update' => TRUE, 'delete' => FALSE), $term, $user2);
// Check assignment of delete permissions.
$user3 = $this->drupalCreateUser(array('delete terms in ' . $vocab->vid));
$this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => FALSE, 'update' => FALSE, 'delete' => TRUE), $term, $user3);
// Check assignment of view, edit, delete permissions.
$user4 = $this->drupalCreateUser(array('access content', 'edit terms in ' . $vocab->vid, 'delete terms in ' . $vocab->vid));
$this->assertTaxonomyMetadataAccess(array('create' => FALSE, 'view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $term, $user4);
// Check assignment of administration permissions.
$user5 = $this->drupalCreateUser(array('administer taxonomy'));
$this->assertTaxonomyMetadataAccess(array('create' => TRUE, 'view' => TRUE, 'update' => TRUE, 'delete' => TRUE), $term, $user5);
}
}
/**
* Tests provided entity property info of the core modules.
*/
@ -1466,6 +1534,7 @@ class EntityMetadataIntegrationTestCase extends EntityWebTestCase {
$book = array('bid' => $node->nid, 'plid' => $node->book['mlid']);
$node2 = $this->drupalCreateNode(array('type' => 'book', 'book' => $book));
$node3 = $this->drupalCreateNode(array('type' => 'page'));
$node4 = $this->drupalCreateNode(array('type' => 'book', 'book' => array('bid' => 0, 'plid' => -1)));
// Test whether the properties work.
$wrapper = entity_metadata_wrapper('node', $node2);
@ -1477,6 +1546,10 @@ class EntityMetadataIntegrationTestCase extends EntityWebTestCase {
$wrapper = entity_metadata_wrapper('node', $node3);
$this->assertEmpty($wrapper, 'book');
$this->assertEmptyArray($wrapper, 'book_ancestors');
// Test a book node which is not contained in a hierarchy.
$wrapper = entity_metadata_wrapper('node', $node4);
$this->assertEmptyArray($wrapper, 'book_ancestors');
}
/**

View File

@ -5,9 +5,9 @@ files[] = entity_token.tokens.inc
files[] = entity_token.module
dependencies[] = entity
; Information added by Drupal.org packaging script on 2015-02-25
version = "7.x-1.6"
; Information added by Drupal.org packaging script on 2018-02-14
version = "7.x-1.9"
core = "7.x"
project = "entity"
datestamp = "1424876582"
datestamp = "1518620551"

View File

@ -107,7 +107,7 @@ interface EntityAPIControllerInterface extends DrupalEntityControllerInterface {
* content language of the current request.
* @param $page
* (optional) If set will control if the entity is rendered: if TRUE
* the entity will be rendered without its title, so that it can be embeded
* the entity will be rendered without its title, so that it can be embedded
* in another context. If FALSE the entity will be displayed with its title
* in a mode suitable for lists.
* If unset, the page mode will be enabled if the current path is the URI

View File

@ -5,6 +5,168 @@
* Provides a base class for entities.
*/
/**
* Interface for class based entities.
*/
interface EntityInterface {
/**
* Returns the internal, numeric identifier.
*
* Returns the numeric identifier, even if the entity type has specified a
* name key. In the latter case, the numeric identifier is supposed to be used
* when dealing generically with entities or internally to refer to an entity,
* i.e. in a relational database. If unsure, use Entity:identifier().
*/
public function internalIdentifier();
/**
* Returns the entity identifier, i.e. the entities name or numeric id.
*
* @return
* The identifier of the entity. If the entity type makes use of a name key,
* the name is returned, else the numeric id.
*
* @see entity_id()
*/
public function identifier();
/**
* Returns the info of the type of the entity.
*
* @see entity_get_info()
*/
public function entityInfo();
/**
* Returns the type of the entity.
*/
public function entityType();
/**
* Returns the bundle of the entity.
*
* @return
* The bundle of the entity. Defaults to the entity type if the entity type
* does not make use of different bundles.
*/
public function bundle();
/**
* Returns the EntityMetadataWrapper of the entity.
*
* @return EntityDrupalWrapper
* An EntityMetadataWrapper containing the entity.
*/
public function wrapper();
/**
* Returns the label of the entity.
*
* Modules may alter the label by specifying another 'label callback' using
* hook_entity_info_alter().
*
* @see entity_label()
*/
public function label();
/**
* Returns the uri of the entity just as entity_uri().
*
* Modules may alter the uri by specifying another 'uri callback' using
* hook_entity_info_alter().
*
* @see entity_uri()
*/
public function uri();
/**
* Checks if the entity has a certain exportable status.
*
* @param $status
* A status constant, i.e. one of ENTITY_CUSTOM, ENTITY_IN_CODE,
* ENTITY_OVERRIDDEN or ENTITY_FIXED.
*
* @return bool
* For exportable entities TRUE if the entity has the status, else FALSE.
* In case the entity is not exportable, NULL is returned.
*
* @see entity_has_status()
*/
public function hasStatus($status);
/**
* Permanently saves the entity.
*
* @see entity_save()
*/
public function save();
/**
* Permanently deletes the entity.
*
* @see entity_delete()
*/
public function delete();
/**
* Exports the entity.
*
* @see entity_export()
*/
public function export($prefix = '');
/**
* Generate an array for rendering the entity.
*
* @see entity_view()
*/
public function view($view_mode = 'full', $langcode = NULL, $page = NULL);
/**
* Builds a structured array representing the entity's content.
*
* @see entity_build_content()
*/
public function buildContent($view_mode = 'full', $langcode = NULL);
/**
* Gets the raw, translated value of a property or field.
*
* Supports retrieving field translations as well as i18n string translations.
*
* Note that this returns raw data values, which might not reflect what
* has been declared for hook_entity_property_info() as no 'getter callbacks'
* are invoked or no referenced entities are loaded. For retrieving values
* reflecting the property info make use of entity metadata wrappers, see
* entity_metadata_wrapper().
*
* @param $property
* The name of the property to return; e.g., 'title'.
* @param $langcode
* (optional) The language code of the language to which the value should
* be translated. If set to NULL, the default display language is being
* used.
*
* @return
* The raw, translated property value; or the raw, un-translated value if no
* translation is available.
*
* @todo Implement an analogous setTranslation() method for updating.
*/
public function getTranslation($property, $langcode = NULL);
/**
* Checks whether the entity is the default revision.
*
* @return Boolean
*
* @see entity_revision_is_default()
*/
public function isDefaultRevision();
}
/**
* A common class for entities.
*
@ -25,7 +187,7 @@
* public $count = 0;
* @endcode
*/
class Entity {
class Entity implements EntityInterface {
protected $entityType;
protected $entityInfo;
@ -34,9 +196,7 @@ class Entity {
protected $wrapper;
/**
* Creates a new entity.
*
* @see entity_create()
* {@inheritdoc}
*/
public function __construct(array $values = array(), $entityType = NULL) {
if (empty($entityType)) {
@ -61,62 +221,42 @@ class Entity {
}
/**
* Returns the internal, numeric identifier.
*
* Returns the numeric identifier, even if the entity type has specified a
* name key. In the latter case, the numeric identifier is supposed to be used
* when dealing generically with entities or internally to refer to an entity,
* i.e. in a relational database. If unsure, use Entity:identifier().
* {@inheritdoc}
*/
public function internalIdentifier() {
return isset($this->{$this->idKey}) ? $this->{$this->idKey} : NULL;
}
/**
* Returns the entity identifier, i.e. the entities name or numeric id.
*
* @return
* The identifier of the entity. If the entity type makes use of a name key,
* the name is returned, else the numeric id.
*
* @see entity_id()
* {@inheritdoc}
*/
public function identifier() {
return isset($this->{$this->nameKey}) ? $this->{$this->nameKey} : NULL;
}
/**
* Returns the info of the type of the entity.
*
* @see entity_get_info()
* {@inheritdoc}
*/
public function entityInfo() {
return $this->entityInfo;
}
/**
* Returns the type of the entity.
* {@inheritdoc}
*/
public function entityType() {
return $this->entityType;
}
/**
* Returns the bundle of the entity.
*
* @return
* The bundle of the entity. Defaults to the entity type if the entity type
* does not make use of different bundles.
* {@inheritdoc}
*/
public function bundle() {
return !empty($this->entityInfo['entity keys']['bundle']) ? $this->{$this->entityInfo['entity keys']['bundle']} : $this->entityType;
}
/**
* Returns the EntityMetadataWrapper of the entity.
*
* @return EntityDrupalWrapper
* An EntityMetadataWrapper containing the entity.
* {@inheritdoc}
*/
public function wrapper() {
if (empty($this->wrapper)) {
@ -130,12 +270,7 @@ class Entity {
}
/**
* Returns the label of the entity.
*
* Modules may alter the label by specifying another 'label callback' using
* hook_entity_info_alter().
*
* @see entity_label()
* {@inheritdoc}
*/
public function label() {
// If the default label flag is enabled, this is being invoked recursively.
@ -165,12 +300,7 @@ class Entity {
}
/**
* Returns the uri of the entity just as entity_uri().
*
* Modules may alter the uri by specifying another 'uri callback' using
* hook_entity_info_alter().
*
* @see entity_uri()
* {@inheritdoc}
*/
public function uri() {
if (isset($this->entityInfo['uri callback']) && $this->entityInfo['uri callback'] == 'entity_class_uri') {
@ -188,17 +318,7 @@ class Entity {
}
/**
* Checks if the entity has a certain exportable status.
*
* @param $status
* A status constant, i.e. one of ENTITY_CUSTOM, ENTITY_IN_CODE,
* ENTITY_OVERRIDDEN or ENTITY_FIXED.
*
* @return
* For exportable entities TRUE if the entity has the status, else FALSE.
* In case the entity is not exportable, NULL is returned.
*
* @see entity_has_status()
* {@inheritdoc}
*/
public function hasStatus($status) {
if (!empty($this->entityInfo['exportable'])) {
@ -207,18 +327,14 @@ class Entity {
}
/**
* Permanently saves the entity.
*
* @see entity_save()
* {@inheritdoc}
*/
public function save() {
return entity_get_controller($this->entityType)->save($this);
}
/**
* Permanently deletes the entity.
*
* @see entity_delete()
* {@inheritdoc}
*/
public function delete() {
$id = $this->identifier();
@ -228,55 +344,28 @@ class Entity {
}
/**
* Exports the entity.
*
* @see entity_export()
* {@inheritdoc}
*/
public function export($prefix = '') {
return entity_get_controller($this->entityType)->export($this, $prefix);
}
/**
* Generate an array for rendering the entity.
*
* @see entity_view()
* {@inheritdoc}
*/
public function view($view_mode = 'full', $langcode = NULL, $page = NULL) {
return entity_get_controller($this->entityType)->view(array($this), $view_mode, $langcode, $page);
}
/**
* Builds a structured array representing the entity's content.
*
* @see entity_build_content()
* {@inheritdoc}
*/
public function buildContent($view_mode = 'full', $langcode = NULL) {
return entity_get_controller($this->entityType)->buildContent($this, $view_mode, $langcode);
}
/**
* Gets the raw, translated value of a property or field.
*
* Supports retrieving field translations as well as i18n string translations.
*
* Note that this returns raw data values, which might not reflect what
* has been declared for hook_entity_property_info() as no 'getter callbacks'
* are invoked or no referenced entities are loaded. For retrieving values
* reflecting the property info make use of entity metadata wrappers, see
* entity_metadata_wrapper().
*
* @param $property_name
* The name of the property to return; e.g., 'title'.
* @param $langcode
* (optional) The language code of the language to which the value should
* be translated. If set to NULL, the default display language is being
* used.
*
* @return
* The raw, translated property value; or the raw, un-translated value if no
* translation is available.
*
* @todo Implement an analogous setTranslation() method for updating.
* {@inheritdoc}
*/
public function getTranslation($property, $langcode = NULL) {
$all_info = entity_get_all_property_info($this->entityType);
@ -296,11 +385,7 @@ class Entity {
}
/**
* Checks whether the entity is the default revision.
*
* @return Boolean
*
* @see entity_revision_is_default()
* {@inheritdoc}
*/
public function isDefaultRevision() {
if (!empty($this->entityInfo['entity keys']['revision'])) {

View File

@ -69,7 +69,7 @@ function entity_property_info_defaults() {
* (optiona) The entity type to return properties for.
*
* @return
* An array of info about properties. If the type is ommitted, all known
* An array of info about properties. If the type is omitted, all known
* properties are returned.
*/
function entity_get_all_property_info($entity_type = NULL) {

View File

@ -127,8 +127,8 @@ class EntityDefaultUIController {
* Use per bundle form ids if possible, such that easy per bundle alterations
* are supported too.
*
* Note that for performance reasons, this method is only invoked for forms,
* which receive the entity_type as first argument. Thus any forms added, must
* Note that for performance reasons, this method is invoked only for forms
* which receive the entity_type as first argument. Thus any forms added must
* follow that pattern.
*
* @see entity_forms()
@ -645,7 +645,7 @@ function entity_ui_operation_form($form, &$form_state, $entity_type, $entity, $o
*/
function entity_ui_main_form_defaults($form, &$form_state, $entity = NULL, $op = NULL) {
// Now equals entity_ui_form_defaults() but is still here to keep backward
// compatability.
// compatibility.
return entity_ui_form_defaults($form, $form_state, $form_state['entity_type'], $entity, $op);
}
@ -760,7 +760,7 @@ function entity_ui_validate_machine_name($element, &$form_state) {
function theme_entity_ui_overview_item($variables) {
$output = $variables['url'] ? l($variables['label'], $variables['url']['path'], $variables['url']['options']) : check_plain($variables['label']);
if ($variables['name']) {
$output .= ' <small> (' . t('Machine name') . ': ' . check_plain($variables['name']) . ')</small>';
$output .= ' <small>(' . t('Machine name') . ': ' . check_plain($variables['name']) . ')</small>';
}
return $output;
}

View File

@ -119,7 +119,11 @@ abstract class EntityMetadataWrapper {
*/
public function set($value) {
if (!$this->validate($value)) {
throw new EntityMetadataWrapperException('Invalid data value given. Be sure it matches the required data type and format.');
throw new EntityMetadataWrapperException(t('Invalid data value given. Be sure it matches the required data type and format. Value at !location: !value.', array(
// An exception's message is output through check_plain().
'!value' => is_array($value) || is_object($value) ? var_export($value, TRUE) : $value,
'!location' => $this->debugIdentifierLocation(),
)));
}
$this->clear();
$this->data = $value;
@ -231,6 +235,21 @@ abstract class EntityMetadataWrapper {
return !empty($this->info['parent']) ? $this->info['parent']->propertyAccess($this->info['name'], $op, $account) : TRUE;
}
/**
* Returns a string to use to identify this wrapper in error messages.
*
* @return
* A string that identifies this wrapper and its chain of ancestors, of the
* form 'grandparentidentifier->parentidentifier->identifier'.
*/
public function debugIdentifierLocation() {
$debug = $this->info['name'];
if (isset($this->info['parent'])) {
$debug = $this->info['parent']->debugIdentifierLocation() . '->' . $debug;
}
return $debug;
}
/**
* Prepare for serializiation.
*/
@ -734,7 +753,11 @@ class EntityDrupalWrapper extends EntityStructureWrapper {
*/
public function set($value) {
if (!$this->validate($value)) {
throw new EntityMetadataWrapperException('Invalid data value given. Be sure it matches the required data type and format.');
throw new EntityMetadataWrapperException(t('Invalid data value given. Be sure it matches the required data type and format. Value at !location: !value.', array(
// An exception's message is output through check_plain().
'!value' => is_array($value) || is_object($value) ? var_export($value, TRUE) : $value,
'!location' => $this->debugIdentifierLocation(),
)));
}
if ($this->info['type'] == 'entity' && $value === $this) {
// Nothing to do.
@ -821,7 +844,12 @@ class EntityDrupalWrapper extends EntityStructureWrapper {
}
else {
// This is not a property, so fallback on entity access.
return $this->entityAccess($op == 'edit' ? 'update' : 'view', $account);
if ($op == 'edit') {
// If the operation is "edit" determine if its actually a "create" for
// new un-saved entities, or an "update" for existing ones.
return $this->entityAccess($this->getIdentifier() ? 'update' : 'create', $account);
}
return $this->entityAccess('view', $account);
}
}
@ -909,6 +937,27 @@ class EntityDrupalWrapper extends EntityStructureWrapper {
}
}
/**
* Returns a string to use to identify this wrapper in error messages.
*/
public function debugIdentifierLocation() {
// An entity wrapper can be at the top of the chain or a part of it.
if (isset($this->info['name'])) {
// This wrapper is part of a chain, eg in the position node->author.
// Return the name.
$debug = $this->info['name'];
}
else {
// This is a wrapper for an actual entity: return its type and id.
$debug = $this->info['type'] . '(' . $this->getIdentifier() . ')';
}
if (isset($this->info['parent'])) {
$debug = $this->info['parent']->debugIdentifierLocation() . '->' . $debug;
}
return $debug;
}
/**
* Prepare for serializiation.
*/
@ -1067,7 +1116,7 @@ class EntityListWrapper extends EntityMetadataWrapper implements IteratorAggrega
*/
public function getIterator() {
// In case there is no data available, just iterate over the first item.
return new EntityMetadataWrapperIterator($this, $this->dataAvailable() ? array_keys(parent::value()) : array(0));
return new EntityMetadataWrapperIterator($this, ($this->dataAvailable() && is_array(parent::value())) ? array_keys(parent::value()) : array(0));
}
/**

View File

@ -29,7 +29,7 @@ function entity_metadata_book_get_properties($node, array $options, $name, $enti
case 'book_ancestors':
$ancestors = array();
while (!empty($node->book['plid'])) {
while (!empty($node->book['plid']) && $node->book['plid'] != -1) {
$link = book_link_load($node->book['plid']);
array_unshift($ancestors, $link['nid']);
$node = node_load($link['nid']);
@ -670,9 +670,11 @@ function entity_metadata_field_file_validate_item($items, $context) {
function entity_metadata_no_hook_node_access($op, $node = NULL, $account = NULL) {
// First deal with the case where a $node is provided.
if (isset($node)) {
if ($op == 'create') {
if (empty($node->vid) && in_array($op, array('create', 'update'))) {
// This is a new node or the original node.
if (isset($node->type)) {
return node_access($op, $node->type, $account);
$op = empty($node->nid) || !empty($node->is_new) ? 'create' : 'update';
return node_access($op, $op == 'create' ? $node->type : $node, $account);
}
else {
throw new EntityMalformedException('Permission to create a node was requested but no node type was given.');
@ -796,14 +798,35 @@ function entity_metadata_comment_properties_access($op, $property, $entity = NUL
* Access callback for the taxonomy entities.
*/
function entity_metadata_taxonomy_access($op, $entity = NULL, $account = NULL, $entity_type = NULL) {
if ($entity_type == 'taxonomy_vocabulary') {
return user_access('administer taxonomy', $account);
}
if (isset($entity) && $op == 'update' && !isset($account) && taxonomy_term_edit_access($entity)) {
// If user has administer taxonomy permission then no further checks.
if (user_access('administer taxonomy', $account)) {
return TRUE;
}
if (user_access('administer taxonomy', $account) || user_access('access content', $account) && $op == 'view') {
return TRUE;
switch ($op) {
case "view":
if (user_access('access content', $account)) {
return TRUE;
}
break;
case "update":
if ($entity_type == 'taxonomy_term') {
return user_access("edit terms in $entity->vid", $account);
}
break;
case "create":
if ($entity_type == 'taxonomy_term') {
// Check for taxonomy_access_fix contrib module which adds additional
// permissions to create new terms in a given vocabulary.
if (function_exists('taxonomy_access_fix_access')) {
return taxonomy_access_fix_access('add terms', $entity->vocabulary_machine_name);
}
}
break;
case "delete":
if ($entity_type == 'taxonomy_term') {
return user_access("delete terms in $entity->vid", $account);
}
break;
}
return FALSE;
}

View File

@ -163,4 +163,10 @@ function entity_metadata_node_entity_property_info_alter(&$info) {
'auto creation' => 'entity_property_create_array',
'field' => TRUE,
);
// Make it a list if cardinality is not 1.
$field_body = field_info_field('body');
if (isset($field_body) && $field_body['cardinality'] != 1) {
$info['node']['properties']['body']['type'] = 'list<text_formatted>';
}
}

View File

@ -6,9 +6,9 @@ files[] = entity_feature.module
dependencies[] = entity_test
hidden = TRUE
; Information added by Drupal.org packaging script on 2015-02-25
version = "7.x-1.6"
; Information added by Drupal.org packaging script on 2018-02-14
version = "7.x-1.9"
core = "7.x"
project = "entity"
datestamp = "1424876582"
datestamp = "1518620551"

View File

@ -7,9 +7,9 @@ files[] = entity_test.install
dependencies[] = entity
hidden = TRUE
; Information added by Drupal.org packaging script on 2015-02-25
version = "7.x-1.6"
; Information added by Drupal.org packaging script on 2018-02-14
version = "7.x-1.9"
core = "7.x"
project = "entity"
datestamp = "1424876582"
datestamp = "1518620551"

View File

@ -2,7 +2,7 @@
/**
* @file
* Test moduel for the entity API.
* Test module for the entity API.
*/
/**

View File

@ -5,9 +5,9 @@ dependencies[] = i18n_string
package = Multilingual - Internationalization
core = 7.x
hidden = TRUE
; Information added by Drupal.org packaging script on 2015-02-25
version = "7.x-1.6"
; Information added by Drupal.org packaging script on 2018-02-14
version = "7.x-1.9"
core = "7.x"
project = "entity"
datestamp = "1424876582"
datestamp = "1518620551"

View File

@ -80,9 +80,12 @@ function template_preprocess_entity_property(&$variables, $hook) {
);
// Populate the content with sensible defaults.
if (!isset($variables['content'])) {
if (!isset($element['#content'])) {
$variables['content'] = entity_property_default_render_value_by_type($element['#entity_wrapped']->{$element['#property_name']});
}
else {
$variables['content'] = $element['#content'];
}
}
/**

View File

@ -605,6 +605,28 @@ class EntityDefaultViewsController {
);
break;
case 'duration':
$return += $description + array(
'field' => array(
'real field' => $views_field_name,
'handler' => 'entity_views_handler_field_duration',
'click sortable' => TRUE,
),
'sort' => array(
'real field' => $views_field_name,
'handler' => 'views_handler_sort',
),
'filter' => array(
'real field' => $views_field_name,
'handler' => 'views_handler_filter_numeric',
),
'argument' => array(
'real field' => $views_field_name,
'handler' => 'views_handler_argument_numeric',
),
);
break;
case 'uri':
$return += $description + array(
'field' => array(

View File

@ -18,7 +18,7 @@ class EntityFieldHandlerHelper {
* Provide appropriate default options for a handler.
*/
public static function option_definition($handler) {
if (entity_property_list_extract_type($handler->definition['type'])) {
if (isset($handler->definition['type']) && entity_property_list_extract_type($handler->definition['type'])) {
$options['list']['contains']['mode'] = array('default' => 'collapse');
$options['list']['contains']['separator'] = array('default' => ', ');
$options['list']['contains']['type'] = array('default' => 'ul');
@ -32,7 +32,7 @@ class EntityFieldHandlerHelper {
* Provide an appropriate default option form for a handler.
*/
public static function options_form($handler, &$form, &$form_state) {
if (entity_property_list_extract_type($handler->definition['type'])) {
if (isset($handler->definition['type']) && entity_property_list_extract_type($handler->definition['type'])) {
$form['list']['mode'] = array(
'#type' => 'select',
'#title' => t('List handling'),

View File

@ -40,7 +40,7 @@ class entity_views_handler_area_entity extends views_handler_area {
$form['entity_id'] = array(
'#type' => 'textfield',
'#title' => t('Entity id'),
'#description' => t('Choose the entity you want to display in the area.'),
'#description' => t('Choose the entity you want to display in the area. To render an entity given by a contextual filter use "%1" for the first argument, "%2" for the second, etc.'),
'#default_value' => $this->options['entity_id'],
);
@ -105,6 +105,9 @@ class entity_views_handler_area_entity extends views_handler_area {
* Render an entity using the view mode.
*/
public function render_entity($entity_type, $entity_id, $view_mode) {
$tokens = $this->get_render_tokens();
// Replace argument tokens in entity id.
$entity_id = strtr($entity_id, $tokens);
if (!empty($entity_type) && !empty($entity_id) && !empty($view_mode)) {
$entity = entity_load_single($entity_type, $entity_id);
if (!empty($this->options['bypass_access']) || entity_access('view', $entity_type, $entity)) {
@ -117,4 +120,31 @@ class entity_views_handler_area_entity extends views_handler_area {
return '';
}
}
/**
* Get the 'render' tokens to use for advanced rendering.
*
* This runs through all of the fields and arguments that
* are available and gets their values. This will then be
* used in one giant str_replace().
*/
function get_render_tokens() {
$tokens = array();
if (!empty($this->view->build_info['substitutions'])) {
$tokens = $this->view->build_info['substitutions'];
}
$count = 0;
foreach ($this->view->display_handler->get_handlers('argument') as $arg => $handler) {
$token = '%' . ++$count;
if (!isset($tokens[$token])) {
$tokens[$token] = '';
}
// Use strip tags as there should never be HTML in the path.
// However, we need to preserve special characters like " that
// were removed by check_plain().
$tokens['%' . $count] = $handler->argument;
}
return $tokens;
}
}

View File

@ -88,6 +88,9 @@ class entity_views_plugin_row_entity_view extends views_plugin_row {
public function render($values) {
if ($entity = $this->get_value($values)) {
// Add the view object as views_plugin_row_node_view::render() would.
// Otherwise the views theme suggestions won't work properly.
$entity->view = $this->view;
$render = $this->rendered_content[entity_id($this->entity_type, $entity)];
return drupal_render($render);
}