diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php index 267e10c..ceb1618 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php @@ -322,11 +322,11 @@ protected function buildQuery($ids, $revision_id = FALSE) { } // Add fields from the {entity} table. - $entity_fields = $this->entityInfo['schema_fields_sql']['base_table']; + $entity_fields = drupal_schema_fields_sql($this->entityInfo['base_table']); if ($this->revisionKey) { // Add all fields from the {entity_revision} table. - $entity_revision_fields = drupal_map_assoc($this->entityInfo['schema_fields_sql']['revision_table']); + $entity_revision_fields = drupal_map_assoc(drupal_schema_fields_sql($this->entityInfo['revision_table'])); // The id field is provided by entity, so remove it. unset($entity_revision_fields[$this->idKey]); diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php index 6f22d83..86a5544 100644 --- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php +++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php @@ -234,7 +234,7 @@ protected function invokeHook($hook, EntityInterface $entity) { */ protected function mapToStorageRecord(EntityInterface $entity) { $record = new \stdClass(); - foreach ($this->entityInfo['schema_fields_sql']['base_table'] as $name) { + foreach (drupal_schema_fields_sql($this->entityInfo['base_table']) as $name) { $record->$name = $entity->$name->value; } return $record; diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 29f0a36..ecc1e0a 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -282,12 +282,12 @@ protected function processDefinition(&$definition, $plugin_id) { } // Prepare entity schema fields SQL info for // Drupal\Core\Entity\DatabaseStorageControllerInterface::buildQuery(). - if (isset($definition['base_table'])) { - $definition['schema_fields_sql']['base_table'] = drupal_schema_fields_sql($definition['base_table']); - if (isset($definition['revision_table'])) { - $definition['schema_fields_sql']['revision_table'] = drupal_schema_fields_sql($definition['revision_table']); - } - } + //if (isset($definition['base_table'])) { + // $definition['schema_fields_sql']['base_table'] = drupal_schema_fields_sql($definition['base_table']); + // if (isset($definition['revision_table'])) { + // $definition['schema_fields_sql']['revision_table'] = drupal_schema_fields_sql($definition['revision_table']); + // } + //} } } diff --git a/core/modules/comment/comment.install b/core/modules/comment/comment.install index f47151f..a4e87ac 100644 --- a/core/modules/comment/comment.install +++ b/core/modules/comment/comment.install @@ -16,7 +16,7 @@ function comment_uninstall() { variable_del('comment_block_count'); $node_types = array_keys(node_type_get_types()); foreach ($node_types as $node_type) { - field_attach_delete_bundle('comment', 'comment_node_' . $node_type); + field_attach_delete_bundle('comment', 'comment_node_' . $node_type, FALSE); variable_del('comment_' . $node_type); variable_del('comment_anonymous_' . $node_type); variable_del('comment_controls_' . $node_type); diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php index ab03d41..6fd47ab 100644 --- a/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php +++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentFieldsTest.php @@ -41,7 +41,7 @@ function testCommentDefaultFields() { $this->assertTrue(isset($instances['comment_node_' . $type_name]['comment_body']), format_string('The comment_body field is present for comments on type @type', array('@type' => $type_name))); // Delete the instance along the way. - field_delete_instance($instances['comment_node_' . $type_name]['comment_body']); + field_delete_instance($instances['comment_node_' . $type_name]['comment_body']->getArray()); } // Check that the 'comment_body' field is deleted. diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc index a4e227c..5e31a62 100644 --- a/core/modules/field/field.attach.inc +++ b/core/modules/field/field.attach.inc @@ -1585,27 +1585,41 @@ function field_attach_create_bundle($entity_type, $bundle) { * The new name of the bundle. */ function field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { - db_update('field_config_instance') - ->fields(array('bundle' => $bundle_new)) - ->condition('entity_type', $entity_type) - ->condition('bundle', $bundle_old) - ->execute(); + $instances = field_read_instances(); + foreach ($instances as $id => $instance) { + if ($instance['entity_type'] == $entity_type && $instance['bundle'] == $bundle_old) { + // @fieldcmi + $new_instance_id = $instance['entity_type'] . '.' . $bundle_new . '.' . $instance['field_name']; + config('field.instance.' . $instance['entity_type'] . '.' . $bundle_old . '.' . $instance['field_name'])->rename('field.instance.' . $new_instance_id); + config('field.instance.' . $instance['entity_type'] . '.' . $bundle_new . '.' . $instance['field_name']) + ->set('data.bundle', $bundle_new) + ->set('id', $new_instance_id) + ->set('label', $new_instance_id) + ->save(); + } + } // Clear the cache. field_cache_clear(); entity_info_cache_clear(); - // Update bundle settings. - $settings = variable_get('field_bundle_settings_' . $entity_type . '__' . $bundle_old, array()); - variable_set('field_bundle_settings_' . $entity_type . '__' . $bundle_new, $settings); - variable_del('field_bundle_settings_' . $entity_type . '__' . $bundle_old); + // Rename bundle settings. + // @fieldcmi entity_load('bundle', '$entity_type . '.' . $bundle_old');? + if (config('field.bundle.' . $entity_type . '.' . $bundle_old)->get()) { + $new_bundle_id = $entity_type . '.' . $bundle_new; + config('field.bundle.' . $entity_type . '.' . $bundle_old)->rename('field.settings.' . $new_bundle_id); + config('field.settings.' . $new_bundle_id) + ->set('id', $new_bundle_id) + ->set('label', $new_bundle_id) + ->save(); + } // Let other modules act on renaming the bundle. module_invoke_all('field_attach_rename_bundle', $entity_type, $bundle_old, $bundle_new); } /** - * Notifies field.module the a bundle was deleted. + * Notifies field.module that a bundle was deleted. * * This deletes the data for the field instances as well as the field instances * themselves. This function actually just marks the data and field instances as @@ -1618,21 +1632,30 @@ function field_attach_rename_bundle($entity_type, $bundle_old, $bundle_new) { * The entity type to which the bundle is bound. * @param $bundle * The bundle to delete. + * @param $field_cleanup + * If TRUE, the field will be deleted as well if its last instance is being + * deleted. If FALSE, it is the caller's responsibility to handle the case of + * fields left without instances. Defaults to TRUE. */ -function field_attach_delete_bundle($entity_type, $bundle) { +function field_attach_delete_bundle($entity_type, $bundle, $field_cleanup = TRUE) { // First, delete the instances themselves. field_read_instances() must be // used here since field_info_instances() does not return instances for // disabled entity types or bundles. $instances = field_read_instances(array('entity_type' => $entity_type, 'bundle' => $bundle), array('include_inactive' => 1)); foreach ($instances as $instance) { - field_delete_instance($instance); + field_delete_instance($instance, $field_cleanup); } // Clear the cache. field_cache_clear(); // Clear bundle display settings. - variable_del('field_bundle_settings_' . $entity_type . '__' . $bundle); + // @fieldcmi + $bundle = entity_load('bundle', $entity_type . '.' . $bundle); + if ($bundle) { + $bundle->delete(); + } + //config('field.settings.' . $entity_type . '.' . $bundle)->delete(); // Let other modules act on deleting the bundle. module_invoke_all('field_attach_delete_bundle', $entity_type, $bundle, $instances); diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc index b8e2015..06e6297 100644 --- a/core/modules/field/field.crud.inc +++ b/core/modules/field/field.crud.inc @@ -6,6 +6,7 @@ */ use Drupal\field\FieldException; +use Drupal\Field\FieldInstance; /** * @defgroup field_crud Field CRUD API @@ -148,52 +149,48 @@ function field_create_field($field) { ); $field['indexes'] += $schema['indexes']; - // The serialized 'data' column contains everything from $field that does not - // have its own column and is not automatically populated when the field is - // read. - $data = $field; - unset($data['columns'], $data['field_name'], $data['type'], $data['active'], $data['module'], $data['storage_type'], $data['storage_active'], $data['storage_module'], $data['locked'], $data['cardinality'], $data['deleted']); - // Additionally, do not save the 'bundles' property populated by - // field_info_field(). - unset($data['bundles']); - - $record = array( - 'field_name' => $field['field_name'], - 'type' => $field['type'], - 'module' => $field['module'], - 'active' => $field['active'], + $field += array( 'storage_type' => $field['storage']['type'], 'storage_module' => $field['storage']['module'], 'storage_active' => $field['storage']['active'], - 'locked' => $field['locked'], - 'data' => $data, - 'cardinality' => $field['cardinality'], - 'translatable' => $field['translatable'], - 'deleted' => $field['deleted'], ); - // Store the field and get the id back. - drupal_write_record('field_config', $record); - $field['id'] = $record['id']; + // Do not save the 'bundles' property populated by + // field_info_field(). + unset($field['bundles']); + + // Generate id for the field. + $field['id'] = field_generate_id(); + + // Save the configuration. + // @fieldcmi + + $e_field = entity_create('fieldentity', array( + 'id' => $field['field_name'], + 'label' => $field['field_name'], + 'data' => $field, + )); +// $e_field->data = $field; + $e_field->save(); + //config('field.field.' . $field['field_name'])->setData($field)->save(); // Invoke hook_field_storage_create_field after the field is // complete (e.g. it has its id). try { - // Invoke hook_field_storage_create_field after - // drupal_write_record() sets the field id. + // Invoke hook_field_storage_create_field. module_invoke($storage_type['module'], 'field_storage_create_field', $field); } catch (Exception $e) { // If storage creation failed, remove the field_config record before // rethrowing the exception. - db_delete('field_config') - ->condition('id', $field['id']) - ->execute(); + // @fieldcmi + entity_delete_multiple('fieldentity', array($field['field_name'])); + //config('field.field.' . $field['field_name'])->delete(); throw $e; } // Clear caches - field_cache_clear(TRUE); + field_cache_clear(); // Invoke external hooks after the cache is cleared for API consistency. module_invoke_all('field_create_field', $field); @@ -272,26 +269,19 @@ function field_update_field($field) { $storage_type = field_info_storage_types($field['storage']['type']); module_invoke($storage_type['module'], 'field_storage_update_field', $field, $prior_field, $has_data); - // Save the new field definition. @todo: refactor with - // field_create_field. - - // The serialized 'data' column contains everything from $field that does not - // have its own column and is not automatically populated when the field is - // read. - $data = $field; - unset($data['columns'], $data['field_name'], $data['type'], $data['locked'], $data['module'], $data['cardinality'], $data['active'], $data['deleted']); // Additionally, do not save the 'bundles' property populated by // field_info_field(). - unset($data['bundles']); - - $field['data'] = $data; + unset($field['bundles']); - // Store the field and create the id. - $primary_key = array('id'); - drupal_write_record('field_config', $field, $primary_key); + // Store the field. + // @fieldcmi + $e_field = entity_load('fieldentity', $field['field_name']); + $e_field->data = $field; + $e_field->save(); + //config('field.field.' . $field['field_name'])->setData($field)->save(); // Clear caches - field_cache_clear(TRUE); + field_cache_clear(); // Invoke external hooks after the cache is cleared for API consistency. module_invoke_all('field_update_field', $field, $prior_field, $has_data); @@ -341,50 +331,46 @@ function field_read_field($field_name, $include_additional = array()) { * field ID, otherwise it is keyed by field name. */ function field_read_fields($params = array(), $include_additional = array()) { - $query = db_select('field_config', 'fc', array('fetch' => PDO::FETCH_ASSOC)); - $query->fields('fc'); - - // Turn the conditions into a query. - foreach ($params as $key => $value) { - // Allow filtering on the 'entity_type' and 'bundle' columns of the - // field_config_instance table. - if ($key == 'entity_type' || $key == 'bundle') { - if (empty($fci_join)) { - $fci_join = $query->join('field_config_instance', 'fci', 'fc.id = fci.field_id'); - } - $key = 'fci.' . $key; - } + $fields = array(); - $query->condition($key, $value); + // Check if we need to retrieve deleted fields. + $include_deleted = (isset($include_additional['include_deleted']) && $include_additional['include_deleted']) || (isset($params['deleted']) && $params['deleted']); + if ($include_deleted) { + $deleted_fields = state()->get('field.field.deleted') ?: array(); } + // Add active and storage active parameters. if (!isset($include_additional['include_inactive']) || !$include_additional['include_inactive']) { - $query - ->condition('fc.active', 1) - ->condition('fc.storage_active', 1); + $params['active'] = 1; + $params['storage_active'] = 1; } - $include_deleted = (isset($include_additional['include_deleted']) && $include_additional['include_deleted']); - if (!$include_deleted) { - $query->condition('fc.deleted', 0); + + // Get configuration fields. + // @fieldcmi + // $config_fields = config_get_storage_names_with_prefix('field.field'); + $config_fields = entity_load_multiple('fieldentity'); + // Merge deleted fields. + if (!empty($deleted_fields)) { + $config_fields += $deleted_fields; } - $fields = array(); - $results = $query->execute(); - foreach ($results as $record) { - $field = unserialize($record['data']); - $field['id'] = $record['id']; - $field['field_name'] = $record['field_name']; - $field['type'] = $record['type']; - $field['module'] = $record['module']; - $field['active'] = $record['active']; - $field['storage']['type'] = $record['storage_type']; - $field['storage']['module'] = $record['storage_module']; - $field['storage']['active'] = $record['storage_active']; - $field['locked'] = $record['locked']; - $field['cardinality'] = $record['cardinality']; - $field['translatable'] = $record['translatable']; - $field['deleted'] = $record['deleted']; + foreach ($config_fields as $config) { + // @fieldcmi + if (is_object($config)) { + $field = $config->data; + } + else { + $field = $config; + } + + // Conditions. + foreach ($params as $key => $value) { + if ($field[$key] != $value) { + continue 2; + } + } + // Invoke read field. module_invoke_all('field_read_field', $field); // Populate storage information. @@ -411,11 +397,12 @@ function field_read_fields($params = array(), $include_additional = array()) { function field_delete_field($field_name) { // Delete all non-deleted instances. $field = field_info_field($field_name); + if (isset($field['bundles'])) { foreach ($field['bundles'] as $entity_type => $bundles) { foreach ($bundles as $bundle) { $instance = field_info_instance($entity_type, $field_name, $bundle); - field_delete_instance($instance, FALSE); + field_delete_instance($instance->getArray(), FALSE); } } } @@ -423,14 +410,19 @@ function field_delete_field($field_name) { // Mark field data for deletion. module_invoke($field['storage']['module'], 'field_storage_delete_field', $field); - // Mark the field for deletion. - db_update('field_config') - ->fields(array('deleted' => 1)) - ->condition('field_name', $field_name) - ->execute(); + // Delete the configuration of this field and save the field configuration + // in the key_value table so we can use it later during field_purge_batch(). + // This makes sure a new field can be created immediately with the same name. + $deleted_fields = state()->get('field.field.deleted') ?: array(); + $field['deleted'] = TRUE; + $deleted_fields[$field['id']] = $field; + state()->set('field.field.deleted', $deleted_fields); + // @fieldcmi + entity_delete_multiple('fieldentity', array($field['field_name'])); + //config('field.field.' . $field['field_name'])->delete(); // Clear the cache. - field_cache_clear(TRUE); + field_cache_clear(); module_invoke_all('field_delete_field', $field); } @@ -490,6 +482,9 @@ function field_create_instance(&$instance) { // Set the field id. $instance['field_id'] = $field['id']; + // Generate id for the instance. + $instance['id'] = field_generate_id(); + // Note that we do *not* prevent creating a field on non-existing bundles, // because that would break the 'Body as field' upgrade for contrib // node types. @@ -509,7 +504,7 @@ function field_create_instance(&$instance) { throw new FieldException($message); } - _field_write_instance($instance); + _field_write_instance($instance, TRUE); // Clear caches field_cache_clear(); @@ -554,7 +549,7 @@ function field_update_instance($instance) { $instance['id'] = $prior_instance['id']; $instance['field_id'] = $prior_instance['field_id']; - _field_write_instance($instance, TRUE); + _field_write_instance($instance); // Clear caches. field_cache_clear(); @@ -567,10 +562,8 @@ function field_update_instance($instance) { * * @param $instance * An instance structure. - * @param $update - * Whether this is a new or existing instance. */ -function _field_write_instance(&$instance, $update = FALSE) { +function _field_write_instance(&$instance, $create = TRUE) { $field = field_read_field($instance['field_name']); $field_type = field_info_field_types($field['type']); @@ -595,7 +588,6 @@ function _field_write_instance(&$instance, $update = FALSE) { // Set default instance settings. $instance['settings'] += field_info_instance_settings($field['type']); - // Set default widget and settings. $instance['widget'] += array( // TODO: what if no 'default_widget' specified ? @@ -636,30 +628,21 @@ function _field_write_instance(&$instance, $update = FALSE) { $instance['display'][$view_mode] = $display; } - // The serialized 'data' column contains everything from $instance that does - // not have its own column and is not automatically populated when the - // instance is read. - $data = $instance; - unset($data['id'], $data['field_id'], $data['field_name'], $data['entity_type'], $data['bundle'], $data['deleted']); - - $record = array( - 'field_id' => $instance['field_id'], - 'field_name' => $instance['field_name'], - 'entity_type' => $instance['entity_type'], - 'bundle' => $instance['bundle'], - 'data' => $data, - 'deleted' => $instance['deleted'], - ); - // We need to tell drupal_update_record() the primary keys to trigger an - // update. - if ($update) { - $record['id'] = $instance['id']; - drupal_write_record('field_config_instance', $record, array('id')); + // Save into config. + //config('field.instance.' . $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name'])->setData($instance)->save(); + // @fieldcmi + if ($create) { + $e_instance = entity_create('instance', array( + 'id' => $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name'], + 'label' => $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name'], + 'data' => $instance, + )); } else { - drupal_write_record('field_config_instance', $record); - $instance['id'] = $record['id']; + $e_instance = entity_load('instance', $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name']); + $e_instance->data = $instance; } + $e_instance->save(); } /** @@ -694,8 +677,8 @@ function field_read_instance($entity_type, $field_name, $bundle, $include_additi * * @param $param * An array of properties to use in selecting a field instance. Valid keys - * include any column of the field_config_instance table. If NULL, all - * instances will be returned. + * include any property of the instance config object, except for data. + * If NULL, all instances will be returned. * @param $include_additional * The default behavior of this function is to not return field instances that * have been marked deleted, or whose field is inactive. Setting @@ -706,44 +689,77 @@ function field_read_instance($entity_type, $field_name, $bundle, $include_additi * An array of instances matching the arguments. */ function field_read_instances($params = array(), $include_additional = array()) { - $include_inactive = isset($include_additional['include_inactive']) && $include_additional['include_inactive']; - $include_deleted = isset($include_additional['include_deleted']) && $include_additional['include_deleted']; + $instances = 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'); + $deleted_fields = state()->get('field.field.deleted'); + $include_inactive = isset($include_additional['include_inactive']) && $include_additional['include_inactive']; + $include_deleted = (isset($include_additional['include_deleted']) && $include_additional['include_deleted']) || (isset($params['deleted']) && $params['deleted']); - // Turn the conditions into a query. - foreach ($params as $key => $value) { - $query->condition('fci.' . $key, $value); + if ($include_deleted) { + $deleted_instances = state()->get('field.instance.deleted') ?: array(); } - if (!$include_inactive) { - $query - ->condition('fc.active', 1) - ->condition('fc.storage_active', 1); - } - if (!$include_deleted) { - $query->condition('fc.deleted', 0); - $query->condition('fci.deleted', 0); + + // Get configuration instances. + // @fieldcmi + //$config_instances = config_get_storage_names_with_prefix('field.instance'); + $config_instances = entity_load_multiple('instance'); + // Merge deleted instances. + if (!empty($deleted_instances)) { + $config_instances += $deleted_instances; } - $instances = array(); - $results = $query->execute(); + foreach ($config_instances as $config) { + if (is_object($config)) { + $instance = $config->data; + } + else { + $instance = $config; + } + + $entity_info = entity_get_info($instance['entity_type']); + + // Get data from the field. If the field is marked as deleted, we + // need to get it from the state storage. + //$field = config('field.field.' . $instance['field_name'])->get(); + $field = entity_load('fieldentity', $instance['field_name']); + if (is_object($field)) { + $field = $field->data; + } + if ($include_deleted) { + if (empty($field) && isset($deleted_fields[$instance['field_id']])) { + $field = $deleted_fields[$instance['field_id']]; + } + } + if (empty($field)) { + continue; + } + + $instance['active'] = $field['active']; + $instance['locked'] = $field['locked']; + $instance['type'] = $field['type']; + $instance['module'] = $field['module']; + $instance['storage_type'] = $field['storage_type']; + $instance['storage_active'] = $field['storage_active']; + $instance['storage_module'] = $field['storage_module']; - foreach ($results as $record) { - // Filter out instances on unknown entity types (for instance because the - // module exposing them was disabled). - $entity_info = entity_get_info($record['entity_type']); if ($include_inactive || $entity_info) { - $instance = unserialize($record['data']); - $instance['id'] = $record['id']; - $instance['field_id'] = $record['field_id']; - $instance['field_name'] = $record['field_name']; - $instance['entity_type'] = $record['entity_type']; - $instance['bundle'] = $record['bundle']; - $instance['deleted'] = $record['deleted']; + // Conditions. + if (!$include_inactive) { + $params['active'] = 1; + $params['storage_active'] = 1; + } + if (!$include_deleted) { + $params['deleted'] = 0; + } + foreach ($params as $key => $value) { + if ($instance[$key] != $value) { + continue 2; + } + } + // Invoke read instance. module_invoke_all('field_read_instance', $instance); + $instances[] = $instance; } } @@ -761,13 +777,16 @@ function field_read_instances($params = array(), $include_additional = array()) * fields left without instances. Defaults to TRUE. */ function field_delete_instance($instance, $field_cleanup = TRUE) { - // Mark the field instance for deletion. - db_update('field_config_instance') - ->fields(array('deleted' => 1)) - ->condition('field_name', $instance['field_name']) - ->condition('entity_type', $instance['entity_type']) - ->condition('bundle', $instance['bundle']) - ->execute(); + + // Delete the configuration of this instance and save the configuration + // in the key_value table so we can use it later during field_purge_batch(). + $deleted_instances = state()->get('field.instance.deleted') ?: array(); + $instance['deleted'] = TRUE; + $deleted_instances[$instance['id']] = $instance; + state()->set('field.instance.deleted', $deleted_instances); + // @fieldcmi + entity_load('instance', $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name'])->delete(); + //config('field.instance.' . $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name'])->delete(); // Clear the cache. field_cache_clear(); @@ -915,8 +934,8 @@ function field_purge_batch($batch_size) { } // Retrieve all deleted fields. Any that have no instances can be purged. - $fields = field_read_fields(array('deleted' => 1), array('include_deleted' => 1)); - foreach ($fields as $field) { + $deleted_fields = state()->get('field.field.deleted') ?: array(); + foreach ($deleted_fields as $field) { $instances = field_read_instances(array('field_id' => $field['id']), array('include_deleted' => 1)); if (empty($instances)) { field_purge_field($field); @@ -966,14 +985,14 @@ function field_purge_data($entity_type, $entity, $field, $instance) { * The instance record to purge. */ function field_purge_instance($instance) { - db_delete('field_config_instance') - ->condition('id', $instance['id']) - ->execute(); - // Notify the storage engine. $field = field_info_field_by_id($instance['field_id']); module_invoke($field['storage']['module'], 'field_storage_purge_instance', $instance); + $deleted_instances = state()->get('field.instance.deleted'); + unset($deleted_instances[$instance['id']]); + state()->set('field.instance.deleted', $deleted_instances); + // Clear the cache. field_info_cache_clear(); @@ -996,9 +1015,9 @@ function field_purge_field($field) { throw new FieldException(t('Attempt to purge a field @field_name that still has instances.', array('@field_name' => $field['field_name']))); } - db_delete('field_config') - ->condition('id', $field['id']) - ->execute(); + $deleted_fields = state()->get('field.field.deleted'); + unset($deleted_fields[$field['id']]); + state()->set('field.field.deleted', $deleted_fields); // Notify the storage engine. module_invoke($field['storage']['module'], 'field_storage_purge_field', $field); diff --git a/core/modules/field/field.info b/core/modules/field/field.info index 88529e2..b138b52 100644 --- a/core/modules/field/field.info +++ b/core/modules/field/field.info @@ -3,6 +3,6 @@ description = Field API to add fields to entities like nodes and users. package = Core version = VERSION core = 8.x -dependencies[] = field_sql_storage +;dependencies[] = field_sql_storage required = TRUE stylesheets[all][] = theme/field.css diff --git a/core/modules/field/field.install b/core/modules/field/field.install index bc566d6..afef7cd 100644 --- a/core/modules/field/field.install +++ b/core/modules/field/field.install @@ -9,158 +9,7 @@ * Implements hook_schema(). */ function field_schema() { - // Static (meta) tables. - $schema['field_config'] = array( - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'The primary identifier for a field', - ), - 'field_name' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'description' => 'The name of this field. Non-deleted field names are unique, but multiple deleted fields can have the same name.', - ), - 'type' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'description' => 'The type of this field.', - ), - 'module' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The module that implements the field type.', - ), - 'active' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Boolean indicating whether the module that implements the field type is enabled.', - ), - 'storage_type' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'description' => 'The storage backend for the field.', - ), - 'storage_module' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '', - 'description' => 'The module that implements the storage backend.', - ), - 'storage_active' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'description' => 'Boolean indicating whether the module that implements the storage backend is enabled.', - ), - 'locked' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - 'description' => '@TODO', - ), - 'data' => array( - 'type' => 'blob', - 'size' => 'big', - 'not null' => TRUE, - 'serialize' => TRUE, - 'description' => 'Serialized data containing the field properties that do not warrant a dedicated column.', - ), - 'cardinality' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - 'translatable' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - 'deleted' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('id'), - 'indexes' => array( - 'field_name' => array('field_name'), - // Used by field_read_fields(). - 'active' => array('active'), - 'storage_active' => array('storage_active'), - 'deleted' => array('deleted'), - // Used by field_sync_field_status(). - 'module' => array('module'), - 'storage_module' => array('storage_module'), - 'type' => array('type'), - 'storage_type' => array('storage_type'), - ), - ); - $schema['field_config_instance'] = array( - 'fields' => array( - 'id' => array( - 'type' => 'serial', - 'not null' => TRUE, - 'description' => 'The primary identifier for a field instance', - ), - 'field_id' => array( - 'type' => 'int', - 'not null' => TRUE, - 'description' => 'The identifier of the field attached by this instance', - ), - 'field_name' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '' - ), - 'entity_type' => array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => TRUE, - 'default' => '' - ), - 'bundle' => array( - 'type' => 'varchar', - 'length' => 128, - 'not null' => TRUE, - 'default' => '' - ), - 'data' => array( - 'type' => 'blob', - 'size' => 'big', - 'not null' => TRUE, - 'serialize' => TRUE, - ), - 'deleted' => array( - 'type' => 'int', - 'size' => 'tiny', - 'not null' => TRUE, - 'default' => 0, - ), - ), - 'primary key' => array('id'), - 'indexes' => array( - // Used by field_delete_instance(). - 'field_name_bundle' => array('field_name', 'entity_type', 'bundle'), - // Used by field_read_instances(). - 'deleted' => array('deleted'), - ), - ); + $schema['cache_field'] = drupal_get_schema_unprocessed('system', 'cache'); $schema['cache_field']['description'] = 'Cache table for the Field module to store already built field informations.'; @@ -271,7 +120,6 @@ function _update_7000_field_delete_field($field_name) { ->execute(); } - /** * Deletes an instance and all its data of a field stored in SQL Storage. * @@ -382,15 +230,148 @@ function _update_7000_field_create_instance($field, &$instance) { */ /** - * Reassign all list.module fields to be controlled by options.module. + * Convert Field API to CMI. */ function field_update_8001() { - db_update('field_config') - ->fields(array( - 'module' => 'options', - )) - ->condition('module', 'list') - ->execute(); + + // Changes field language into langcode. This used to be in field_sql_storage + // but needs to be here, because otherwhise _update_7000_field_read_fields() + // would fail. + + // Prepare updated schema data structures. + $primary_key_data = array ( + 'entity_type', + 'entity_id', + 'deleted', + 'delta', + 'langcode', + ); + $primary_key_revision = array ( + 'entity_type', + 'entity_id', + 'revision_id', + 'deleted', + 'delta', + 'langcode', + ); + $langcode_index = array( + 'langcode', + ); + $field_langcode = array( + 'type' => 'varchar', + 'length' => 32, + 'not null' => true, + 'default' => '', + ); + + // Retrieve field data. + $fields = _update_7000_field_read_fields(array('storage_type' => 'field_sql_storage')); + + // Update schema. + foreach ($fields as $field) { + $data_table = _field_sql_storage_tablename($field); + if (db_table_exists($data_table)) { + $revision_table = _field_sql_storage_revision_tablename($field); + $table_info = array($data_table => $primary_key_data, $revision_table => $primary_key_revision); + + foreach ($table_info as $table => $primary_key) { + db_drop_primary_key($table); + db_drop_index($table, 'language'); + db_change_field($table, 'language', 'langcode', $field_langcode); + db_add_primary_key($table, $primary_key); + db_add_index($table, 'langcode', $langcode_index); + } + } + } + + // Convert to CMI. + $deleted_fields = state()->get('field.field.deleted') ?: array(); + $deleted_instances = state()->get('field.instance.deleted') ?: array(); + + $field_ids = array(); + $fields = db_query("SELECT * FROM {field_config}")->fetchAll(PDO::FETCH_ASSOC); + foreach ($fields as $field) { + $field += unserialize($field['data']); + unset($field['data']); + + // Generate id for the field. + $old_id = $field['id']; + $field['id'] = field_generate_id(); + + // Reassign all list.module fields to be controlled by options.module. + if ($field['module'] == 'list') { + $field['module'] = 'options'; + } + + $field['storage_type'] = $field['storage']['type']; + $field['storage_module'] = $field['storage']['module']; + $field['storage_active'] = $field['storage']['active']; + + if (!$field['deleted']) { + // @fieldcmi + $e_field = entity_create('fieldentity', array( + 'id' => $field['field_name'], + 'label' => $field['field_name'], + 'data' => $field, + )); + $e_field->save(); + } + else { + $deleted_fields[$field['field_name']] = $field; + } + $field_ids[$old_id] = array( + 'id' => $field['id'], + ); + } + + $instances = db_query("SELECT * FROM {field_config_instance}")->fetchAll(PDO::FETCH_ASSOC); + foreach ($instances as $instance) { + $instance += unserialize($instance['data']); + unset($instance['data']); + + // Map old field id to new UUID. + $old_id = $instance['field_id']; + $instance['field_id'] = $field_ids[$old_id]['id']; + + // Generate id for the instance. + $instance['id'] = field_generate_id(); + + if (!$instance['deleted']) { + entity_create('instance', array( + 'id' => $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name'], + 'label' => $instance['entity_type'] . '.' . $instance['bundle'] . '.' . $instance['field_name'], + 'data' => $instance, + ))->save(); + } + else { + $deleted_instances[$instance['id']] = $instance; + } + } + + // Save the deleted fields and instances. + state()->set('field.field.deleted', $deleted_fields); + state()->set('field.instance.deleted', $deleted_instances); + + // Convert the field bundle settings variables. + $results = db_query("SELECT name, value FROM {variable} WHERE name LIKE 'field_bundle_settings_%'"); + foreach ($results as $record) { + $identifier = str_replace('field_bundle_settings_', '', $record->name); + list($entity_type, $bundle) = explode('__', $identifier); + + // Save the configuration. + entity_create('bundle', array( + 'id' => $entity_type. '.' . $bundle, + 'label' => $entity_type. '.' . $bundle, + 'data' => unserialize($record->value), + ))->save(); + + // Remove the variable. + update_variable_del($record->name); + } + + // Drop the tables. + db_drop_table('field_config'); + db_drop_table('field_config_instance'); } /** diff --git a/core/modules/field/field.module b/core/modules/field/field.module index ac0d1b4..c39af8e 100644 --- a/core/modules/field/field.module +++ b/core/modules/field/field.module @@ -6,6 +6,7 @@ use Drupal\Core\Template\Attribute; use Drupal\Core\Entity\Query\QueryFactory; +use Drupal\Component\Uuid\Uuid; /* * Load all public Field API functions. Drupal currently has no @@ -327,6 +328,79 @@ function field_cron() { field_purge_batch($limit); } + /** + * Implements hook_config_import_create(). + */ +// @fieldcmi +function field_config_import_create($name, $new_config, $old_config) { + + list($module, $type) = explode('.', $name); + if ($module != 'field') { + return; + } + + $config = $new_config->get('data'); + + switch ($type) { + case 'field': + field_create_field($config); + break; + case 'instance': + field_create_instance($config); + break; + } +} + +/** + * Implements hook_config_import_change(). + */ +// @fieldcmi +function field_config_import_change($name, $new_config, $old_config) { + + list($module, $type) = explode('.', $name); + if ($module != 'field') { + return; + } + + $config = $new_config->get('data'); + + switch ($type) { + case 'field': + field_update_field($config); + break; + case 'instance': + field_update_instance($config); + break; + } +} + +/** + * Implements hook_config_import_delete(). + */ +// @fieldcmi +function field_config_import_delete($name, $new_config, $old_config) { + + list($module, $type) = explode('.', $name); + if ($module != 'field') { + return; + } + + switch ($type) { + case 'field': + $config = $old_config->get('data'); + field_delete_field($config['field_name']); + break; + case 'instance': + $config = $old_config->get('data'); + // In case the field has been deleted through this import hook, + // the instance will be deleted by that already. + if (!empty($config)) { + field_delete_instance($config); + } + break; + } +} + /** * Implements hook_system_info_alter(). * @@ -334,7 +408,9 @@ function field_cron() { * required if there are any active fields of that type. */ function field_system_info_alter(&$info, $file, $type) { - if ($type == 'module' && module_hook($file->name, 'field_info')) { + // During a Drupal 7 to Drupal 8 upgrade it is only safe to call + // field_read_fields() once the field_update_8001 has finished. + if ($type == 'module' && module_hook($file->name, 'field_info') && drupal_get_installed_schema_version('field') >= 8001) { $fields = field_read_fields(array('module' => $file->name), array('include_deleted' => TRUE)); if ($fields) { $info['required'] = TRUE; @@ -499,23 +575,47 @@ function field_modules_disabled($modules) { } /** - * Refreshes the 'active' and 'storage_active' columns for fields. + * Refreshes the 'active' and 'storage_active' values for fields. */ function field_sync_field_status() { - // Refresh the 'active' and 'storage_active' columns according to the current + + $deleted_fields = state()->get('field.field.deleted') ?: array(); + $fields = field_read_fields(array(), array('include_deleted' => 1 ,'include_inactive' => 1)); + // Refresh the 'active' and 'storage_active' values according to the current // set of enabled modules. $modules = module_list(); foreach ($modules as $module_name) { - field_associate_fields($module_name); + $fields = field_associate_fields($module_name, $fields); + } + + foreach ($fields as $id => $field) { + if (!in_array($field['module'], $modules)) { + $fields[$id]['active'] = 0; + } + if (!in_array($field['storage_module'], $modules)) { + $fields[$id]['storage_active'] = 0; + } + } + + foreach ($fields as $id => $field) { + if (!$field['deleted']) { + // We can not use field_update_field because the prior_field does not + // check whether a field is really there or not. + // @fieldcmi + $e_field = entity_load('fieldentity', $field['field_name']); + $e_field->data = $field; + $e_field->save(); + // config('field.field.' . $field['field_name'])->setData($field)->save(); + } + else { + $deleted_fields[$field['id']] = $field; + } } - db_update('field_config') - ->fields(array('active' => 0)) - ->condition('module', $modules, 'NOT IN') - ->execute(); - db_update('field_config') - ->fields(array('storage_active' => 0)) - ->condition('storage_module', $modules, 'NOT IN') - ->execute(); + + // Save the deleted fields. + state()->set('field.field.deleted', $deleted_fields); + + field_cache_clear(); } /** @@ -523,24 +623,37 @@ function field_sync_field_status() { * * @param $module * The name of the module to update on. + * @param $fields + * A collection of fields. */ -function field_associate_fields($module) { +function field_associate_fields($module, $fields) { + // Associate field types. $field_types = (array) module_invoke($module, 'field_info'); + if ($field_types) { - db_update('field_config') - ->fields(array('module' => $module, 'active' => 1)) - ->condition('type', array_keys($field_types)) - ->execute(); + $field_types = array_keys($field_types); + foreach ($fields as $id => $field) { + if (in_array($field['type'], $field_types)) { + $fields[$id]['module'] = $module; + $fields[$id]['active'] = TRUE; + } + } } + // Associate storage backends. $storage_types = (array) module_invoke($module, 'field_storage_info'); if ($storage_types) { - db_update('field_config') - ->fields(array('storage_module' => $module, 'storage_active' => 1)) - ->condition('storage_type', array_keys($storage_types)) - ->execute(); + $storage_types = array_keys($storage_types); + foreach ($fields as $id => $field) { + if (in_array($field['storage_type'], $storage_types)) { + $fields[$id]['storage_module'] = $module; + $fields[$id]['storage_active'] = TRUE; + } + } } + + return $fields; } /** @@ -700,12 +813,33 @@ function _field_sort_items_value_helper($a, $b) { * If no $settings are passed, the current settings are returned. */ function field_bundle_settings($entity_type, $bundle, $settings = NULL) { + $identifier = $entity_type . '.' . $bundle; + if (isset($settings)) { - variable_set('field_bundle_settings_' . $entity_type . '__' . $bundle, $settings); + // @fieldcmi + $e_bundle = entity_load('bundle', $identifier); + if ($e_bundle) { + $e_bundle->data = $settings; + } + else { + $e_bundle = entity_create('bundle', array( + 'id' => $identifier, + 'label' => $identifier, + 'data' => $settings, + )); + } + $e_bundle->save(); + // config('field.settings.' . $identifier)->setData($settings)->save(); field_info_cache_clear(); } else { - $settings = variable_get('field_bundle_settings_' . $entity_type . '__' . $bundle, array()); + $e_bundle = entity_load('bundle', $identifier); + if ($e_bundle) { + $settings = $e_bundle->data; + } + else { + $settings = array(); + } $settings += array( 'view_modes' => array(), 'extra_fields' => array(), @@ -829,6 +963,21 @@ function field_cache_clear() { } /** + * Generate an id for fields or instances. + * + * When a field is a deleted, the tables are renamed to {field_data_field_id} + * and {field_revision_field_id}. To make sure alternative uuid implementations + * don't generate longer uuid's and using str_replace() to replace dashes + * to underscores might end up in table names longer than 64 characters, we + * hash the uuid and then take the first 6 characters so we end up with a short + * unique id. + */ +function field_generate_id() { + $uuid = new Uuid(); + return substr(hash('sha256', $uuid->generate()), 0, 6); +} + +/** * Filters an HTML string to prevent cross-site-scripting (XSS) vulnerabilities. * * Like filter_xss_admin(), but with a shorter list of allowed tags. diff --git a/core/modules/field/lib/Drupal/field/FieldInfo.php b/core/modules/field/lib/Drupal/field/FieldInfo.php index b1692f7..ea470df 100644 --- a/core/modules/field/lib/Drupal/field/FieldInfo.php +++ b/core/modules/field/lib/Drupal/field/FieldInfo.php @@ -142,17 +142,10 @@ public function getFieldMap() { $map = array(); - $query = db_select('field_config_instance', 'fci'); - $query->join('field_config', 'fc', 'fc.id = fci.field_id'); - $query->fields('fc', array('type')); - $query->fields('fci', array('field_name', 'entity_type', 'bundle')) - ->condition('fc.active', 1) - ->condition('fc.storage_active', 1) - ->condition('fc.deleted', 0) - ->condition('fci.deleted', 0); - foreach ($query->execute() as $row) { - $map[$row->field_name]['bundles'][$row->entity_type][] = $row->bundle; - $map[$row->field_name]['type'] = $row->type; + $instances = field_read_instances(); + foreach ($instances as $key => $instance) { + $map[$instance['field_name']]['bundles'][$instance['entity_type']][] = $instance['bundle']; + $map[$instance['field_name']]['type'] = $instance['type']; } // Save in "static" and persistent caches. @@ -274,7 +267,7 @@ public function getField($field_name) { // Do not check the (large) persistent cache, but read the definition. // Cache miss: read from definition. - if ($field = field_read_field(array('field_name' => $field_name))) { + if ($field = field_read_field($field_name)) { $field = $this->prepareField($field); // Save in the "static" cache. @@ -379,7 +372,7 @@ public function getBundleInstances($entity_type, $bundle) { // Collect the fields in the bundle. $params = array('entity_type' => $entity_type, 'bundle' => $bundle); - $fields = field_read_fields($params); + $fields = field_read_fields(); // This iterates on non-deleted instances, so deleted fields are kept out of // the persistent caches. @@ -410,6 +403,7 @@ public function getBundleInstances($entity_type, $bundle) { // The persistent cache additionally contains the definitions of the fields // involved in the bundle. + uasort($instances, array($this, '_orderInstances')); $cache = array( 'instances' => $instances, 'fields' => array() @@ -568,4 +562,11 @@ public function prepareExtraFields($extra_fields, $entity_type, $bundle) { return $result; } + + /** + * Helper function to sort the instances on the widget weight. + */ + function _orderInstances($a, $b) { + return ($a['widget']['weight'] < $b['widget']['weight']) ? -1 : 1; + } } diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Bundle.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Bundle.php new file mode 100644 index 0000000..dedf431 --- /dev/null +++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Bundle.php @@ -0,0 +1,60 @@ +entity_type, $field['field_name'], $bundle); - field_delete_instance($instance); + field_delete_instance($instance->getArray()); // The instance still exists, deleted. $instances = field_read_instances(array('field_id' => $field['id'], 'deleted' => 1), array('include_deleted' => 1, 'include_inactive' => 1)); @@ -217,7 +219,7 @@ function testPurgeInstance() { // Delete the instance. $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); - field_delete_instance($instance); + field_delete_instance($instance->getArray()); // No field hooks were called. $mem = field_test_memorize(); @@ -281,7 +283,7 @@ function testPurgeField() { // Delete the first instance. $bundle = reset($this->bundles); $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); - field_delete_instance($instance); + field_delete_instance($instance->getArray()); // Assert that hook_field_delete() was not called yet. $mem = field_test_memorize(); @@ -312,7 +314,7 @@ function testPurgeField() { // Delete the second instance. $bundle = next($this->bundles); $instance = field_info_instance($this->entity_type, $field['field_name'], $bundle); - field_delete_instance($instance); + field_delete_instance($instance->getArray()); // Assert that hook_field_delete() was not called yet. $mem = field_test_memorize(); diff --git a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php index 4e7557e..a6a91a1 100644 --- a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php @@ -45,10 +45,11 @@ function testCreateField() { $mem = field_test_memorize(); $this->assertIdentical($mem['field_test_field_create_field'][0][0], $field_definition, 'hook_field_create_field() called with correct arguments.'); - // Read the raw record from the {field_config_instance} table. - $result = db_query('SELECT * FROM {field_config} WHERE field_name = :field_name', array(':field_name' => $field_definition['field_name'])); - $record = $result->fetchAssoc(); - $record['data'] = unserialize($record['data']); + // Read the configuration. + // @fieldcmi + $e_record = entity_load('fieldentity', $field_definition['field_name']); + $record = $e_record->data; + //$record = config('field.field.' . $field_definition['field_name'])->get(); // Ensure that basic properties are preserved. $this->assertEqual($record['field_name'], $field_definition['field_name'], 'The field name is properly saved.'); @@ -59,7 +60,7 @@ function testCreateField() { // Ensure that default settings are present. $field_type = field_info_field_types($field_definition['type']); - $this->assertIdentical($record['data']['settings'], $field_type['settings'], 'Default field settings have been written.'); + $this->assertEqual($record['settings'], $field_type['settings'], 'Default field settings have been written.'); // Ensure that default storage was set. $this->assertEqual($record['storage_type'], variable_get('field_storage_default'), 'The field type is properly saved.'); @@ -157,11 +158,12 @@ function testCreateField() { function testCreateFieldFail() { $field_name = 'duplicate'; $field_definition = array('field_name' => $field_name, 'type' => 'test_field', 'storage' => array('type' => 'field_test_storage_failure')); - $query = db_select('field_config')->condition('field_name', $field_name)->countQuery(); + //$field = config('field.field.' . $field_name)->get(); + // @fieldcmi + $field = entity_load('fieldentity', $field_name); - // The field does not appear in field_config. - $count = $query->execute()->fetchField(); - $this->assertEqual($count, 0, 'A field_config row for the field does not exist.'); + // The field does not exist. + $this->assertFalse($field, 'The field does not exist.'); // Try to create the field. try { @@ -172,9 +174,10 @@ function testCreateFieldFail() { $this->assertTrue(TRUE, 'Field creation (correctly) fails.'); } - // The field does not appear in field_config. - $count = $query->execute()->fetchField(); - $this->assertEqual($count, 0, 'A field_config row for the field does not exist.'); + // The field does not exist. + // $field = config('field.field.' . $field_name)->get(); + $field = entity_load('fieldentity', $field_name); + $this->assertFalse($field, 'The field does not exist.'); } /** @@ -268,8 +271,8 @@ function testDeleteField() { // Make sure that the field is marked as deleted when it is specifically // loaded. - $field = field_read_field($this->field['field_name'], array('include_deleted' => TRUE)); - $this->assertTrue(!empty($field['deleted']), 'A deleted field is marked for deletion.'); + $deleted_fields = state()->get('field.field.deleted'); + $this->assertTrue(isset($deleted_fields[$field['id']]), 'A deleted field is marked for deletion.'); // Make sure that this field's instance is marked as deleted when it is // specifically loaded. @@ -470,6 +473,6 @@ function _testActiveHelper($field_definition, $modules) { // Check that the field is active again after all modules have been // enabled. $field = field_read_field($field_name); - $this->assertTrue($field_definition <= $field, 'The field was was marked active.'); + $this->assertTrue($field_definition <= $field, 'The field was marked active.'); } } diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportChangeTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportChangeTest.php new file mode 100644 index 0000000..d94c14e --- /dev/null +++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportChangeTest.php @@ -0,0 +1,69 @@ + 'Field CMI change tests', + 'description' => 'Update field and instances during CMI change hook invocation.', + 'group' => 'Field API', + ); + } + + function setUp() { + parent::setUp(); + + $this->instance_manifest = 'manifest.field.instance'; + $this->instance_name = 'field.instance.node.test_import.field_test_import'; + $this->drupalCreateContentType(array('type' => 'test_import', 'name' => 'Test import')); + + $admin_user = $this->drupalCreateUser(array('access administration pages', 'access content overview', 'administer nodes', 'bypass node access')); + $this->drupalLogin($admin_user); + } + + /** + * Test importing changes. + */ + function testImportChange() { + + // Assert default test import. + $this->drupalGet('node/add/test_import'); + $this->assertRaw('Test import field'); + + // Change label. + $active = $this->container->get('config.storage'); + $staging = $this->container->get('config.storage.staging'); + $manifest = $active->read($this->instance_manifest); + $instance = $active->read($this->instance_name); + $instance['data']['label'] = 'Test update import field'; + $staging->write($this->instance_name, $instance); + $staging->write($this->instance_manifest, $manifest); + + // Import. + config_import(); + + // Assert updated label. + $this->drupalGet('node/add/test_import'); + $this->assertText('Test update import field'); + } +} diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php new file mode 100644 index 0000000..82b390e --- /dev/null +++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportCreateTest.php @@ -0,0 +1,87 @@ + 'Field CMI create tests', + 'description' => 'Create field and instances during CMI create hook invocation.', + 'group' => 'Field API', + ); + } + + function setUp() { + parent::setUp(); + + $this->field_test_import_staging = 'field.field.field_test_import_staging'; + $this->instance_test_import_staging = 'field.instance.node.test_import.field_test_import_staging'; + $this->field_manifest = 'manifest.field.field'; + $this->instance_manifest = 'manifest.field.instance'; + $this->drupalCreateContentType(array('type' => 'test_import', 'name' => 'Test import')); + + $admin_user = $this->drupalCreateUser(array('access administration pages', 'access content overview', 'administer nodes', 'bypass node access')); + $this->drupalLogin($admin_user); + } + + /** + * Test importing new fields. + */ + function testImportCreate() { + + // Assert default test import. + $this->drupalGet('node/add/test_import'); + $this->assertNoText('Test import field'); + + // Enable field_test_config module and assert the test import + // field and instance is available on the Test content type. + // This tests creating fields and instances that are provided + // by a module. + module_enable(array('field_test_config')); + $this->drupalGet('node/add/test_import'); + $this->assertText('Test import field'); + $module_path = drupal_get_path('module', 'field_test_config'); + + // Copy another field and instance to the staging directory + // on the Test content type and run config_import() to test + // importing from the staging directory. + $active = $this->container->get('config.storage'); + $staging = $this->container->get('config.storage.staging'); + $field_manifest = $active->read($this->field_manifest); + $instance_manifest = $active->read($this->instance_manifest); + + // Copy the files. + $copied = file_unmanaged_copy($module_path .'/staging/' . $this->field_test_import_staging . '.yml', 'public://config_staging/' . $this->field_test_import_staging . '.yml'); + $this->assertTrue($copied); + $copied = file_unmanaged_copy($module_path .'/staging/' . $this->instance_test_import_staging . '.yml', 'public://config_staging/' . $this->instance_test_import_staging . '.yml'); + $this->assertTrue($copied); + + // Add to manifest. + $field_manifest['field_test_import_staging'] = array('name' => $this->field_test_import_staging); + $instance_manifest['node.test_import.field_test_import_staging'] = array('name' => $this->instance_test_import_staging); + + // Write to manifest and new config. + $staging->write($this->field_manifest, $field_manifest); + $staging->write($this->instance_manifest, $instance_manifest); + + // Import. + config_import(); + + // Assert the staging field is there. + $this->drupalGet('node/add/test_import'); + $this->assertText('Import from staging'); + } +} diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php new file mode 100644 index 0000000..96daa94 --- /dev/null +++ b/core/modules/field/lib/Drupal/field/Tests/FieldImportDeleteTest.php @@ -0,0 +1,100 @@ + 'Field CMI delete tests', + 'description' => 'Delete field and instances during CMI delete hook invocation.', + 'group' => 'Field API', + ); + } + + function setUp() { + parent::setUp(); + + $this->body_field_name = 'field.field.body'; + $this->test_import_field_name = 'field.field.test_import'; + $this->body_instance_name = 'field.instance.node.test_import.body'; + $this->test_import_instance_name = 'field.instance.node.test_import.field_test_import'; + $this->field_manifest = 'manifest.field.field'; + $this->instance_manifest = 'manifest.field.instance'; + $this->drupalCreateContentType(array('type' => 'test_import', 'name' => 'Test import')); + + $admin_user = $this->drupalCreateUser(array('access administration pages', 'access content overview', 'administer nodes', 'bypass node access')); + $this->drupalLogin($admin_user); + } + + /** + * Test importing deletions. + */ + function testImportDelete() { + + $body_field = field_info_field('body'); + $field_test_import = field_info_field('field_test_import'); + + // Assert default test import. + $this->drupalGet('node/add/test_import'); + $this->assertRaw('Test import field'); + $this->assertRaw('Body'); + + // Delete body field and instance, the test import instance + // from the manifest. + $active = $this->container->get('config.storage'); + $staging = $this->container->get('config.storage.staging'); + $field_manifest = $active->read($this->field_manifest); + $instance_manifest = $active->read($this->instance_manifest); + unset($field_manifest['body']); + unset($instance_manifest['node.test_import.body']); + unset($instance_manifest['node.test_import.field_test_import']); + $staging->write($this->field_manifest, $field_manifest); + $staging->write($this->instance_manifest, $instance_manifest); + + // Import. + config_import(); + + // Assert the field and instance are gone from the form. + $this->drupalGet('node/add/test_import'); + $this->assertNoText('Test import field'); + $this->assertNoText('Body'); + + // Check the body and import field are in state of deleted fields. + $deleted_fields = state()->get('field.field.deleted') ?: array(); + $this->assertTrue(isset($deleted_fields[$body_field['id']])); + $this->assertTrue(isset($deleted_fields[$field_test_import['id']])); + + // Run purge_batch(). + field_purge_batch(10); + + // Check the deleted fields are removed from state. + $deleted_fields = state()->get('field.field.deleted') ?: array(); + $this->assertTrue(empty($deleted_fields), 'Fields are deleted'); + + // Check all config files are gone. + $active = $this->container->get('config.storage'); + $this->assertIdentical($active->listAll($this->body_field_name), array()); + $this->assertIdentical($active->listAll($this->test_import_field_name), array()); + $this->assertIdentical($active->listAll($this->body_instance_name), array()); + $this->assertIdentical($active->listAll($this->test_import_instance_name), array()); + } +} diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php index cfc1718..bef8bda 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldInfoTest.php @@ -133,13 +133,12 @@ function testFieldPrepare() { // 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(); + // @fieldcmi + //$field = config('field.field.' . $field_definition['field_name'])->get(); + $e_field = entity_load('fieldentity', $field_definition['field_name']); + $field = $e_field->data; + $field['data']['settings'] = array(); + field_update_field($field); field_cache_clear(); @@ -148,7 +147,7 @@ function testFieldPrepare() { // Check that all expected settings are in place. $field_type = field_info_field_types($field_definition['type']); - $this->assertIdentical($field['settings'], $field_type['settings'], 'All expected default field settings are present.'); + $this->assertEqual($field['settings'], $field_type['settings'], 'All expected default field settings are present.'); } /** @@ -170,18 +169,16 @@ function testInstancePrepare() { // 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' => $instance_definition['field_name'], ':bundle' => $instance_definition['bundle']))->fetchField(); - $data = unserialize($data); - $data['settings'] = array(); - $data['widget']['settings'] = 'unavailable_widget'; - $data['widget']['settings'] = array(); - $data['display']['default']['type'] = 'unavailable_formatter'; - $data['display']['default']['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(); + //$instance = config('field.instance.' . $instance_definition['entity_type'] . '.' . $instance_definition['bundle'] . '.' . $instance_definition['field_name'])->get(); + $e_instance = entity_load('instance', $instance_definition['entity_type'] . '.' . $instance_definition['bundle'] . '.' . $instance_definition['field_name']); + $instance = $e_instance->data; + $instance['settings'] = array(); + $instance['widget']['settings'] = 'unavailable_widget'; + $instance['widget']['settings'] = array(); + $instance['display']['default']['type'] = 'unavailable_formatter'; + $instance['display']['default']['settings'] = array(); + + field_update_instance($instance); field_cache_clear(); @@ -190,7 +187,7 @@ function testInstancePrepare() { // 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'] , 'All expected instance settings are present.'); + $this->assertEqual($instance['settings'], $field_type['instance_settings'] , 'All expected instance settings are present.'); // Check that the default widget is used and expected settings are in place. $widget = $instance->getWidget(); diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php index 84cee72..50311d7 100644 --- a/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php +++ b/core/modules/field/lib/Drupal/field/Tests/FieldInstanceCrudTest.php @@ -8,6 +8,7 @@ namespace Drupal\field\Tests; use Drupal\field\FieldException; +use Drupal\field\FieldInstance; class FieldInstanceCrudTest extends FieldTestBase { @@ -55,11 +56,10 @@ function setUp() { function testCreateFieldInstance() { field_create_instance($this->instance_definition); - // Read the raw record from the {field_config_instance} table. - $result = db_query('SELECT * 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'])); - $record = $result->fetchAssoc(); - $record['data'] = unserialize($record['data']); - + // Read the configuration. + //$record = config('field.instance.' . $this->instance_definition['entity_type'] . '.' . $this->instance_definition['bundle'] . '.' . $this->instance_definition['field_name'])->get(); + $e_record = entity_load('instance', $this->instance_definition['entity_type'] . '.' . $this->instance_definition['bundle'] . '.' . $this->instance_definition['field_name']); + $record = $e_record->data; $field_type = field_info_field_types($this->field['type']); $widget_type = field_info_widget_types($field_type['default_widget']); $formatter_type = field_info_formatter_types($field_type['default_formatter']); @@ -68,17 +68,17 @@ function testCreateFieldInstance() { $this->assertIdentical($record['id'], $this->instance_definition['id'], 'The instance id is filled in'); // Check that default values are set. - $this->assertIdentical($record['data']['required'], FALSE, 'Required defaults to false.'); - $this->assertIdentical($record['data']['label'], $this->instance_definition['field_name'], 'Label defaults to field name.'); - $this->assertIdentical($record['data']['description'], '', 'Description defaults to empty string.'); - $this->assertIdentical($record['data']['widget']['type'], $field_type['default_widget'], 'Default widget has been written.'); - $this->assertTrue(isset($record['data']['display']['default']), 'Display for "full" view_mode has been written.'); - $this->assertIdentical($record['data']['display']['default']['type'], $field_type['default_formatter'], 'Default formatter for "full" view_mode has been written.'); + $this->assertEqual($record['required'], FALSE, 'Required defaults to false.'); + $this->assertIdentical($record['label'], $this->instance_definition['field_name'], 'Label defaults to field name.'); + $this->assertIdentical($record['description'], '', 'Description defaults to empty string.'); + $this->assertIdentical($record['widget']['type'], $field_type['default_widget'], 'Default widget has been written.'); + $this->assertTrue(isset($record['display']['default']), 'Display for "full" view_mode has been written.'); + $this->assertIdentical($record['display']['default']['type'], $field_type['default_formatter'], 'Default formatter for "full" view_mode has been written.'); // Check that default settings are set. - $this->assertIdentical($record['data']['settings'], $field_type['instance_settings'] , 'Default instance settings have been written.'); - $this->assertIdentical($record['data']['widget']['settings'], $widget_type['settings'] , 'Default widget settings have been written.'); - $this->assertIdentical($record['data']['display']['default']['settings'], $formatter_type['settings'], 'Default formatter settings for "full" view_mode have been written.'); + $this->assertEqual($record['settings'], $field_type['instance_settings'] , 'Default instance settings have been written.'); + $this->assertIdentical($record['widget']['settings'], $widget_type['settings'] , 'Default widget settings have been written.'); + $this->assertIdentical($record['display']['default']['settings'], $formatter_type['settings'], 'Default formatter settings for "full" view_mode have been written.'); // Guarantee that the field/bundle combination is unique. try { @@ -143,7 +143,14 @@ function testReadFieldInstance() { // Read the instance back. $instance = field_read_instance('test_entity', $this->instance_definition['field_name'], $this->instance_definition['bundle']); - $this->assertTrue($this->instance_definition == $instance, 'The field was properly read.'); + $this->assertTrue($this->instance_definition['id'] == $instance['id'], 'The field was properly read.'); + $this->assertTrue($this->instance_definition['label'] == $instance['label'], 'The field was properly read.'); + $this->assertTrue($this->instance_definition['field_id'] == $instance['field_id'], 'The field was properly read.'); + $this->assertTrue($this->instance_definition['entity_type'] == $instance['entity_type'], 'The field was properly read.'); + $this->assertTrue($this->instance_definition['bundle'] == $instance['bundle'], 'The field was properly read.'); + $this->assertTrue($this->instance_definition['settings'] == $instance['settings'], 'The field was properly read.'); + $this->assertTrue($this->instance_definition['display'] == $instance['display'], 'The field was properly read.'); + $this->assertTrue($this->instance_definition['widget'] == $instance['widget'], 'The field was properly read.'); } /** @@ -239,7 +246,9 @@ function testDeleteFieldInstance() { // Make sure the field is deleted when its last instance is deleted. field_delete_instance($another_instance); - $field = field_read_field($another_instance['field_name'], array('include_deleted' => TRUE)); - $this->assertTrue(!empty($field['deleted']), 'A deleted field is marked for deletion after all its instances have been marked for deletion.'); + $deleted_fields = state()->get('field.field.deleted'); + $this->assertTrue(isset($deleted_fields[$another_instance['field_id']]), 'A deleted field is marked for deletion.'); + $field = field_read_field(array('field_name' => $another_instance['field_name'])); + $this->assertFalse($field, 'The field marked to be deleted is not found anymore in the configuration.'); } } diff --git a/core/modules/field/modules/field_sql_storage/field_sql_storage.install b/core/modules/field/modules/field_sql_storage/field_sql_storage.install index 4d4fe57..d17fc42 100644 --- a/core/modules/field/modules/field_sql_storage/field_sql_storage.install +++ b/core/modules/field/modules/field_sql_storage/field_sql_storage.install @@ -11,8 +11,12 @@ function field_sql_storage_schema() { $schema = array(); - // Dynamic (data) tables. - if (db_table_exists('field_config')) { + // During a Drupal 7 to Drupal 8 upgrade it is only safe to call + // field_read_fields() once the field_update_8001 has finished. + if (drupal_get_installed_schema_version('field') >= 8001) { + // Dynamic (data) tables. + //module_load_include('module', 'field'); + //drupal_classloader_register('field', dirname(drupal_get_filename('module', 'field'))); $fields = field_read_fields(array(), array('include_deleted' => TRUE, 'include_inactive' => TRUE)); drupal_load('module', 'field_sql_storage'); foreach ($fields as $field) { @@ -21,6 +25,7 @@ function field_sql_storage_schema() { } } } + return $schema; } @@ -78,51 +83,3 @@ function _update_8000_field_sql_storage_write($entity_type, $bundle, $entity_id, } } -/** - * Changes field language into langcode. - */ -function field_sql_storage_update_8000(&$sandbox) { - // Prepare updated schema data structures. - $primary_key_data = array ( - 'entity_type', - 'entity_id', - 'deleted', - 'delta', - 'langcode', - ); - $primary_key_revision = array ( - 'entity_type', - 'entity_id', - 'revision_id', - 'deleted', - 'delta', - 'langcode', - ); - $langcode_index = array( - 'langcode', - ); - $field_langcode = array( - 'type' => 'varchar', - 'length' => 32, - 'not null' => true, - 'default' => '', - ); - - // Retrieve field data. - $fields = _update_7000_field_read_fields(array('storage_type' => 'field_sql_storage')); - - // Update schema. - foreach ($fields as $field) { - $data_table = _field_sql_storage_tablename($field); - $revision_table = _field_sql_storage_revision_tablename($field); - $table_info = array($data_table => $primary_key_data, $revision_table => $primary_key_revision); - - foreach ($table_info as $table => $primary_key) { - db_drop_primary_key($table); - db_drop_index($table, 'language'); - db_change_field($table, 'language', 'langcode', $field_langcode); - db_add_primary_key($table, $primary_key); - db_add_index($table, 'langcode', $langcode_index); - } - } -} diff --git a/core/modules/field/modules/link/link.info b/core/modules/field/modules/link/link.info index a43c222..0bee0d1 100644 --- a/core/modules/field/modules/link/link.info +++ b/core/modules/field/modules/link/link.info @@ -4,3 +4,4 @@ core = 8.x package = Core version = VERSION dependencies[] = field +dependencies[] = field_sql_storage diff --git a/core/modules/field/modules/number/number.info b/core/modules/field/modules/number/number.info index aabe3d7..5785127 100644 --- a/core/modules/field/modules/number/number.info +++ b/core/modules/field/modules/number/number.info @@ -4,3 +4,4 @@ package = Core version = VERSION core = 8.x dependencies[] = field +dependencies[] = field_sql_storage diff --git a/core/modules/field/modules/options/options.info b/core/modules/field/modules/options/options.info index f19e952..eebb593 100644 --- a/core/modules/field/modules/options/options.info +++ b/core/modules/field/modules/options/options.info @@ -4,3 +4,4 @@ package = Core version = VERSION core = 8.x dependencies[] = field +dependencies[] = field_sql_storage diff --git a/core/modules/field/modules/text/text.info b/core/modules/field/modules/text/text.info index 3ba8357..5eab8ac 100644 --- a/core/modules/field/modules/text/text.info +++ b/core/modules/field/modules/text/text.info @@ -4,4 +4,5 @@ package = Core version = VERSION core = 8.x dependencies[] = field +dependencies[] = field_sql_storage required = TRUE diff --git a/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml b/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml new file mode 100644 index 0000000..b9d108d --- /dev/null +++ b/core/modules/field/tests/modules/field_test_config/config/field.field.field_test_import.yml @@ -0,0 +1,53 @@ +id: field_test_import +uuid: fb38277f-1fd4-49d5-8d09-9d7037fdcce9 +name: '' +label: field_test_import +data: + field_name: field_test_import + type: text + translatable: false + entity_types: { } + cardinality: '1' + locked: false + settings: + max_length: '255' + storage: + type: field_sql_storage + settings: { } + module: field_sql_storage + active: 1 + details: + sql: + FIELD_LOAD_CURRENT: + field_data_field_test_import: + value: field_test_import_value + format: field_test_import_format + FIELD_LOAD_REVISION: + field_revision_field_test_import: + value: field_test_import_value + format: field_test_import_format + deleted: 0 + module: text + active: 1 + columns: + value: + type: varchar + length: '255' + 'not null': false + format: + type: varchar + length: 255 + 'not null': false + 'foreign keys': + format: + table: filter_format + columns: + format: format + indexes: + format: + - format + storage_type: field_sql_storage + storage_module: field_sql_storage + storage_active: 1 + id: b5a22b +langcode: und diff --git a/core/modules/field/tests/modules/field_test_config/config/field.instance.node.test_import.field_test_import.yml b/core/modules/field/tests/modules/field_test_config/config/field.instance.node.test_import.field_test_import.yml new file mode 100644 index 0000000..b1218a1 --- /dev/null +++ b/core/modules/field/tests/modules/field_test_config/config/field.instance.node.test_import.field_test_import.yml @@ -0,0 +1,40 @@ +id: node.test_import.field_test_import +uuid: 392b4e9d-6157-412e-9603-3d622512f498 +name: '' +label: node.test_import.field_test_import +data: + field_name: field_test_import + entity_type: node + bundle: test_import + label: 'Test import field' + widget: + weight: '-2' + type: text_textfield + module: text + active: 0 + settings: + size: '60' + field_id: b5a22b + id: 948dff + settings: + text_processing: '0' + user_register_form: false + display: + default: + label: above + type: text_default + settings: { } + module: text + weight: 2 + required: 0 + description: '' + deleted: 0 + active: 1 + locked: false + type: text + module: text + storage_type: field_sql_storage + storage_active: 1 + storage_module: field_sql_storage + default_value: null +langcode: und diff --git a/core/modules/field/tests/modules/field_test_config/field_test_config.info b/core/modules/field/tests/modules/field_test_config/field_test_config.info new file mode 100644 index 0000000..49b0509 --- /dev/null +++ b/core/modules/field/tests/modules/field_test_config/field_test_config.info @@ -0,0 +1,6 @@ +name = "Field API configuration tests" +description = "Support module for the Field API configuration tests." +core = 8.x +package = Testing +version = VERSION +hidden = TRUE diff --git a/core/modules/field/tests/modules/field_test_config/field_test_config.module b/core/modules/field/tests/modules/field_test_config/field_test_config.module new file mode 100644 index 0000000..e208464 --- /dev/null +++ b/core/modules/field/tests/modules/field_test_config/field_test_config.module @@ -0,0 +1,6 @@ +getArray()); drupal_set_message(t('The field %field has been deleted from the %type content type.', array('%field' => $instance['label'], '%type' => $bundle_label))); } else { diff --git a/core/modules/file/file.install b/core/modules/file/file.install index ca6e1fd..a8c7b0c 100644 --- a/core/modules/file/file.install +++ b/core/modules/file/file.install @@ -125,8 +125,8 @@ function file_schema() { ), 'id' => array( 'description' => 'The primary key of the object using the file.', - 'type' => 'int', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 64, 'not null' => TRUE, 'default' => 0, ), @@ -232,3 +232,29 @@ function file_requirements($phase) { return $requirements; } + +/** + * @defgroup updates-7.x-to-8.x Updates from 7.x to 8.x + * @{ + * Update functions from 7.x to 8.x. + */ + +/** + * Convert the id column in file_usage table for Field API. + */ +function file_update_8001() { + $spec = array( + 'description' => 'The primary key of the object using the file.', + 'type' => 'varchar', + 'length' => 64, + 'not null' => TRUE, + 'default' => '', + ); + db_change_field('file_usage', 'id', 'id', $spec); +} + +/** + * @} End of "defgroup updates-7.x-to-8.x". + * The next series of updates should start at 9000. + */ + diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 56f8402..9e56db6 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -22,10 +22,6 @@ function forum_install() { * Implements hook_enable(). */ function forum_enable() { - // If we enable forum at the same time as taxonomy we need to call - // field_associate_fields() as otherwise the field won't be enabled until - // hook modules_enabled is called which takes place after hook_enable events. - field_associate_fields('taxonomy'); // Create the forum vocabulary if it does not exist. // @todo Change Forum module so forum.settings can contain the vocabulary's @@ -55,7 +51,7 @@ function forum_enable() { } // Create the 'taxonomy_forums' field if it doesn't already exist. - if (!field_info_field('taxonomy_forums')) { + if (!field_read_field('taxonomy_forums', array('include_inactive' => TRUE))) { $field = array( 'field_name' => 'taxonomy_forums', 'type' => 'taxonomy_term_reference', diff --git a/core/modules/node/node.module b/core/modules/node/node.module index fc53461..7e695f5 100644 --- a/core/modules/node/node.module +++ b/core/modules/node/node.module @@ -194,6 +194,11 @@ function node_cron() { * Implements hook_entity_info_alter(). */ function node_entity_info_alter(&$info) { + if (!db_table_exists('node_type')) { + // entity_get_info() is called in drupal_get_schema now this means this can + // be called before the schema is installed. + return; + } // Add a translation handler for fields if the language module is enabled. if (module_exists('language')) { $info['node']['translation']['node'] = TRUE; diff --git a/core/modules/rdf/rdf.module b/core/modules/rdf/rdf.module index 782c06f..ffd8425 100644 --- a/core/modules/rdf/rdf.module +++ b/core/modules/rdf/rdf.module @@ -390,6 +390,11 @@ function rdf_modules_uninstalled($modules) { * */ function rdf_entity_info_alter(&$entity_info) { + if (!db_table_exists('rdf_mapping')) { + // entity_get_info() is called in drupal_get_schema now this means this can + // be called before the schema is installed. + return; + } // Loop through each entity type and its bundles. foreach ($entity_info as $entity_type => $entity_type_info) { if (!empty($entity_type_info['bundles'])) { diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php new file mode 100644 index 0000000..9df2b03 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php @@ -0,0 +1,46 @@ + 'Field API upgrade test', + 'description' => 'Upgrade tests for Field API.', + 'group' => 'Upgrade path', + ); + } + + public function setUp() { + $this->databaseDumpFiles = array( + drupal_get_path('module', 'system') . '/tests/upgrade/drupal-7.filled.standard_all.database.php.gz', + ); + parent::setUp(); + } + + /** + * Test the upgrade path for fields and instances to the new + * configuration system. + */ + function testFieldConfigUpgrade() { + $this->assertTrue($this->performUpgrade(), t('The upgrade was completed successfully.')); + + // Assert the field_config and field_config_instance tables are gone. + $this->assertTrue(!db_table_exists('field_config'), 'The field_config table has been removed.'); + $this->assertTrue(!db_table_exists('field_config_instance'), 'The field_config_instance table has been removed.'); + + // Assert the body field and instance on the article are converted to CMI. + $body_field = field_info_field('body'); + $this->assertNotNull($body_field, 'The body field has been found.'); + $body_field_instance = field_info_instance('node', 'body', 'article'); + $this->assertNotNull($body_field, 'The body field instance on the article content type has been found.'); + } +} diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module index 75573a9..da793d6 100644 --- a/core/modules/taxonomy/taxonomy.module +++ b/core/modules/taxonomy/taxonomy.module @@ -109,6 +109,11 @@ function taxonomy_permission() { * Implements hook_entity_info_alter(). */ function taxonomy_entity_info_alter(&$info) { + if (!db_table_exists('taxonomy_vocabulary')) { + // entity_get_info() is called in drupal_get_schema now this means this can + // be called both the schema is installed. + return; + } foreach (taxonomy_vocabulary_get_names() as $machine_name => $vocabulary) { $info['taxonomy_term']['bundles'][$machine_name] = array( 'label' => $vocabulary->name, diff --git a/core/modules/translation_entity/translation_entity.install b/core/modules/translation_entity/translation_entity.install index b571da3..71efcbe 100644 --- a/core/modules/translation_entity/translation_entity.install +++ b/core/modules/translation_entity/translation_entity.install @@ -20,9 +20,10 @@ function translation_entity_schema() { 'description' => 'The entity type this translation relates to', ), 'entity_id' => array( - 'type' => 'int', - 'unsigned' => TRUE, + 'type' => 'varchar', + 'length' => 64, 'not null' => TRUE, + 'default' => 0, 'description' => 'The entity id this translation relates to', ), 'langcode' => array(