all contrib module security updates done

This commit is contained in:
2020-09-24 22:47:32 +02:00
parent 7d87153100
commit 0fbb9c4610
1612 changed files with 116663 additions and 32269 deletions
@@ -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
@@ -171,7 +171,7 @@ class EntityAPIController extends DrupalDefaultEntityController implements Entit
if ($this->revisionKey) {
// Compare revision id of the base and revision table, if equal then this
// is the default revision.
$query->addExpression('base.' . $this->revisionKey . ' = revision.' . $this->revisionKey, $this->defaultRevisionKey);
$query->addExpression('CASE WHEN base.' . $this->revisionKey . ' = revision.' . $this->revisionKey . ' THEN 1 ELSE 0 END', $this->defaultRevisionKey);
}
return $query;
}
@@ -373,10 +373,7 @@ class EntityAPIController extends DrupalDefaultEntityController implements Entit
// Do nothing, in case invalid or no ids have been passed.
return;
}
// This transaction causes troubles on MySQL, see
// http://drupal.org/node/1007830. So we deactivate this by default until
// is shipped in a point release.
// $transaction = isset($transaction) ? $transaction : db_transaction();
$transaction = isset($transaction) ? $transaction : db_transaction();
try {
$ids = array_keys($entities);
@@ -400,9 +397,7 @@ class EntityAPIController extends DrupalDefaultEntityController implements Entit
db_ignore_slave();
}
catch (Exception $e) {
if (isset($transaction)) {
$transaction->rollback();
}
$transaction->rollback();
watchdog_exception($this->entityType, $e);
throw $e;
}
@@ -587,6 +582,30 @@ class EntityAPIController extends DrupalDefaultEntityController implements Entit
$entity->content = $content;
$langcode = isset($langcode) ? $langcode : $GLOBALS['language_content']->language;
// Allow modules to change the view mode.
$context = array(
'entity_type' => $this->entityType,
'entity' => $entity,
'langcode' => $langcode,
);
drupal_alter('entity_view_mode', $view_mode, $context);
// Make sure the used view-mode gets stored.
$entity->content += array('#view_mode' => $view_mode);
// By default add in properties for all defined extra fields.
if ($extra_field_controller = entity_get_extra_fields_controller($this->entityType)) {
$wrapper = entity_metadata_wrapper($this->entityType, $entity);
$extra = $extra_field_controller->fieldExtraFields();
$type_extra = &$extra[$this->entityType][$this->entityType]['display'];
$bundle_extra = &$extra[$this->entityType][$wrapper->getBundle()]['display'];
foreach ($wrapper as $name => $property) {
if (isset($type_extra[$name]) || isset($bundle_extra[$name])) {
$this->renderEntityProperty($wrapper, $name, $property, $view_mode, $langcode, $entity->content);
}
}
}
// Add in fields.
if (!empty($this->entityInfo['fieldable'])) {
// Perform the preparation tasks if they have not been performed yet.
@@ -608,6 +627,24 @@ class EntityAPIController extends DrupalDefaultEntityController implements Entit
return $build;
}
/**
* Renders a single entity property.
*/
protected function renderEntityProperty($wrapper, $name, $property, $view_mode, $langcode, &$content) {
$info = $property->info();
$content[$name] = array(
'#label_hidden' => FALSE,
'#label' => $info['label'],
'#entity_wrapped' => $wrapper,
'#theme' => 'entity_property',
'#property_name' => $name,
'#access' => $property->access('view'),
'#entity_type' => $this->entityType,
);
$content['#attached']['css']['entity.theme'] = drupal_get_path('module', 'entity') . '/theme/entity.theme.css';
}
/**
* Implements EntityAPIControllerInterface.
*/
@@ -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,17 +187,16 @@
* public $count = 0;
* @endcode
*/
class Entity {
class Entity implements EntityInterface {
protected $entityType;
protected $entityInfo;
protected $idKey, $nameKey, $statusKey;
protected $defaultLabel = FALSE;
protected $wrapper;
/**
* Creates a new entity.
*
* @see entity_create()
* {@inheritdoc}
*/
public function __construct(array $values = array(), $entityType = NULL) {
if (empty($entityType)) {
@@ -56,68 +217,60 @@ class Entity {
$this->entityInfo = entity_get_info($this->entityType);
$this->idKey = $this->entityInfo['entity keys']['id'];
$this->nameKey = isset($this->entityInfo['entity keys']['name']) ? $this->entityInfo['entity keys']['name'] : $this->idKey;
$this->statusKey = empty($info['entity keys']['status']) ? 'status' : $info['entity keys']['status'];
$this->statusKey = empty($this->entityInfo['entity keys']['status']) ? 'status' : $this->entityInfo['entity keys']['status'];
}
/**
* 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 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 wrapper() {
if (empty($this->wrapper)) {
$this->wrapper = entity_metadata_wrapper($this->entityType, $this);
}
elseif ($this->wrapper->value() !== $this) {
// Wrapper has been modified outside, so let's better create a new one.
$this->wrapper = entity_metadata_wrapper($this->entityType, $this);
}
return $this->wrapper;
}
/**
* {@inheritdoc}
*/
public function label() {
// If the default label flag is enabled, this is being invoked recursively.
@@ -147,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') {
@@ -170,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'])) {
@@ -189,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();
@@ -210,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);
@@ -278,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'])) {
@@ -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) {
@@ -251,7 +251,11 @@ function entity_property_verify_data_type($data, $type) {
return TRUE;
}
elseif (isset($info[$type]['entity keys']['name'])) {
return entity_property_verify_data_type($data, 'token');
// Read the data type of the name key from the metadata if available.
$key = $info[$type]['entity keys']['name'];
$property_info = entity_get_property_info($type);
$property_type = isset($property_info['properties'][$key]['type']) ? $property_info['properties'][$key]['type'] : 'token';
return entity_property_verify_data_type($data, $property_type);
}
return entity_property_verify_data_type($data, empty($info[$type]['fieldable']) ? 'text' : 'integer');
}
@@ -392,7 +396,12 @@ function entity_property_verbatim_get($data, array $options, $name, $type, $info
*/
function entity_property_verbatim_date_get($data, array $options, $name, $type, $info) {
$name = isset($info['schema field']) ? $info['schema field'] : $name;
return is_numeric($data[$name]) ? $data[$name] : strtotime($data[$name], REQUEST_TIME);
if (is_array($data) || (is_object($data) && $data instanceof ArrayAccess)) {
return is_numeric($data[$name]) ? $data[$name] : strtotime($data[$name], REQUEST_TIME);
}
elseif (is_object($data)) {
return is_numeric($data->$name) ? $data->$name : strtotime($data->$name, REQUEST_TIME);
}
}
/**
@@ -503,6 +512,8 @@ function entity_property_text_formatted_info() {
'label' => t('Text format'),
'options list' => 'entity_metadata_field_text_formats',
'getter callback' => 'entity_property_verbatim_get',
'setter callback' => 'entity_property_verbatim_set',
'setter permissions' => 'administer filters',
),
);
}
@@ -6,12 +6,16 @@
*/
/**
* Default controller for providing UI.
* Default UI controller providing admin UI.
*
* This controller suites best for managing configuration entities.
* For a controller suiting content entities, see EntityContentUIController.
*/
class EntityDefaultUIController {
protected $entityType;
protected $entityInfo, $path;
protected $id_count;
/**
* Defines the number of entries to show per page in overview table.
@@ -30,7 +34,8 @@ class EntityDefaultUIController {
*/
public function hook_menu() {
$items = array();
$id_count = count(explode('/', $this->path));
// Set this on the object so classes that extend hook_menu() can use it.
$this->id_count = count(explode('/', $this->path));
$wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object';
$plural_label = isset($this->entityInfo['plural label']) ? $this->entityInfo['plural label'] : $this->entityInfo['label'] . 's';
@@ -60,12 +65,12 @@ class EntityDefaultUIController {
$items[$this->path . '/manage/' . $wildcard] = array(
'title' => 'Edit',
'title callback' => 'entity_label',
'title arguments' => array($this->entityType, $id_count + 1),
'title arguments' => array($this->entityType, $this->id_count + 1),
'page callback' => 'entity_ui_get_form',
'page arguments' => array($this->entityType, $id_count + 1),
'page arguments' => array($this->entityType, $this->id_count + 1),
'load arguments' => array($this->entityType),
'access callback' => 'entity_access',
'access arguments' => array('update', $this->entityType, $id_count + 1),
'access arguments' => array('update', $this->entityType, $this->id_count + 1),
);
$items[$this->path . '/manage/' . $wildcard . '/edit'] = array(
'title' => 'Edit',
@@ -77,7 +82,7 @@ class EntityDefaultUIController {
$items[$this->path . '/manage/' . $wildcard . '/clone'] = array(
'title' => 'Clone',
'page callback' => 'entity_ui_get_form',
'page arguments' => array($this->entityType, $id_count + 1, 'clone'),
'page arguments' => array($this->entityType, $this->id_count + 1, 'clone'),
'load arguments' => array($this->entityType),
'access callback' => 'entity_access',
'access arguments' => array('create', $this->entityType),
@@ -85,10 +90,10 @@ class EntityDefaultUIController {
// Menu item for operations like revert and delete.
$items[$this->path . '/manage/' . $wildcard . '/%'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $id_count + 1, $id_count + 2),
'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $this->id_count + 1, $this->id_count + 2),
'load arguments' => array($this->entityType),
'access callback' => 'entity_access',
'access arguments' => array('delete', $this->entityType, $id_count + 1),
'access arguments' => array('delete', $this->entityType, $this->id_count + 1),
'file' => 'includes/entity.ui.inc',
);
@@ -122,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()
@@ -492,6 +497,128 @@ class EntityDefaultUIController {
}
}
/**
* UI controller providing UI for content entities.
*
* For a controller providing UI for bundleable content entities, see
* EntityBundleableUIController.
* For a controller providing admin UI for configuration entities, see
* EntityDefaultUIController.
*/
class EntityContentUIController extends EntityDefaultUIController {
/**
* Provides definitions for implementing hook_menu().
*/
public function hook_menu() {
$items = parent::hook_menu();
$wildcard = isset($this->entityInfo['admin ui']['menu wildcard']) ? $this->entityInfo['admin ui']['menu wildcard'] : '%entity_object';
// Unset the manage entity path, as the provided UI is for admin entities.
unset($items[$this->path]);
$defaults = array(
'file' => $this->entityInfo['admin ui']['file'],
'file path' => isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']),
);
// Add view, edit and delete menu items for content entities.
$items[$this->path . '/' . $wildcard] = array(
'title callback' => 'entity_ui_get_page_title',
'title arguments' => array('view', $this->entityType, $this->id_count),
'page callback' => 'entity_ui_entity_page_view',
'page arguments' => array($this->id_count),
'load arguments' => array($this->entityType),
'access callback' => 'entity_access',
'access arguments' => array('view', $this->entityType, $this->id_count),
) + $defaults;
$items[$this->path . '/' . $wildcard . '/view'] = array(
'title' => 'View',
'type' => MENU_DEFAULT_LOCAL_TASK,
'load arguments' => array($this->entityType),
'weight' => -10,
) + $defaults;
$items[$this->path . '/' . $wildcard . '/edit'] = array(
'page callback' => 'entity_ui_get_form',
'page arguments' => array($this->entityType, $this->id_count),
'load arguments' => array($this->entityType),
'access callback' => 'entity_access',
'access arguments' => array('edit', $this->entityType, $this->id_count),
'title' => 'Edit',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
) + $defaults;
$items[$this->path . '/' . $wildcard . '/delete'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array($this->entityType . '_operation_form', $this->entityType, $this->id_count, 'delete'),
'load arguments' => array($this->entityType),
'access callback' => 'entity_access',
'access arguments' => array('delete', $this->entityType, $this->id_count),
'title' => 'Delete',
'type' => MENU_LOCAL_TASK,
'context' => MENU_CONTEXT_INLINE,
'file' => $this->entityInfo['admin ui']['file'],
'file path' => isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']),
) + $defaults;
return $items;
}
/**
* Operation form submit callback.
*/
public function operationFormSubmit($form, &$form_state) {
parent::operationFormSubmit($form, $form_state);
// The manage entity path is unset for the content entity UI.
$form_state['redirect'] = '<front>';
}
}
/**
* UI controller providing UI for bundleable content entities.
*
* Adds a bundle selection page to the entity/add path, analogously to the
* node/add path.
*/
class EntityBundleableUIController extends EntityContentUIController {
/**
* Provides definitions for implementing hook_menu().
*/
public function hook_menu() {
$items = parent::hook_menu();
// Extend the 'add' path.
$items[$this->path . '/add'] = array(
'title callback' => 'entity_ui_get_action_title',
'title arguments' => array('add', $this->entityType),
'page callback' => 'entity_ui_bundle_add_page',
'page arguments' => array($this->entityType),
'access callback' => 'entity_access',
'access arguments' => array('create', $this->entityType),
'type' => MENU_LOCAL_ACTION,
);
$items[$this->path . '/add/%'] = array(
'title callback' => 'entity_ui_get_action_title',
'title arguments' => array('add', $this->entityType, $this->id_count + 1),
'page callback' => 'entity_ui_get_bundle_add_form',
'page arguments' => array($this->entityType, $this->id_count + 1),
'access callback' => 'entity_access',
'access arguments' => array('create', $this->entityType),
);
if (!empty($this->entityInfo['admin ui']['file'])) {
// Add in the include file for the entity form.
foreach (array('/add', '/add/%') as $path_end) {
$items[$this->path . $path_end]['file'] = $this->entityInfo['admin ui']['file'];
$items[$this->path . $path_end]['file path'] = isset($this->entityInfo['admin ui']['file path']) ? $this->entityInfo['admin ui']['file path'] : drupal_get_path('module', $this->entityInfo['module']);
}
}
return $items;
}
}
/**
* Form builder function for the overview form.
*
@@ -518,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);
}
@@ -597,39 +724,6 @@ function entity_ui_controller_form_submit($form, &$form_state) {
entity_ui_controller($form_state['entity_type'])->$method($form, $form_state);
}
/**
* Gets the page title for the passed operation.
*/
function entity_ui_get_page_title($op, $entity_type, $entity = NULL) {
$label = entity_label($entity_type, $entity);
switch ($op) {
case 'edit':
return t('Edit @label', array('@label' => $label));
case 'clone':
return t('Clone @label', array('@label' => $label));
case 'revert':
return t('Revert @label', array('@label' => $label));
case 'delete':
return t('Delete @label', array('@label' => $label));
case 'export':
return t('Export @label', array('@label' => $label));
}
return entity_ui_get_action_title($op, $entity_type);
}
/**
* Gets the page/menu title for local action operations.
*/
function entity_ui_get_action_title($op, $entity_type) {
$info = entity_get_info($entity_type);
switch ($op) {
case 'add':
return t('Add @entity_type', array('@entity_type' => drupal_strtolower($info['label'])));
case 'import':
return t('Import @entity_type', array('@entity_type' => drupal_strtolower($info['label'])));
}
}
/**
* Submit builder for the main entity form, which extracts the form values and updates the entity.
*
@@ -666,7 +760,8 @@ 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;
}
@@ -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;
@@ -228,13 +232,24 @@ abstract class EntityMetadataWrapper {
* If there is no access information for this property, TRUE is returned.
*/
public function access($op, $account = NULL) {
if (empty($this->info['parent']) && $this instanceof EntityDrupalWrapper) {
// If there is no parent just incorporate entity based access.
return $this->entityAccess($op == 'edit' ? 'update' : 'view', $account);
}
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.
*/
@@ -500,19 +515,15 @@ class EntityStructureWrapper extends EntityMetadataWrapper implements IteratorAg
protected function propertyAccess($name, $op, $account = NULL) {
$info = $this->getPropertyInfo($name);
// If the property should be accessed and it's an entity, make sure the user
// is allowed to view that entity.
if ($op == 'view' && $this->$name instanceof EntityDrupalWrapper && !$this->$name->entityAccess($op, $account)) {
return FALSE;
}
// If a property should be edited and this is an entity, make sure the user
// has update access for this entity.
// If a property should be edited and this is part of an entity, make sure
// the user has update access for this entity.
if ($op == 'edit') {
$entity = $this;
while (!($entity instanceof EntityDrupalWrapper) && isset($entity->info['parent'])) {
$entity = $entity->info['parent'];
}
if ($entity instanceof EntityDrupalWrapper && !$entity->entityAccess('update', $account)) {
if ($entity instanceof EntityDrupalWrapper && $entity->entityAccess('update', $account) === FALSE) {
return FALSE;
}
}
@@ -523,6 +534,7 @@ class EntityStructureWrapper extends EntityMetadataWrapper implements IteratorAg
elseif ($op == 'edit' && isset($info['setter permission'])) {
return user_access($info['setter permission'], $account);
}
// If access is unknown, we return TRUE.
return TRUE;
}
@@ -662,7 +674,7 @@ class EntityDrupalWrapper extends EntityStructureWrapper {
$this->bundle = $this->type;
}
// Detect the bundle if not set yet and add in properties from the bundle.
elseif (!$this->bundle && !empty($this->entityInfo['fieldable']) && $load && $this->dataAvailable()) {
elseif (!$this->bundle && $load && $this->dataAvailable()) {
try {
if ($entity = $this->value()) {
list($id, $vid, $bundle) = entity_extract_ids($this->type, $entity);
@@ -741,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.
@@ -766,7 +782,7 @@ class EntityDrupalWrapper extends EntityStructureWrapper {
elseif ($this->id === FALSE && !$this->data) {
$this->updateParent(NULL);
}
elseif ($previous_id != $this->id) {
elseif ($previous_id !== $this->id) {
$this->updateParent($this->id);
}
return $this;
@@ -804,6 +820,39 @@ class EntityDrupalWrapper extends EntityStructureWrapper {
return $this->type;
}
/**
* {@inheritdoc}
*
* Note that this method checks property access, but can be used for checking
* entity access *only* if the wrapper is not a property (i.e. has no parent
* wrapper).
* To be safe, better use EntityDrupalWrapper::entityAccess() for checking
* entity access.
*/
public function access($op, $account = NULL) {
if (!empty($this->info['parent'])) {
// If this is a property, make sure the user is able to view the
// currently referenced entity also.
if ($this->entityAccess('view', $account) === FALSE) {
return FALSE;
}
if (parent::access($op, $account) === FALSE) {
return FALSE;
}
// If access is unknown, we return TRUE.
return TRUE;
}
else {
// This is not a property, so fallback on entity access.
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);
}
}
/**
* Checks whether the operation $op is allowed on the entity.
*
@@ -811,6 +860,12 @@ class EntityDrupalWrapper extends EntityStructureWrapper {
*/
public function entityAccess($op, $account = NULL) {
$entity = $this->dataAvailable() ? $this->value() : NULL;
// The value() method could return FALSE on entities such as user 0, so we
// need to use NULL instead to conform to the expectations of
// entity_access().
if ($entity === FALSE) {
$entity = NULL;
}
return entity_access($op, $this->type, $entity, $account);
}
@@ -882,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.
*/
@@ -1025,8 +1101,11 @@ class EntityListWrapper extends EntityMetadataWrapper implements IteratorAggrega
// Support setting lists of fully loaded entities.
if ($this->isEntityList && $values && is_object(reset($values))) {
foreach ($values as $key => $value) {
list($id, $vid, $bundle) = entity_extract_ids($this->itemType, $value);
$values[$key] = $id;
// Ignore outdated NULL value references in lists of entities.
if (isset($value)) {
list($id, $vid, $bundle) = entity_extract_ids($this->itemType, $value);
$values[$key] = $id;
}
}
}
return parent::set($values);
@@ -1037,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));
}
/**