/** * Check view and edit permissions. * * @param $op * The type of operation. Either 'view' or 'edit'. */ function have_access($op) { global $user; $db = DBConnection::instance(); $field_id = (int) _post('fid'); if (!$field_id) { $field_id = (int) _get('fid'); } $field = (object) $db->dq("SELECT entity_id, entity_type, delta FROM {mytinytodo_fields} WHERE id = ?", $field_id)->fetch_assoc(); $field_info = field_info_field_by_id($field->delta); if ($field->entity_type == 'node') { if (!($node = node_load($field->entity_id))) { return false; } $node_access = $op == 'edit' ? 'update' : $op; if (node_access($node_access, $node, $user) && field_access($op, $field_info, $field->entity_type, $node, $user)) { return true; } } else { if ($field->entity_type == 'user') { if (!($account = user_load($field->entity_id))) { return false; } if (field_access($op, $field_info, $field->entity_type, $account, $user)) { return true; } } else { if ($field->entity_type == 'comment') { if (!($comment = comment_load($field->entity_id))) { return false; } if ($op == 'view' && !user_access('access comments')) { return false; } else { if ($op == 'edit' && !comment_access($op, $comment)) { return false; } } if (field_access($op, $field_info, $field->entity_type, $comment, $user)) { return true; } } else { if (module_exists('entity')) { if (!($entity = entity_load($field_id))) { return false; } $entity_access = $op == 'edit' ? 'update' : $op; if (entity_access($entity_access, $field->entity_type, $entity, $user) && field_access($op, $field_info, $field->entity_type, $entity, $user)) { return true; } } } } } return false; }
/** * Delete a single revision of field data for an entity. * * This hook is invoked from field_attach_delete_revision() to ask the field * storage module to delete field revision data. * * Deleting the current (most recently written) revision is not * allowed as has undefined results. * * @param $entity_type * The entity type of entity, such as 'node' or 'user'. * @param $entity * The entity on which to operate. The revision to delete is * indicated by the entity's revision ID property, as identified by * hook_fieldable_info() for $entity_type. * @param $fields * An array listing the fields to delete. The keys and values of the * array are field IDs. */ function hook_field_storage_delete_revision($entity_type, $entity, $fields) { list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity); if (isset($vid)) { foreach ($fields as $field_id) { $field = field_info_field_by_id($field_id); $revision_name = _field_sql_storage_revision_tablename($field); db_delete($revision_name)->condition('entity_type', $entity_type)->condition('entity_id', $id)->condition('revision_id', $vid)->execute(); } } }
/** * Execute an EntityFieldQuery. * * This hook is called to find the entities having certain entity and field * conditions and sort them in the given field order. If the field storage * engine also handles property sorts and orders, it should unset those * properties in the called object to signal that those have been handled. * * @param EntityFieldQuery $query * An EntityFieldQuery. * * @return * See EntityFieldQuery::execute() for the return values. */ function hook_field_storage_query($query) { $load_current = $options['age'] == FIELD_LOAD_CURRENT; $field = field_info_field_by_id($field_id); $field_name = $field['field_name']; $table = $load_current ? _field_sql_storage_tablename($field) : _field_sql_storage_revision_tablename($field); $field_columns = array_keys($field['columns']); // Build the query. $query = db_select($table, 't'); $query->join('field_config_entity_type', 'e', 't.etid = e.etid'); // Add conditions. foreach ($conditions as $condition) { // A condition is either a (column, value, operator) triple, or a // (column, value) pair with implied operator. @(list($column, $value, $operator) = $condition); // Translate operator and value if needed. switch ($operator) { case 'STARTS_WITH': $operator = 'LIKE'; $value = db_like($value) . '%'; break; case 'ENDS_WITH': $operator = 'LIKE'; $value = '%' . db_like($value); break; case 'CONTAINS': $operator = 'LIKE'; $value = '%' . db_like($value) . '%'; break; } // Translate field columns into prefixed db columns. if (in_array($column, $field_columns)) { $column = _field_sql_storage_columnname($field_name, $column); } // Translate entity types into numeric ids. Expressing the condition on the // local 'etid' column rather than the JOINed 'type' column avoids a // filesort. if ($column == 'type') { $column = 't.etid'; if (is_array($value)) { foreach (array_keys($value) as $key) { $value[$key] = _field_sql_storage_etid($value[$key]); } } else { $value = _field_sql_storage_etid($value); } } // Track condition on 'deleted'. if ($column == 'deleted') { $condition_deleted = TRUE; } $query->condition($column, $value, $operator); } // Exclude deleted data unless we have a condition on it. if (!isset($condition_deleted)) { $query->condition('deleted', 0); } // For a count query, return the count now. if ($options['count']) { return $query->fields('t', array('etid', 'entity_id', 'revision_id'))->distinct()->countQuery()->execute()->fetchField(); } // For a data query, add fields. $query->fields('t', array('bundle', 'entity_id', 'revision_id'))->fields('e', array('type'))->orderBy('t.etid')->orderBy('t.entity_id'); // Initialize results array $return = array(); // Getting $count entities possibly requires reading more than $count rows // since fields with multiple values span over several rows. We query for // batches of $count rows until we've either read $count entities or received // less rows than asked for. $entity_count = 0; do { if ($options['limit'] != FIELD_QUERY_NO_LIMIT) { $query->range($options['cursor'], $options['limit']); } $results = $query->execute(); $row_count = 0; foreach ($results as $row) { $row_count++; $options['cursor']++; // If querying all revisions and the entity type has revisions, we need // to key the results by revision_ids. $entity_type = entity_get_info($row->type); $id = $load_current || empty($entity_type['entity keys']['revision']) ? $row->entity_id : $row->revision_id; if (!isset($return[$row->type][$id])) { $return[$row->type][$id] = entity_create_stub_entity($row->type, array($row->entity_id, $row->revision_id, $row->bundle)); $entity_count++; } } } while ($options['limit'] != FIELD_QUERY_NO_LIMIT && $row_count == $options['limit'] && $entity_count < $options['limit']); // The query is complete when the last batch returns less rows than asked // for. if ($row_count < $options['limit']) { $options['cursor'] = FIELD_QUERY_COMPLETE; } return $return; }
/** * An lightest-weight version of entity save that invokes field storage. * * @param string $entity_type * The entity type of $entity. * @param object $entity * The entity object to update. * @param array $fields * (optional) An optional array of field names that if provided will only * cause those specific fields to be saved, if values are provided. * * @throws \InvalidArgumentException */ public static function updateFieldValuesStorage($entity_type, $entity, array $fields = array()) { list($id, , $bundle) = entity_extract_ids($entity_type, $entity); if (empty($id)) { throw new InvalidArgumentException(t('Cannot call EntityHelper::updateFieldValues() on an unsaved entity.')); } // Let any module update field data before the storage engine, accumulating // saved fields along the way. $skip_fields = array(); foreach (module_implements('field_storage_pre_update') as $module) { $function = $module . '_field_storage_pre_update'; $function($entity_type, $entity, $skip_fields); } // Collect the storage backends used by the remaining fields in the entities. $storages = array(); foreach (field_info_instances($entity_type, $bundle) as $instance) { $field = field_info_field_by_id($instance['field_id']); $field_id = $field['id']; $field_name = $field['field_name']; // Check if we care about saving this field or not. if (!empty($fields) && !in_array($field_name, $fields)) { continue; } // Leave the field untouched if $entity comes with no $field_name property, // but empty the field if it comes as a NULL value or an empty array. // Function property_exists() is slower, so we catch the more frequent // cases where it's an empty array with the faster isset(). if (isset($entity->{$field_name}) || property_exists($entity, $field_name)) { // Collect the storage backend if the field has not been written yet. if (!isset($skip_fields[$field_id])) { $storages[$field['storage']['type']][$field_id] = $field_id; } } } // Field storage backends save any remaining unsaved fields. foreach ($storages as $storage => $storage_fields) { $storage_info = field_info_storage_types($storage); module_invoke($storage_info['module'], 'field_storage_write', $entity_type, $entity, FIELD_STORAGE_UPDATE, $storage_fields); } // Clear the cache for this entity now. entity_get_controller($entity_type)->resetCache(array($id)); }