Commit 065346b3 authored by Dries's avatar Dries

- Patch #535034 by yched: clean up how fields and instances are prepared for runtime.

parent fd22c347
......@@ -822,9 +822,56 @@ function comment_build_content($comment, $build_mode = 'full') {
$comment->content += field_attach_view('comment', $comment, $build_mode);
if (empty($comment->in_preview)) {
$links = array();
$node = node_load($comment->nid);
if ($node->comment == COMMENT_NODE_OPEN) {
if (user_access('administer comments') && user_access('post comments')) {
$links['comment_delete'] = array(
'title' => t('delete'),
'href' => "comment/delete/$comment->cid",
'html' => TRUE,
);
$links['comment_edit'] = array(
'title' => t('edit'),
'href' => "comment/edit/$comment->cid",
'html' => TRUE,
);
$links['comment_reply'] = array(
'title' => t('reply'),
'href' => "comment/reply/$comment->nid/$comment->cid",
'html' => TRUE,
);
if ($comment->status == COMMENT_NOT_PUBLISHED) {
$links['comment_approve'] = array(
'title' => t('approve'),
'href' => "comment/approve/$comment->cid",
'html' => TRUE,
);
}
}
elseif (user_access('post comments')) {
if (comment_access('edit', $comment)) {
$links['comment_edit'] = array(
'title' => t('edit'),
'href' => "comment/edit/$comment->cid",
'html' => TRUE,
);
}
$links['comment_reply'] = array(
'title' => t('reply'),
'href' => "comment/reply/$comment->nid/$comment->cid",
'html' => TRUE,
);
}
else {
$links['comment_forbidden']['title'] = theme('comment_post_forbidden', $node);
$links['comment_forbidden']['html'] = TRUE;
}
}
$comment->content['links']['comment'] = array(
'#theme' => 'links',
'#links' => comment_links($comment),
'#links' => $links,
'#attributes' => array('class' => 'links inline'),
);
}
......@@ -1323,80 +1370,6 @@ function comment_delete_multiple($cids) {
}
}
/**
* Implement hook_link().
*/
function comment_link($type, $object, $build_mode) {
if ($type == 'comment') {
$links = comment_links($object, FALSE);
return $links;
}
}
/**
* Build command links for a comment (e.g.\ edit, reply, delete) with respect to the current user's access permissions.
*
* @param $comment
* The comment to which the links will be related.
* @return
* An associative array containing the links.
*/
function comment_links(&$comment) {
global $user;
$links = array();
$node = node_load($comment->nid);
if ($node->comment == COMMENT_NODE_OPEN) {
if (user_access('administer comments') && user_access('post comments')) {
$links['comment_delete'] = array(
'title' => t('delete'),
'href' => "comment/delete/$comment->cid",
'html' => TRUE,
);
$links['comment_edit'] = array(
'title' => t('edit'),
'href' => "comment/edit/$comment->cid",
'html' => TRUE,
);
$links['comment_reply'] = array(
'title' => t('reply'),
'href' => "comment/reply/$comment->nid/$comment->cid",
'html' => TRUE,
);
if ($comment->status == COMMENT_NOT_PUBLISHED) {
$links['comment_approve'] = array(
'title' => t('approve'),
'href' => "comment/approve/$comment->cid",
'html' => TRUE,
);
}
}
elseif (user_access('post comments')) {
if (comment_access('edit', $comment)) {
$links['comment_edit'] = array(
'title' => t('edit'),
'href' => "comment/edit/$comment->cid",
'html' => TRUE,
);
}
$links['comment_reply'] = array(
'title' => t('reply'),
'href' => "comment/reply/$comment->nid/$comment->cid",
'html' => TRUE,
);
}
else {
$node = node_load($comment->nid);
$links['comment_forbidden']['title'] = theme('comment_post_forbidden', $node);
$links['comment_forbidden']['html'] = TRUE;
}
}
return $links;
}
/**
* Comment operations. Offer different update operations depending on
* which comment administration page is being viewed.
......
......@@ -339,10 +339,6 @@ function field_read_fields($params = array(), $include_additional = array()) {
unset($field['data']);
$field += $data;
// Make sure all settings expected in the current execution context are
// present.
$field['settings'] += field_info_field_settings($field['type']);
module_invoke_all('field_read_field', $field);
// Populate storage information.
......@@ -624,7 +620,6 @@ function field_read_instances($params = array(), $include_additional = array())
$query = db_select('field_config_instance', 'fci', array('fetch' => PDO::FETCH_ASSOC));
$query->join('field_config', 'fc', 'fc.id = fci.field_id');
$query->fields('fci');
$query->fields('fc', array('type'));
// Turn the conditions into a query.
foreach ($params as $key => $value) {
......@@ -654,14 +649,6 @@ function field_read_instances($params = array(), $include_additional = array())
$instance['widget']['module'] = $record['widget_module'];
$instance['widget']['active'] = $record['widget_active'];
// Make sure all settings expected in the current execution context are
// present.
$instance['settings'] += field_info_instance_settings($record['type']);
$instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']);
foreach ($instance['display'] as $build_mode => $display) {
$instance['display'][$build_mode]['settings'] += field_info_formatter_settings($display['type']);
}
module_invoke_all('field_read_instance', $instance);
$instances[] = $instance;
}
......
......@@ -112,14 +112,9 @@ function field_default_view($obj_type, $object, $field, $instance, $items, $buil
list($id, $vid, $bundle) = field_attach_extract_ids($obj_type, $object);
$addition = array();
$display = $instance['display'][$build_mode];
// If we don't have specific settings for the current build_mode, we use the
// (required) 'full' build_mode.
$display = isset($instance['display'][$build_mode]) ? $instance['display'][$build_mode] : $instance['display']['full'];
// Ensure we have a valid formatter and formatter settings.
$display = _field_get_formatter($display, $field);
if ($display['type'] && $display['type'] !== 'hidden') {
if ($display['type'] !== 'hidden') {
$theme = 'field_formatter_' . $display['type'];
$single = (field_behaviors_formatter('multiple values', $display) == FIELD_BEHAVIOR_DEFAULT);
......@@ -174,6 +169,7 @@ function field_default_view($obj_type, $object, $field, $instance, $items, $buil
$addition = array($field['field_name'] => $element);
}
return $addition;
}
......
......@@ -16,29 +16,6 @@
* and settings defined by or with the Field API.
*/
/**
* Return valid formatter type and settings.
*
* Backs up to default formatter and settings if the intended formatter is no
* longer available. This might happen when the formatter has been renamed in
* the module, or if the module has been disabled since then.
*/
function _field_get_formatter($display, $field) {
if ($display['type'] != 'hidden') {
$formatter_type = field_info_formatter_types($display['type']);
if (empty($formatter_type)) {
$field_type = field_info_field_types($field['type']);
$display['type'] = $field_type['default_formatter'];
$formatter_type = field_info_formatter_types($display['type']);
}
$function = $formatter_type['module'] . '_field_formatter_settings';
if (drupal_function_exists($function)) {
$display['settings'] += $function($display['type']);
}
}
return $display;
}
/**
* Collate all information on field types, widget types and related structures.
*
......@@ -169,18 +146,16 @@ function _field_info_collate_types($reset = FALSE) {
* Collate all information on existing fields and instances.
*
* @param $reset
* If TRUE, clear the cache. The information will be rebuilt from the database
* next time it is needed. Defaults to FALSE.
* If TRUE, clear the cache. The information will be rebuilt from the
* database next time it is needed. Defaults to FALSE.
* @return
* If $reset is TRUE, nothing.
* If $reset is FALSE, an array containing the following elements:
*
* - fields: array of all defined Field objects, keyed by field
* name. Each field has an additional element, bundles, which is
* an array of all bundles to which the field is assigned.
* - instances: array whose keys are bundle names and whose values
* are an array, keyed by field name, of all Instance objects in
* that bundle.
* - fields: array of all defined Field objects, keyed by field name. Each
* field has an additional element, bundles, which is an array of all
* bundles to which the field is assigned.
* - instances: array whose keys are bundle names and whose values are an
* array, keyed by field name, of all instances in that bundle.
*/
function _field_info_collate_fields($reset = FALSE) {
static $info;
......@@ -197,13 +172,23 @@ function _field_info_collate_fields($reset = FALSE) {
}
else {
$info = array(
'fields' => field_read_fields(),
'instances' => array_fill_keys(array_keys(field_info_bundles()), array()),
'fields' => array(),
'instances' => array(),
);
// Populate fields
$fields = field_read_fields();
foreach ($fields as $field) {
$field = _field_info_prepare_field($field);
$info['fields'][$field['field_name']] = $field;
}
// Populate instances.
$info['instances'] = array_fill_keys(array_keys(field_info_bundles()), array());
$instances = field_read_instances();
foreach ($instances as $instance) {
$field = $info['fields'][$instance['field_name']];
$instance = _field_info_prepare_instance($instance, $field);
$info['instances'][$instance['bundle']][$instance['field_name']] = $instance;
$info['fields'][$instance['field_name']]['bundles'][] = $instance['bundle'];
}
......@@ -215,6 +200,69 @@ function _field_info_collate_fields($reset = FALSE) {
return $info;
}
/**
* Prepare a field definition for the current run-time context.
*
* Since the field was last saved or updated, new field settings can be
* expected.
*
* @param $field
* The raw field structure as read from the database.
*/
function _field_info_prepare_field($field) {
// Make sure all expected field settings are present.
$field['settings'] += field_info_field_settings($field['type']);
return $field;
}
/**
* Prepare an instance definition for the current run-time context.
*
* Since the instance was last saved or updated, a number of things might have
* changed: widgets or formatters disabled, new settings expected, new build
* modes added...
*
* @param $instance
* The raw instance structure as read from the database.
* @param $field
* The field structure for the instance.
*/
function _field_info_prepare_instance($instance, $field) {
$field_type = field_info_field_types($field['type']);
// Make sure all expected instance settings are present.
$instance['settings'] += field_info_instance_settings($field['type']);
// Fallback to default widget if widget type is not available.
if (!field_info_widget_types($instance['widget']['type'])) {
$instance['widget']['type'] = $field_type['default_widget'];
}
// Make sure all expected widget settings are present.
$instance['widget']['settings'] += field_info_widget_settings($instance['widget']['type']);
foreach ($instance['display'] as $build_mode => $display) {
if ($display['type'] != 'hidden') {
// Fallback to default formatter if formatter type is not available.
if (!field_info_formatter_types($instance['display'][$build_mode]['type'])) {
$instance['display'][$build_mode]['type'] = $field_type['default_formatter'];
}
// Make sure all expected formatter settings are present.
$instance['display'][$build_mode]['settings'] += field_info_formatter_settings($instance['display'][$build_mode]['type']);
}
}
// Fallback to 'full' display settings for unspecified build modes.
$obj_type = field_info_bundle_entity($instance['bundle']);
foreach (field_build_modes($obj_type) as $build_mode => $label) {
if (!isset($instance['display'][$build_mode])) {
$instance['display'][$build_mode] = $instance['display']['full'];
}
}
return $instance;
}
/**
* Helper function for determining the behavior of a widget
* with respect to a given operation.
......
......@@ -391,8 +391,8 @@ function _field_filter_xss_display_allowed_tags() {
* @param $item
* The field item(s) to be formatted (such as $node->field_foo[0],
* or $node->field_foo if the formatter handles multiple values itself)
* @param $formatter_name
* The name of the formatter to use.
* @param $formatter_type
* The name of the formatter type to use.
* @param $node
* Optionally, the containing node object for context purposes and
* field-instance options.
......@@ -402,22 +402,25 @@ function _field_filter_xss_display_allowed_tags() {
* It will have been passed through the necessary check_plain() or check_markup()
* functions as necessary.
*/
function field_format($obj_type, $object, $field, $item, $formatter_name = NULL, $formatter_settings = array()) {
function field_format($obj_type, $object, $field, $item, $formatter_type = NULL, $formatter_settings = array()) {
if (!is_array($field)) {
$field = field_info_field($field);
}
if (field_access('view', $field)) {
// Basically, we need $field, $instance, $obj_type, $object to be able to display a value...
$field_type = field_info_field_types($field['type']);
// We need $field, $instance, $obj_type, $object to be able to display a value...
list(, , $bundle) = field_attach_extract_ids($obj_type, $object);
$instance = field_info_instance($field['field_name'], $bundle);
$display = array(
'type' => $formatter_name,
'type' => $formatter_type ? $formatter_type : $field_type['default_formatter'],
'settings' => $formatter_settings,
);
$display = _field_get_formatter($display, $field);
if ($display['type'] && $display['type'] !== 'hidden') {
$display['settings'] += field_info_formatter_settings($display['type']);
if ($display['type'] !== 'hidden') {
$theme = $formatter['module'] . '_formatter_' . $display['type'];
$element = array(
......@@ -427,6 +430,7 @@ function field_format($obj_type, $object, $field, $item, $formatter_name = NULL,
'#formatter' => $display['type'],
'#settings' => $display['settings'],
'#object' => $object,
'#object_type' => $obj_type,
'#delta' => isset($item['#delta']) ? $item['#delta'] : NULL,
);
......
......@@ -871,6 +871,9 @@ class FieldInfoTestCase extends DrupalWebTestCase {
parent::setUp('field_sql_storage', 'field', 'field_test');
}
/**
* Test that field types and field definitions are correcly cached.
*/
function testFieldInfo() {
// Test that field_test module's fields, widgets, and formatters show up.
$field_test_info = field_test_field_info();
......@@ -943,7 +946,93 @@ class FieldInfoTestCase extends DrupalWebTestCase {
$this->assertTrue($instance < $instances[$instance['field_name']], t('Instance appears in info correctly'));
}
// Test that the field_info settings convenience functions work
/**
* Test that cached field definitions are ready for current runtime context.
*/
function testFieldPrepare() {
$field_definition = array(
'field_name' => 'field',
'type' => 'test_field',
);
field_create_field($field_definition);
// Simulate a stored field definition missing a field setting (e.g. a
// third-party module adding a new field setting has been enabled, and
// existing fields do not know the setting yet).
$data = db_result(db_query('SELECT data FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name'])));
$data = unserialize($data);
$data['settings'] = array();
db_update('field_config')
->fields(array('data' => serialize($data)))
->condition('field_name', $field_definition['field_name'])
->execute();
field_cache_clear();
// Read the field back.
$field = field_info_field($field_definition['field_name']);
// Check that all expected settings are in place.
$field_type = field_info_field_types($field_definition['type']);
$this->assertIdentical($field['settings'], $field_type['settings'], t('All expected default field settings are present.'));
}
/**
* Test that cached instance definitions are ready for current runtime context.
*/
function testInstancePrepare() {
$field_definition = array(
'field_name' => 'field',
'type' => 'test_field',
);
field_create_field($field_definition);
$instance_definition = array(
'field_name' => $field_definition['field_name'],
'bundle' => FIELD_TEST_BUNDLE,
);
field_create_instance($instance_definition);
// Simulate a stored instance definition missing various settings (e.g. a
// third-party module adding instance, widget or display settings has been
// enabled, but existing instances do not know the new settings).
$data = db_result(db_query('SELECT data FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $instance_definition['field_name'], ':bundle' => $instance_definition['bundle'])));
$data = unserialize($data);
$data['settings'] = array();
$data['widget']['settings'] = 'unavailable_widget';
$data['widget']['settings'] = array();
$data['display']['full']['type'] = 'unavailable_formatter';
$data['display']['full']['settings'] = array();
db_update('field_config_instance')
->fields(array('data' => serialize($data)))
->condition('field_name', $instance_definition['field_name'])
->condition('bundle', $instance_definition['bundle'])
->execute();
field_cache_clear();
// Read the instance back.
$instance = field_info_instance($instance_definition['field_name'], $instance_definition['bundle']);
// Check that all expected instance settings are in place.
$field_type = field_info_field_types($field_definition['type']);
$this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('All expected instance settings are present.'));
// Check that the default widget is used and expected settings are in place.
$this->assertIdentical($instance['widget']['type'], $field_type['default_widget'], t('Unavailable widget replaced with default widget.'));
$widget_type = field_info_widget_types($instance['widget']['type']);
$this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
// Check that the default formatter is used and expected settings are in place.
foreach (field_build_modes('test_entity') as $build_mode => $label) {
$this->assertIdentical($instance['display'][$build_mode]['type'], $field_type['default_formatter'], t('Unavailable formatter replaced with default formatter in build_mode %build_mode', array('%build_mode' => $build_mode)));
$formatter_type = field_info_formatter_types($instance['display'][$build_mode]['type']);
$this->assertIdentical($instance['display'][$build_mode]['settings'], $formatter_type['settings'] , t('All expected formatter settings are present in build_mode %build_mode', array('%build_mode' => $build_mode)));
}
}
/**
* Test that the field_info settings convenience functions work.
*/
function testSettingsInfo() {
$info = field_test_field_info();
foreach ($info as $type => $data) {
......@@ -1355,23 +1444,9 @@ class FieldCrudTestCase extends DrupalWebTestCase {
);
field_create_field($field_definition);
// Simulate a stored field definition missing a field setting (e.g. a
// third-party module adding a new field setting has been enabled, and
// existing fields do not know the setting yet).
$data = db_query('SELECT data FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name']))->fetchField();
$data = unserialize($data);
$data['settings'] = array();
db_update('field_config')
->fields(array('data' => serialize($data)))
->condition('field_name', $field_definition['field_name'])
->execute();
// Read the field back.
$field = field_read_field($field_definition['field_name']);
// Check that all expected settings are in place.
$field_type = field_info_field_types($field_definition['type']);
$this->assertIdentical($field['settings'], $field_type['settings'], t('All expected default field settings are present.'));
$this->assertTrue($field_definition < $field, t('The field was properly read.'));
}
/**
......@@ -1585,30 +1660,9 @@ class FieldInstanceCrudTestCase extends DrupalWebTestCase {
function testReadFieldInstance() {
field_create_instance($this->instance_definition);
// Simulate a stored instance definition missing various settings (e.g. a
// third-party module adding instance, widget or display settings has been
// enabled, but existing instances do not know the new settings).
$data = db_query('SELECT data FROM {field_config_instance} WHERE field_name = :field_name AND bundle = :bundle', array(':field_name' => $this->instance_definition['field_name'], ':bundle' => $this->instance_definition['bundle']))->fetchField();
$data = unserialize($data);
$data['settings'] = array();
$data['widget']['settings'] = array();
$data['display']['full']['settings'] = array();
db_update('field_config_instance')
->fields(array('data' => serialize($data)))
->condition('field_name', $this->instance_definition['field_name'])
->condition('bundle', $this->instance_definition['bundle'])
->execute();
// Read the instance back.
$instance = field_read_instance($this->instance_definition['field_name'], $this->instance_definition['bundle']);
// Check that all expected settings are in place.
$field_type = field_info_field_types($this->field['type']);
$widget_type = field_info_widget_types($instance['widget']['type']);
$formatter_type = field_info_formatter_types($instance['display']['full']['type']);
$this->assertIdentical($instance['settings'], $field_type['instance_settings'] , t('All expected instance settings are present.'));
$this->assertIdentical($instance['widget']['settings'], $widget_type['settings'] , t('All expected widget settings are present.'));
$this->assertIdentical($instance['display']['full']['settings'], $formatter_type['settings'] , t('All expected formatter settings are present.'));
$this->assertTrue($this->instance_definition < $instance, t('The field was properly read.'));
}
/**
......
......@@ -557,25 +557,6 @@ function hook_link($type, $object, $build_mode) {
return $links;
}
/**
* Perform alterations before links on a comment are rendered. One popular use of
* this hook is to modify/remove links from other modules. If you want to add a link
* to the links section of a node, use hook_link instead.
*
* @param $links
* Nested array of links for the node keyed by providing module.
* @param $node
* A node object that contains the links.
*/
function hook_link_alter(array &$links, $node) {
foreach ($links as $module => $link) {
if (strpos($module, 'taxonomy_term') !== FALSE) {
// Link back to the forum and not the taxonomy term page
$links[$module]['href'] = str_replace('taxonomy/term', 'forum', $link['href']);
}
}
}
/**
* Perform alterations profile items before they are rendered. You may omit/add/re-sort/re-categorize, etc.
*
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment