Commit b21ecfc0 authored by fago's avatar fago
Browse files

Issue #1031010 by fago, Kristen Pol, tedbow, tim.plunkett, wizonesolutions,...

Issue #1031010 by fago, Kristen Pol, tedbow, tim.plunkett, wizonesolutions, stevector: Support revisions for field collections.
parent 79c22254
......@@ -32,5 +32,5 @@ Restrictions
-------------
* As of now, the field collection field does not properly respect different
revisions or languages of the host entity. Thus, for now it is suggested to
only use the field for entities that are not revisionable and translatable.
\ No newline at end of file
languages of the host entity. Thus, for now it is suggested to only use the
field for entities that are not translatable.
\ No newline at end of file
......@@ -18,19 +18,54 @@ function field_collection_schema() {
'not null' => TRUE,
'description' => 'Primary Key: Unique field collection item ID.',
),
'revision_id' => array(
'type' => 'int',
'not null' => TRUE,
'description' => 'Default revision ID.',
),
'field_name' => array(
'description' => 'The name of the field on the host entity embedding this entity.',
'type' => 'varchar',
'length' => 32,
'not null' => TRUE,
),
'archived' => array(
'description' => 'Boolean indicating whether the field collection item is archived.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
),
),
'primary key' => array('item_id'),
);
$schema['field_collection_item_revision'] = array(
'description' => 'Stores revision information about field collection items.',
'fields' => array(
'revision_id' => array(
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique revision ID.',
),
'item_id' => array(
'type' => 'int',
'not null' => TRUE,
'description' => 'Field collection item ID.',
),
),
'primary key' => array('revision_id'),
'indexes' => array(
'item_id' => array('item_id'),
),
'foreign keys' => array(
'versioned_field_collection_item' => array(
'table' => 'field_collection_item',
'columns' => array('item_id' => 'item_id'),
),
),
);
return $schema;
}
/**
* Implements hook_field_schema().
*/
......@@ -38,7 +73,13 @@ function field_collection_field_schema($field) {
$columns = array(
'value' => array(
'type' => 'int',
'not null' => FALSE
'not null' => FALSE,
'description' => 'The field collection item id.',
),
'revision_id' => array(
'type' => 'int',
'not null' => FALSE,
'description' => 'The field collection item revision id.',
),
);
return array(
......@@ -55,3 +96,100 @@ function field_collection_update_7000() {
->condition('permission', 'administer field-collections')
->execute();
}
/**
* Add revision support.
*/
function field_collection_update_7001() {
// Add revision_id column to field_collection_item table.
$revision_id_spec = array(
'type' => 'int',
'not null' => TRUE,
'description' => 'Default revision ID.',
// Set default to 0 temporarily.
'initial' => 0,
);
db_add_field('field_collection_item', 'revision_id', $revision_id_spec);
// Initialize the revision_id to be the same as the item_id.
db_update('field_collection_item')
->expression('revision_id', 'item_id')
->execute();
// Add the archived column
$archived_spec = array(
'description' => 'Boolean indicating whether the field collection item is archived.',
'type' => 'int',
'not null' => TRUE,
'default' => 0,
);
db_add_field('field_collection_item', 'archived', $archived_spec);
// Create the new table. It is important to explicitly define the schema here
// rather than use the hook_schema definition: http://drupal.org/node/150220.
$schema['field_collection_item_revision'] = array(
'description' => 'Stores revision information about field collection items.',
'fields' => array(
'revision_id' => array(
'type' => 'serial',
'not null' => TRUE,
'description' => 'Primary Key: Unique revision ID.',
),
'item_id' => array(
'type' => 'int',
'not null' => TRUE,
'description' => 'Field collection item ID.',
),
),
'primary key' => array('revision_id'),
'indexes' => array(
'item_id' => array('item_id'),
),
'foreign keys' => array(
'versioned_field_collection_item' => array(
'table' => 'field_collection_item',
'columns' => array('item_id' => 'item_id'),
),
),
);
db_create_table('field_collection_item_revision', $schema['field_collection_item_revision']);
// Fill the new table with the correct data.
$items = db_select('field_collection_item', 'fci')
->fields('fci')
->execute();
foreach ($items as $item) {
// Update field_collection_item_revision table.
db_insert('field_collection_item_revision')
->fields(array(
'revision_id' => $item->item_id,
'item_id' => $item->item_id,
))
->execute();
}
// Update the field_collection_field_schema columns for all tables.
foreach (field_read_fields(array('type' => 'field_collection')) as $field_name => $field) {
$table_prefixes = array('field_data', 'field_revision');
foreach ($table_prefixes as $table_prefix) {
$table = sprintf('%s_%s', $table_prefix, $field_name);
$value_column = sprintf('%s_value', $field_name);
$revision_id_column = sprintf('%s_revision_id', $field_name);
// Add a revision_id column.
$revision_id_spec['description'] = 'The field collection item revision id.';
db_add_field($table, $revision_id_column, $revision_id_spec);
// Initialize the revision_id to be the same as the item_id.
db_update($table)
->expression($revision_id_column, $value_column)
->execute();
}
}
// Need to get the system up-to-date so drupal_schema_fields_sql() will work.
$schema = drupal_get_schema('field_collection_item_revision', TRUE);
}
This diff is collapsed.
......@@ -85,7 +85,7 @@ function field_collection_item_delete_confirm($form, &$form_state, $field_collec
*/
function field_collection_item_delete_confirm_submit($form, &$form_state) {
$field_collection_item = $form_state['field_collection_item'];
$field_collection_item->delete();
$field_collection_item->deleteRevision();
drupal_set_message(t('%label has been deleted.', array('%label' => drupal_ucfirst($field_collection_item->label()))));
$form_state['redirect'] = '<front>';
}
......
......@@ -55,7 +55,8 @@ class FieldCollectionBasicTestCase extends DrupalWebTestCase {
$entity->save();
$node = node_load($node->nid, NULL, TRUE);
$this->assertEqual($entity->item_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['value'], 'a field_collection has been successfully created.');
$this->assertEqual($entity->item_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['value'], 'A field_collection has been successfully created and referenced.');
$this->assertEqual($entity->revision_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id'], 'A field_collection has been successfully created and referenced.');
// Test adding an additional field_collection during node edit.
$entity2 = entity_create('field_collection_item', array('field_name' => $this->field_name));
......@@ -63,9 +64,11 @@ class FieldCollectionBasicTestCase extends DrupalWebTestCase {
node_save($node);
$node = node_load($node->nid, NULL, TRUE);
$this->assertTrue(!empty($entity2->item_id), 'field_collection has been saved.');
$this->assertTrue(!empty($entity2->item_id) && !empty($entity2->revision_id), 'Field_collection has been saved.');
$this->assertEqual($entity->item_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['value'], 'Existing reference has been kept during update.');
$this->assertEqual($entity->revision_id, $node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id'], 'Existing reference has been kept during update (revision).');
$this->assertEqual($entity2->item_id, $node->{$this->field_name}[LANGUAGE_NONE][1]['value'], 'New field_collection has been properly referenced');
$this->assertEqual($entity2->revision_id, $node->{$this->field_name}[LANGUAGE_NONE][1]['revision_id'], 'New field_collection has been properly referenced (revision)');
// Make sure deleting the field_collection removes the reference.
$entity2->delete();
......@@ -89,7 +92,7 @@ class FieldCollectionBasicTestCase extends DrupalWebTestCase {
// Now the node should have been saved with the collection and the link
// should have been established.
$this->assertTrue(!empty($node->nid), 'Node has been saved with the collection.');
$this->assertTrue(count($node->{$this->field_name}[LANGUAGE_NONE]) == 1 && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']), 'Link has been established.');
$this->assertTrue(count($node->{$this->field_name}[LANGUAGE_NONE]) == 1 && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']) && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']), 'Link has been established.');
// Again, test creating a field collection with a not-yet saved host entity,
// but this time save both entities via the host.
......@@ -97,8 +100,98 @@ class FieldCollectionBasicTestCase extends DrupalWebTestCase {
$entity = entity_create('field_collection_item', array('field_name' => $this->field_name));
$entity->setHostEntity('node', $node);
node_save($node);
$this->assertTrue(!empty($entity->item_id), 'Collection has been saved with the host.');
$this->assertTrue(count($node->{$this->field_name}[LANGUAGE_NONE]) == 1 && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']), 'Link has been established.');
$this->assertTrue(!empty($entity->item_id) && !empty($entity->revision_id), 'Collection has been saved with the host.');
$this->assertTrue(count($node->{$this->field_name}[LANGUAGE_NONE]) == 1 && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']) && !empty($node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']), 'Link has been established.');
// Test Revisions.
$node = $this->drupalCreateNode(array('type' => 'article'));
$item = entity_create('field_collection_item', array('field_name' => $this->field_name));
$item->setHostEntity('node', $node);
$item->save();
// Test saving a new revision of a node.
$node->revision = TRUE;
node_save($node);
$item_updated = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
$this->assertNotEqual($item->revision_id, $item_updated->revision_id, 'Creating a new host entity revision creates a new field collection revision.');
// Test saving the node without creating a new revision.
$item = $item_updated;
$node->revision = FALSE;
node_save($node);
$item_updated = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
$this->assertEqual($item->revision_id, $item_updated->revision_id, 'Updating a new host entity without creating a new revision does not create a new field collection revision.');
// Create a new revision of the node, such we have a non default node and
// field collection revision. Then test using it.
$vid = $node->vid;
$item_revision_id = $item_updated->revision_id;
$node->revision = TRUE;
node_save($node);
$item_updated = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
$this->assertNotEqual($item_revision_id, $item_updated->revision_id, 'Creating a new host entity revision creates a new field collection revision.');
$this->assertTrue($item_updated->isDefaultRevision(), 'Field collection of default host entity revision is default too.');
$this->assertEqual($item_updated->hostEntityId(), $node->nid, 'Can access host entity ID of default field collection revision.');
$this->assertEqual($item_updated->hostEntity()->vid, $node->vid, 'Loaded default host entity revision.');
$item = entity_revision_load('field_collection_item', $item_revision_id);
$this->assertFalse($item->isDefaultRevision(), 'Field collection of non-default host entity is non-default too.');
$this->assertEqual($item->hostEntityId(), $node->nid, 'Can access host entity ID of non-default field collection revision.');
$this->assertEqual($item->hostEntity()->vid, $vid, 'Loaded non-default host entity revision.');
// Delete the non-default revision and make sure the field collection item
// revision has been deleted too.
entity_revision_delete('node', $vid);
$this->assertFalse(entity_revision_load('node', $vid), 'Host entity revision deleted.');
$this->assertFalse(entity_revision_load('field_collection_item', $item_revision_id), 'Field collection item revision deleted.');
// Test having archived field collections, i.e. collections referenced only
// in non-default revisions.
$node = $this->drupalCreateNode(array('type' => 'article'));
// Manually create a field_collection.
$item = entity_create('field_collection_item', array('field_name' => $this->field_name));
$item->setHostEntity('node', $node);
$item->save();
// Create two revisions.
$node_vid = $node->vid;
$node->revision = TRUE;
node_save($node);
$node_vid2 = $node->vid;
$node->revision = TRUE;
node_save($node);
// Now delete the field collection item for the default revision.
$item = field_collection_item_load($node->{$this->field_name}[LANGUAGE_NONE][0]['value']);
$item_revision_id = $item->revision_id;
$item->deleteRevision();
$node = node_load($node->nid);
$this->assertTrue(!isset($node->{$this->field_name}[LANGUAGE_NONE][0]), 'Field collection item revision removed from host.');
$this->assertFalse(field_collection_item_revision_load($item->revision_id), 'Field collection item default revision deleted.');
$item = field_collection_item_load($item->item_id);
$this->assertNotEqual($item->revision_id, $item_revision_id, 'Field collection default revision has been updated.');
$this->assertTrue($item->archived, 'Field collection item has been archived.');
$this->assertFalse($item->isInUse(), 'Field collection item specified as not in use.');
$this->assertTrue($item->isDefaultRevision(), 'Field collection of non-default host entity is default (but archived).');
$this->assertEqual($item->hostEntityId(), $node->nid, 'Can access host entity ID of non-default field collection revision.');
$this->assertEqual($item->hostEntity()->nid, $node->nid, 'Loaded non-default host entity revision.');
// Test deleting a revision of an archived field collection.
$node_revision2 = node_load($node->nid, $node_vid2);
$item = field_collection_item_revision_load($node_revision2->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']);
$item->deleteRevision();
// There should be one revision left, so the item should still exist.
$item = field_collection_item_load($item->item_id);
$this->assertTrue($item->archived, 'Field collection item is still archived.');
$this->assertFalse($item->isInUse(), 'Field collection item specified as not in use.');
// Test that deleting the first node revision deletes the whole field
// collection item as it contains its last revision.
node_revision_delete($node_vid);
$this->assertFalse(field_collection_item_load($item->item_id), 'Archived field collection deleted when last revision deleted.');
}
/**
......@@ -136,6 +229,7 @@ class FieldCollectionBasicTestCase extends DrupalWebTestCase {
$user_privileged = $this->drupalCreateUser(array('access content', 'edit any article content'));
$this->drupalLogin($user_privileged);
$this->drupalGet("node/$node->nid");
$this->assertLinkByHref($path, 0, 'Add link is shown.');
$this->drupalGet($path);
$this->assertText(t('Test text field'), 'Add form is shown.');
......@@ -166,6 +260,26 @@ class FieldCollectionBasicTestCase extends DrupalWebTestCase {
$this->drupalGet($path);
// Add form is shown again.
$this->assertText(t('Test text field'), 'Field collection item has been deleted.');
// Test the viewing a revision. There should be no links to change it.
$vid = $node->vid;
$node = node_load($node->nid, NULL, TRUE);
$node->revision = TRUE;
node_save($node);
$this->drupalGet("node/$node->nid/revisions/$vid/view");
$this->assertResponse(403, 'Access to view revision denied');
// Login in as admin and try again.
$user = $this->drupalCreateUser(array('administer nodes', 'bypass node access'));
$this->drupalLogin($user);
$this->drupalGet("node/$node->nid/revisions/$vid/view");
$this->assertNoResponse(403, 'Access to view revision granted');
$this->assertNoLinkByHref($path, 'No links on revision view.');
$this->assertNoLinkByHref('field-collection/field-test-collection/2/edit', 'No links on revision view.');
$this->assertNoLinkByHref('field-collection/field-test-collection/2/delete', 'No links on revision view.');
$this->drupalGet("node/$node->nid/revisions");
}
}
......@@ -250,7 +364,7 @@ class FieldCollectionRulesIntegrationTestCase extends DrupalWebTestCase {
$node = node_load($node->nid, NULL, TRUE);
$this->assertTrue(!empty($node->{$this->field_name}[LANGUAGE_NONE][0]['value']), 'A field_collection has been successfully created.');
$item_id = $node->{$this->field_name}[LANGUAGE_NONE][0]['value'];
$this->assertTrue(!empty($node->{$this->field_name}[LANGUAGE_NONE][0]['revision_id']), 'A field_collection has been successfully created (revision).');
// Now try making use of the field collection in rules.
$action_set = rules_action_set(array('node' => array('type' => 'node', 'bundle' => 'article')));
......@@ -284,7 +398,7 @@ class FieldCollectionRulesIntegrationTestCase extends DrupalWebTestCase {
$set->action('data_set', array('data:select' => 'node:' . $this->field_name . ':field-text', 'value' => 'bar'));
$set->execute($node);
$this->assertEqual($entity->field_text[LANGUAGE_NONE][0]['value'], 'bar', 'Field collection item used during creation via Rules.');
$this->assertTrue(!empty($entity->item_id), 'Field collection item has been saved by Rules and the host entity.');
$this->assertTrue(!empty($entity->item_id) && !empty($entity->revision_id), 'Field collection item has been saved by Rules and the host entity.');
RulesLog::logger()->checkLog();
}
}
Supports Markdown
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