diff --git a/core/core.services.yml b/core/core.services.yml
index 78398b8..95c56a8 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -86,6 +86,8 @@ services:
arguments: ['@database', config_snapshot]
config.storage.schema:
class: Drupal\Core\Config\Schema\SchemaStorage
+ config.storage.installer:
+ class: Drupal\Core\Config\InstallStorage
config.typed:
class: Drupal\Core\Config\TypedConfigManager
arguments: ['@config.storage', '@config.storage.schema']
diff --git a/core/includes/entity.api.php b/core/includes/entity.api.php
index a56c1ef..3f39e75 100644
--- a/core/includes/entity.api.php
+++ b/core/includes/entity.api.php
@@ -26,8 +26,8 @@
function hook_entity_info(&$entity_info) {
// Add a form controller for a custom node form without overriding the default
// node form. To override the default node form, use hook_entity_info_alter()
- // to alter $entity_info['node']['form_controller_class']['default'].
- $entity_info['node']['form_controller_class']['mymodule_foo'] = 'Drupal\mymodule\NodeFooFormController';
+ // to alter $entity_info['node']['controllers']['form']['default'].
+ $entity_info['node']['controllers']['form']['mymodule_foo'] = 'Drupal\mymodule\NodeFooFormController';
}
/**
@@ -216,7 +216,7 @@ function hook_entity_bundle_delete($entity_type, $bundle) {
function hook_entity_info_alter(&$entity_info) {
// Set the controller class for nodes to an alternate implementation of the
// Drupal\Core\Entity\EntityStorageControllerInterface interface.
- $entity_info['node']['controller_class'] = 'Drupal\mymodule\MyCustomNodeStorageController';
+ $entity_info['node']['controllers']['storage'] = 'Drupal\mymodule\MyCustomNodeStorageController';
}
/**
diff --git a/core/includes/entity.inc b/core/includes/entity.inc
index 77ec01a..c2c97c4 100644
--- a/core/includes/entity.inc
+++ b/core/includes/entity.inc
@@ -250,8 +250,8 @@ function entity_load_by_uuid($entity_type, $uuid, $reset = FALSE) {
* Drupal\Core\Entity\EntityStorageControllerInterface interface. By default,
* Drupal\Core\Entity\DatabaseStorageController is used. Entity types can
* specify that a different class should be used by setting the
- * 'controller_class' key in the entity plugin annotation. These classes can
- * either implement the Drupal\Core\Entity\EntityStorageControllerInterface
+ * "controllers['storage']" key in the entity plugin annotation. These classes
+ * can either implement the Drupal\Core\Entity\EntityStorageControllerInterface
* interface, or, most commonly, extend the
* Drupal\Core\Entity\DatabaseStorageController class.
* See Drupal\node\Plugin\Core\Entity\Node and Drupal\node\NodeStorageController
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 47982dd..6617366 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -760,6 +760,12 @@ function install_tasks($install_state) {
'type' => 'batch',
'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
),
+ 'install_update_configuration_translations' => array(
+ 'display_name' => st('Translate configuration'),
+ 'display' => $needs_translations,
+ 'type' => 'batch',
+ 'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
+ ),
'install_finished' => array(
'display_name' => st('Finished'),
),
@@ -1855,6 +1861,22 @@ function install_import_translations_remaining(&$install_state) {
}
/**
+ * Creates configuration translations.
+ *
+ * @param array $install_state
+ * An array of information about the current installation state.
+ *
+ * @return array
+ * The batch definition, if there are configuration objects to update.
+ *
+ * @see install_tasks()
+ */
+function install_update_configuration_translations(&$install_state) {
+ Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+ return locale_config_batch_update_components(array(), array($install_state['parameters']['langcode']));
+}
+
+/**
* Performs final installation steps and displays a 'finished' page.
*
* @param $install_state
diff --git a/core/includes/module.inc b/core/includes/module.inc
index 11f8244..85c0240 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -5,9 +5,11 @@
* API for loading and interacting with Drupal modules.
*/
-use Drupal\Component\Graph\Graph;
use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\Yaml\Parser;
/**
* Builds a list of bootstrap modules and enabled modules and themes.
@@ -566,7 +568,6 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE)
$module_list = array_keys($module_list);
}
- $storage = drupal_container()->get('config.storage');
$schema_store = Drupal::keyValue('system.schema');
$disabled_config = config('system.module.disabled');
foreach ($module_list as $module) {
@@ -578,6 +579,38 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE)
// Remove all configuration belonging to the module.
config_uninstall_default_config('module', $module);
+ // Remove any cache bins defined by the module.
+ $service_yaml_file = drupal_get_path('module', $module) . "/$module.services.yml";
+ if (file_exists($service_yaml_file)) {
+ $parser = new Parser;
+ $definitions = $parser->parse(file_get_contents($service_yaml_file));
+ if (isset($definitions['services'])) {
+ foreach ($definitions['services'] as $id => $definition) {
+ if (isset($definition['tags'])) {
+ foreach ($definition['tags'] as $tag) {
+ // This works for the default cache registration and even in some
+ // cases when a non-default "super" factory is used. That should
+ // be extremely rare.
+ if ($tag['name'] == 'cache.bin' && isset($definition['factory_service']) && isset($definition['factory_method']) && !empty($definition['arguments'])) {
+ try {
+ $factory = Drupal::service($definition['factory_service']);
+ if (method_exists($factory, $definition['factory_method'])) {
+ $backend = call_user_func_array(array($factory, $definition['factory_method']), $definition['arguments']);
+ if ($backend instanceof CacheBackendInterface) {
+ $backend->removeBin();
+ }
+ }
+ }
+ catch (Exception $e) {
+ watchdog_exception('system', $e, 'Failed to remove cache bin defined by the service %id.', array('%id' => $id));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO);
$schema_store->delete($module);
$disabled_config->clear($module);
diff --git a/core/lib/Drupal/Core/Cache/BackendChain.php b/core/lib/Drupal/Core/Cache/BackendChain.php
index d3fac6e..5df1a08 100644
--- a/core/lib/Drupal/Core/Cache/BackendChain.php
+++ b/core/lib/Drupal/Core/Cache/BackendChain.php
@@ -222,4 +222,14 @@ public function isEmpty() {
return TRUE;
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeBin() {
+ foreach ($this->backends as $backend) {
+ $this->removeBin();
+ }
+ }
+
}
diff --git a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
index d38a094..a054604 100644
--- a/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
+++ b/core/lib/Drupal/Core/Cache/CacheBackendInterface.php
@@ -287,6 +287,11 @@ public function invalidateAll();
public function garbageCollection();
/**
+ * Remove a cache bin.
+ */
+ public function removeBin();
+
+ /**
* Checks if a cache bin is empty.
*
* A cache bin is considered empty if it does not contain any valid data for
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 39359c3..913a43b 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -9,6 +9,7 @@
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Database;
+use Drupal\Core\Database\SchemaObjectExistsException;
/**
* Defines a default cache implementation.
@@ -69,7 +70,13 @@ public function getMultiple(&$cids, $allow_invalid = FALSE) {
// is used here only due to the performance overhead we would incur
// otherwise. When serving an uncached page, the overhead of using
// ::select() is a much smaller proportion of the request.
- $result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
+ $result = array();
+ try {
+ $result = $this->connection->query('SELECT cid, data, created, expire, serialized, tags, checksum_invalidations, checksum_deletions FROM {' . $this->connection->escapeTable($this->bin) . '} WHERE cid IN (:cids)', array(':cids' => $cids));
+ }
+ catch (\Exception $e) {
+ // Nothing to do.
+ }
$cache = array();
foreach ($result as $item) {
$item = $this->prepareItem($item, $allow_invalid);
@@ -134,6 +141,29 @@ protected function prepareItem($cache, $allow_invalid) {
* Implements Drupal\Core\Cache\CacheBackendInterface::set().
*/
public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANENT, array $tags = array()) {
+ $try_again = FALSE;
+ try {
+ // The bin might not yet exist.
+ $this->doSet($cid, $data, $expire, $tags);
+ }
+ catch (\Exception $e) {
+ // If there was an exception, try to create the bins.
+ if (!$try_again = $this->ensureBinExists()) {
+ // If the exception happened for other reason than the missing bin
+ // table, propagate the exception.
+ throw $e;
+ }
+ }
+ // Now that the bin has been created, try again if necessary.
+ if ($try_again) {
+ $this->doSet($cid, $data, $expire, $tags);
+ }
+ }
+
+ /**
+ * Actually set the cache.
+ */
+ protected function doSet($cid, $data, $expire, $tags) {
$flat_tags = $this->flattenTags($tags);
$checksum = $this->checksumTags($flat_tags);
$fields = array(
@@ -163,22 +193,25 @@ public function set($cid, $data, $expire = CacheBackendInterface::CACHE_PERMANEN
* Implements Drupal\Core\Cache\CacheBackendInterface::delete().
*/
public function delete($cid) {
- $this->connection->delete($this->bin)
- ->condition('cid', $cid)
- ->execute();
+ $this->deleteMultiple(array($cid));
}
/**
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteMultiple().
*/
public function deleteMultiple(array $cids) {
- // Delete in chunks when a large array is passed.
- do {
- $this->connection->delete($this->bin)
- ->condition('cid', array_splice($cids, 0, 1000), 'IN')
- ->execute();
+ try {
+ // Delete in chunks when a large array is passed.
+ do {
+ $this->connection->delete($this->bin)
+ ->condition('cid', array_splice($cids, 0, 1000), 'IN')
+ ->execute();
+ }
+ while (count($cids));
+ }
+ catch (\Exception $e) {
+ $this->catchException($e);
}
- while (count($cids));
}
/**
@@ -188,11 +221,16 @@ public function deleteTags(array $tags) {
$tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
foreach ($this->flattenTags($tags) as $tag) {
unset($tag_cache[$tag]);
- $this->connection->merge('cache_tags')
- ->insertFields(array('deletions' => 1))
- ->expression('deletions', 'deletions + 1')
- ->key(array('tag' => $tag))
- ->execute();
+ try {
+ $this->connection->merge('cache_tags')
+ ->insertFields(array('deletions' => 1))
+ ->expression('deletions', 'deletions + 1')
+ ->key(array('tag' => $tag))
+ ->execute();
+ }
+ catch (\Exception $e) {
+ $this->catchException($e, 'cache_tags');
+ }
}
}
@@ -200,7 +238,12 @@ public function deleteTags(array $tags) {
* Implements Drupal\Core\Cache\CacheBackendInterface::deleteAll().
*/
public function deleteAll() {
- $this->connection->truncate($this->bin)->execute();
+ try {
+ $this->connection->truncate($this->bin)->execute();
+ }
+ catch (\Exception $e) {
+ $this->catchException($e);
+ }
}
/**
@@ -214,28 +257,38 @@ public function invalidate($cid) {
* Implements Drupal\Core\Cache\CacheBackendInterface::invalideMultiple().
*/
public function invalidateMultiple(array $cids) {
- // Update in chunks when a large array is passed.
- do {
- $this->connection->update($this->bin)
- ->fields(array('expire' => REQUEST_TIME - 1))
- ->condition('cid', array_splice($cids, 0, 1000), 'IN')
- ->execute();
+ try {
+ // Update in chunks when a large array is passed.
+ do {
+ $this->connection->update($this->bin)
+ ->fields(array('expire' => REQUEST_TIME - 1))
+ ->condition('cid', array_splice($cids, 0, 1000), 'IN')
+ ->execute();
+ }
+ while (count($cids));
+ }
+ catch (\Exception $e) {
+ $this->catchException($e);
}
- while (count($cids));
}
/**
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
*/
public function invalidateTags(array $tags) {
- $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
- foreach ($this->flattenTags($tags) as $tag) {
- unset($tag_cache[$tag]);
- $this->connection->merge('cache_tags')
- ->insertFields(array('invalidations' => 1))
- ->expression('invalidations', 'invalidations + 1')
- ->key(array('tag' => $tag))
- ->execute();
+ try {
+ $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache');
+ foreach ($this->flattenTags($tags) as $tag) {
+ unset($tag_cache[$tag]);
+ $this->connection->merge('cache_tags')
+ ->insertFields(array('invalidations' => 1))
+ ->expression('invalidations', 'invalidations + 1')
+ ->key(array('tag' => $tag))
+ ->execute();
+ }
+ }
+ catch (\Exception $e) {
+ $this->catchException($e, 'cache_tags');
}
}
@@ -243,19 +296,31 @@ public function invalidateTags(array $tags) {
* Implements Drupal\Core\Cache\CacheBackendInterface::invalidateAll().
*/
public function invalidateAll() {
- $this->connection->update($this->bin)
- ->fields(array('expire' => REQUEST_TIME - 1))
- ->execute();
+ try {
+ $this->connection->update($this->bin)
+ ->fields(array('expire' => REQUEST_TIME - 1))
+ ->execute();
+ }
+ catch (\Exception $e) {
+ $this->catchException($e);
+ }
}
/**
* Implements Drupal\Core\Cache\CacheBackendInterface::garbageCollection().
*/
public function garbageCollection() {
- Database::getConnection()->delete($this->bin)
- ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>')
- ->condition('expire', REQUEST_TIME, '<')
- ->execute();
+ try {
+ Database::getConnection()->delete($this->bin)
+ ->condition('expire', CacheBackendInterface::CACHE_PERMANENT, '<>')
+ ->condition('expire', REQUEST_TIME, '<')
+ ->execute();
+ }
+ catch (\Exception $e) {
+ // If the table does not exist, it surely does not have garbage in it.
+ // If the table exists, the next garbage collection will clean up.
+ // There is nothing to do.
+ }
}
/**
@@ -329,9 +394,160 @@ public function isEmpty() {
$this->garbageCollection();
$query = $this->connection->select($this->bin);
$query->addExpression('1');
- $result = $query->range(0, 1)
- ->execute()
- ->fetchField();
+ try {
+ $result = $query->range(0, 1)
+ ->execute()
+ ->fetchField();
+ }
+ catch (\Exception $e) {
+ $this->catchException($e);
+ }
return empty($result);
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeBin() {
+ try {
+ $this->connection->schema()->dropTable($this->bin);
+ }
+ catch (\Exception $e) {
+ $this->catchException($e);
+ }
+ }
+
+ /**
+ * Check if the cache bin exists and create it if not.
+ */
+ protected function ensureBinExists() {
+ try {
+ $database_schema = $this->connection->schema();
+ if (!$database_schema->tableExists($this->bin)) {
+ $schema_definition = $this->schemaDefinition();
+ $database_schema->createTable($this->bin, $schema_definition['bin']);
+ // If the bin doesn't exist, the cache tags table may also not exist.
+ if (!$database_schema->tableExists('cache_tags')) {
+ $database_schema->createTable('cache_tags', $schema_definition['cache_tags']);
+ }
+ return TRUE;
+ }
+ }
+ // If another process has already created the cache table, attempting to
+ // recreate it will throw an exception. In this case just catch the
+ // exception and do nothing.
+ catch (SchemaObjectExistsException $e) {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Act on an exception when cache might be stale.
+ *
+ * If the cache_tags table does not yet exist, that's fine but if the table
+ * exists and yet the query failed, then the cache is stale and the
+ * exception needs to propagate.
+ *
+ * @param $e
+ * The exception.
+ * @param string|null $table_name
+ * The table name, defaults to $this->bin. Can be cache_tags.
+ */
+ protected function catchException(\Exception $e, $table_name = NULL) {
+ if ($this->connection->schema()->tableExists($table_name ?: $this->bin)) {
+ throw $e;
+ }
+ }
+
+ /**
+ * Defines the schema for the cache bin and cache_tags table.
+ */
+ public function schemaDefinition() {
+ $schema['bin'] = array(
+ 'description' => 'Storage for the cache API.',
+ 'fields' => array(
+ 'cid' => array(
+ 'description' => 'Primary Key: Unique cache ID.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'data' => array(
+ 'description' => 'A collection of data to cache.',
+ 'type' => 'blob',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ ),
+ 'expire' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'created' => array(
+ 'description' => 'A Unix timestamp indicating when the cache entry was created.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'serialized' => array(
+ 'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
+ 'type' => 'int',
+ 'size' => 'small',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'tags' => array(
+ 'description' => 'Space-separated list of cache tags for this entry.',
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'checksum_invalidations' => array(
+ 'description' => 'The tag invalidation sum when this entry was saved.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'checksum_deletions' => array(
+ 'description' => 'The tag deletion sum when this entry was saved.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'indexes' => array(
+ 'expire' => array('expire'),
+ ),
+ 'primary key' => array('cid'),
+ );
+ $schema['cache_tags'] = array(
+ 'description' => 'Cache table for tracking cache tags related to the cache bin.',
+ 'fields' => array(
+ 'tag' => array(
+ 'description' => 'Namespace-prefixed tag string.',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'invalidations' => array(
+ 'description' => 'Number incremented when the tag is invalidated.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ 'deletions' => array(
+ 'description' => 'Number incremented when the tag is deleted.',
+ 'type' => 'int',
+ 'not null' => TRUE,
+ 'default' => 0,
+ ),
+ ),
+ 'primary key' => array('tag'),
+ );
+ return $schema;
+ }
}
diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php
index ac83a06..4dba0ec 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackend.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php
@@ -215,4 +215,10 @@ public function isEmpty() {
*/
public function garbageCollection() {
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeBin() {}
+
}
diff --git a/core/lib/Drupal/Core/Cache/NullBackend.php b/core/lib/Drupal/Core/Cache/NullBackend.php
index 16c0331..127101a 100644
--- a/core/lib/Drupal/Core/Cache/NullBackend.php
+++ b/core/lib/Drupal/Core/Cache/NullBackend.php
@@ -98,4 +98,9 @@ public function garbageCollection() {}
public function isEmpty() {
return TRUE;
}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function removeBin() {}
}
diff --git a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
index 4fbba3b..73785fd 100644
--- a/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
+++ b/core/lib/Drupal/Core/Database/Driver/mysql/Schema.php
@@ -480,7 +480,7 @@ public function prepareComment($comment, $length = NULL) {
// Truncate comment to maximum comment length.
if (isset($length)) {
// Add table prefixes before truncating.
- $comment = truncate_utf8($this->connection->prefixTables($comment), $length, TRUE, TRUE);
+ $comment = substr($this->connection->prefixTables($comment), 0, $length);
}
return $this->connection->quote($comment);
diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
index f3c02ed..0487d03 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
@@ -42,13 +42,37 @@ class EntityType extends Plugin {
public $base_table;
/**
- * The name of the class that is used to load the objects.
+ * An associative array where the keys are the names of different controller
+ * types (listed below) and the values are the names of the classes that
+ * implement that controller:
+ * - storage: The name of the class that is used to load the objects. The
+ * class must implement \Drupal\Core\Entity\EntityStorageControllerInterface.
+ * - form: An associative array where the keys are the names of the different
+ * form operations (such as 'create', 'edit', or 'delete') and the values
+ * are the names of the controller classes for those operations. The name of
+ * the operation is passed also to the form controller's constructor, so
+ * that one class can be used for multiple entity forms when the forms are
+ * similar. The classes must implement
+ * \Drupal\Core\Entity\EntityFormControllerInterface
+ * - list: The name of the class that provides listings of the entities. The
+ * class must implement \Drupal\Core\Entity\EntityListControllerInterface.
+ * - render: The name of the class that is used to render the entities. The
+ * class must implement \Drupal\Core\Entity\EntityRenderControllerInterface.
+ * - access: The name of the class that is used for access checks. The class
+ * must implement \Drupal\Core\Entity\EntityAccessControllerInterface.
+ * Defaults to \Drupal\Core\Entity\EntityAccessController.
+ * - translation: The name of the controller class that should be used to
+ * handle the translation process. The class must implement
+ * \Drupal\translation_entity\EntityTranslationControllerInterface.
*
- * This must implement \Drupal\Core\Entity\EntityStorageControllerInterface.
+ * @todo Interfaces from outside \Drupal\Core or \Drupal\Component should not
+ * be used here.
*
- * @var string
+ * @var array
*/
- public $controller_class = 'Drupal\Core\Entity\DatabaseStorageController';
+ public $controllers = array(
+ 'access' => 'Drupal\Core\Entity\EntityAccessController',
+ );
/**
* Boolean indicating whether fields can be attached to entities of this type.
@@ -68,20 +92,6 @@ class EntityType extends Plugin {
public $field_cache = TRUE;
/**
- * The names of classes for various form operations.
- *
- * An associative array where the keys are the names of the different form
- * operations (such as 'create', 'edit', or 'delete') and the values are the
- * names of the controller classes for those operations. The name of the
- * operation is passed also to the form controller's constructor, so that one
- * class can be used for multiple entity forms when the forms are similar.
- * Defaults to Drupal\Core\Entity\EntityFormController.
- *
- * @var array (optional)
- */
- public $form_controller_class = array('Drupal\Core\Entity\EntityFormController');
-
- /**
* The human-readable name of the type.
*
* @var string
@@ -115,43 +125,6 @@ class EntityType extends Plugin {
public $label_callback;
/**
- * The name of the class that provides listings of the entities.
- *
- * The class must implement \Drupal\Core\Entity\EntityListControllerInterface.
- *
- * @var string
- */
- public $list_controller_class = 'Drupal\Core\Entity\EntityListController';
-
- /**
- * The name of the class that is used to render the entities.
- *
- * @var string
- */
- public $render_controller_class;
-
- /**
- * The name of the class that is used for access checks.
- *
- * The class must implement \Drupal\Core\Entity\EntityAccessControllerInterface.
- *
- * @var string
- */
- public $access_controller_class = 'Drupal\Core\Entity\EntityAccessController';
-
- /**
- * The name of the translation controller class that should be used to handle the translation process.
- *
- * The class must implement \Drupal\translation_entity\EntityTranslationControllerInterface.
- *
- * @todo Interfaces from outside \Drupal\Core or \Drupal\Component should not
- * be used here.
- *
- * @var string
- */
- public $translation_controller_class;
-
- /**
* Boolean indicating whether entities should be statically cached during a page request.
*
* @todo This is only used by \Drupal\Core\Entity\DatabaseStorageController.
diff --git a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
index 25ebce2..b1d8ec3 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageController.php
@@ -344,11 +344,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 1048ca1..c8ff3e2 100644
--- a/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
+++ b/core/lib/Drupal/Core/Entity/DatabaseStorageControllerNG.php
@@ -241,7 +241,7 @@ protected function attachPropertyData(array &$entities, $load_revision = FALSE)
// Fetch the field definitions to check which field is translatable.
$field_definition = $this->getFieldDefinitions(array());
- $data_fields = array_flip($this->entityInfo['schema_fields_sql']['data_table']);
+ $data_fields = array_flip(drupal_schema_fields_sql($this->entityInfo['data_table']));
foreach ($data as $values) {
$id = $values[$this->idKey];
@@ -439,7 +439,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;
@@ -456,7 +456,7 @@ protected function mapToStorageRecord(EntityInterface $entity) {
*/
protected function mapToRevisionStorageRecord(EntityInterface $entity) {
$record = new \stdClass();
- foreach ($this->entityInfo['schema_fields_sql']['revision_table'] as $name) {
+ foreach (drupal_schema_fields_sql($this->entityInfo['revision_table']) as $name) {
if (isset($entity->$name->value)) {
$record->$name = $entity->$name->value;
}
@@ -482,7 +482,7 @@ protected function mapToDataStorageRecord(EntityInterface $entity, $langcode) {
$translation = $entity->getTranslation($langcode, FALSE);
$record = new \stdClass();
- foreach ($this->entityInfo['schema_fields_sql']['data_table'] as $name) {
+ foreach (drupal_schema_fields_sql($this->entityInfo['data_table']) as $name) {
$record->$name = $translation->$name->value;
}
$record->langcode = $langcode;
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 6adeb96..c5d72f2 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -9,7 +9,6 @@
use Drupal\Component\Plugin\PluginManagerBase;
use Drupal\Component\Plugin\Factory\DefaultFactory;
-use Drupal\Component\Plugin\Discovery\ProcessDecorator;
use Drupal\Core\Plugin\Discovery\AlterDecorator;
use Drupal\Core\Plugin\Discovery\CacheDecorator;
use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
@@ -52,7 +51,6 @@ public function __construct(array $namespaces) {
);
$this->discovery = new AnnotatedClassDiscovery('Core', 'Entity', $namespaces, $annotation_namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
$this->discovery = new InfoHookDecorator($this->discovery, 'entity_info');
- $this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
$this->discovery = new AlterDecorator($this->discovery, 'entity_info');
$this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . language(LANGUAGE_TYPE_INTERFACE)->langcode, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
@@ -60,25 +58,6 @@ public function __construct(array $namespaces) {
}
/**
- * Overrides Drupal\Component\Plugin\PluginManagerBase::processDefinition().
- */
- public function processDefinition(&$definition, $plugin_id) {
- parent::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['data_table'])) {
- $definition['schema_fields_sql']['data_table'] = drupal_schema_fields_sql($definition['data_table']);
- }
- if (isset($definition['revision_table'])) {
- $definition['schema_fields_sql']['revision_table'] = drupal_schema_fields_sql($definition['revision_table']);
- }
- }
- }
-
- /**
* Checks whether a certain entity type has a certain controller.
*
* @param string $entity_type
@@ -91,7 +70,7 @@ public function processDefinition(&$definition, $plugin_id) {
*/
public function hasController($entity_type, $controller_type) {
$definition = $this->getDefinition($entity_type);
- return !empty($definition[$controller_type]);
+ return !empty($definition['controllers'][$controller_type]);
}
/**
@@ -110,6 +89,7 @@ public function hasController($entity_type, $controller_type) {
*/
public function getControllerClass($entity_type, $controller_type, $nested = NULL) {
$definition = $this->getDefinition($entity_type);
+ $definition = $definition['controllers'];
if (empty($definition[$controller_type])) {
throw new \InvalidArgumentException(sprintf('The entity (%s) did not specify a %s.', $entity_type, $controller_type));
}
@@ -143,7 +123,7 @@ public function getControllerClass($entity_type, $controller_type, $nested = NUL
*/
public function getStorageController($entity_type) {
if (!isset($this->controllers['storage'][$entity_type])) {
- $class = $this->getControllerClass($entity_type, 'controller_class');
+ $class = $this->getControllerClass($entity_type, 'storage');
$this->controllers['storage'][$entity_type] = new $class($entity_type);
}
return $this->controllers['storage'][$entity_type];
@@ -160,7 +140,7 @@ public function getStorageController($entity_type) {
*/
public function getListController($entity_type) {
if (!isset($this->controllers['listing'][$entity_type])) {
- $class = $this->getControllerClass($entity_type, 'list_controller_class');
+ $class = $this->getControllerClass($entity_type, 'list');
$this->controllers['listing'][$entity_type] = new $class($entity_type, $this->getStorageController($entity_type));
}
return $this->controllers['listing'][$entity_type];
@@ -179,7 +159,7 @@ public function getListController($entity_type) {
*/
public function getFormController($entity_type, $operation) {
if (!isset($this->controllers['form'][$operation][$entity_type])) {
- $class = $this->getControllerClass($entity_type, 'form_controller_class', $operation);
+ $class = $this->getControllerClass($entity_type, 'form', $operation);
$this->controllers['form'][$operation][$entity_type] = new $class($operation);
}
return $this->controllers['form'][$operation][$entity_type];
@@ -196,7 +176,7 @@ public function getFormController($entity_type, $operation) {
*/
public function getRenderController($entity_type) {
if (!isset($this->controllers['render'][$entity_type])) {
- $class = $this->getControllerClass($entity_type, 'render_controller_class');
+ $class = $this->getControllerClass($entity_type, 'render');
$this->controllers['render'][$entity_type] = new $class($entity_type);
}
return $this->controllers['render'][$entity_type];
@@ -213,7 +193,7 @@ public function getRenderController($entity_type) {
*/
public function getAccessController($entity_type) {
if (!isset($this->controllers['access'][$entity_type])) {
- $class = $this->getControllerClass($entity_type, 'access_controller_class');
+ $class = $this->getControllerClass($entity_type, 'access');
$this->controllers['access'][$entity_type] = new $class($entity_type);
}
return $this->controllers['access'][$entity_type];
diff --git a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
index 4ac5d9a..377a002 100644
--- a/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityStorageControllerInterface.php
@@ -10,7 +10,7 @@
/**
* Defines a common interface for entity controller classes.
*
- * All entity controller classes specified via the 'controller_class' key
+ * All entity controller classes specified via the "controllers['storage']" key
* returned by \Drupal\Core\Entity\EntityManager or hook_entity_info_alter()
* have to implement this interface.
*
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php
index 318c741..8298a41 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Feed.php
@@ -19,10 +19,12 @@
* id = "aggregator_feed",
* label = @Translation("Aggregator feed"),
* module = "aggregator",
- * controller_class = "Drupal\aggregator\FeedStorageController",
- * render_controller_class = "Drupal\aggregator\FeedRenderController",
- * form_controller_class = {
- * "default" = "Drupal\aggregator\FeedFormController"
+ * controllers = {
+ * "storage" = "Drupal\aggregator\FeedStorageController",
+ * "render" = "Drupal\aggregator\FeedRenderController",
+ * "form" = {
+ * "default" = "Drupal\aggregator\FeedFormController"
+ * }
* },
* base_table = "aggregator_feed",
* fieldable = TRUE,
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php
index f3fc06c..d7a410d 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Plugin/Core/Entity/Item.php
@@ -19,8 +19,10 @@
* id = "aggregator_item",
* label = @Translation("Aggregator feed item"),
* module = "aggregator",
- * controller_class = "Drupal\aggregator\ItemStorageController",
- * render_controller_class = "Drupal\aggregator\ItemRenderController",
+ * controllers = {
+ * "storage" = "Drupal\aggregator\ItemStorageController",
+ * "render" = "Drupal\aggregator\ItemRenderController"
+ * },
* base_table = "aggregator_item",
* fieldable = TRUE,
* entity_keys = {
diff --git a/core/modules/block/block.install b/core/modules/block/block.install
index b1fc925..01c5779 100644
--- a/core/modules/block/block.install
+++ b/core/modules/block/block.install
@@ -7,17 +7,6 @@
use Drupal\Component\Uuid\Uuid;
/**
- * Implements hook_schema().
- */
-function block_schema() {
-
- $schema['cache_block'] = drupal_get_schema_unprocessed('system', 'cache');
- $schema['cache_block']['description'] = 'Cache table for the Block module to store already built blocks, identified by module, delta, and various contexts which may change the block, such as theme, locale, and caching mode defined for the block.';
-
- return $schema;
-}
-
-/**
* Implements hook_install().
*/
function block_install() {
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
index 933b890..63f5e82 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
@@ -20,13 +20,15 @@
* label = @Translation("Custom Block"),
* bundle_label = @Translation("Custom Block type"),
* module = "custom_block",
- * controller_class = "Drupal\custom_block\CustomBlockStorageController",
- * access_controller_class = "Drupal\custom_block\CustomBlockAccessController",
- * render_controller_class = "Drupal\custom_block\CustomBlockRenderController",
- * form_controller_class = {
- * "default" = "Drupal\custom_block\CustomBlockFormController"
+ * controllers = {
+ * "storage" = "Drupal\custom_block\CustomBlockStorageController",
+ * "access" = "Drupal\custom_block\CustomBlockAccessController",
+ * "render" = "Drupal\custom_block\CustomBlockRenderController",
+ * "form" = {
+ * "default" = "Drupal\custom_block\CustomBlockFormController"
+ * },
+ * "translation" = "Drupal\custom_block\CustomBlockTranslationController"
* },
- * translation_controller_class = "Drupal\custom_block\CustomBlockTranslationController",
* base_table = "custom_block",
* revision_table = "custom_block_revision",
* menu_base_path = "block/%custom_block",
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php
index dca5964..3103120 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlockType.php
@@ -18,10 +18,12 @@
* id = "custom_block_type",
* label = @Translation("Custom block type"),
* module = "custom_block",
- * controller_class = "Drupal\custom_block\CustomBlockTypeStorageController",
- * list_controller_class = "Drupal\custom_block\CustomBlockTypeListController",
- * form_controller_class = {
- * "default" = "Drupal\custom_block\CustomBlockTypeFormController"
+ * controllers = {
+ * "storage" = "Drupal\custom_block\CustomBlockTypeStorageController",
+ * "form" = {
+ * "default" = "Drupal\custom_block\CustomBlockTypeFormController"
+ * },
+ * "list" = "Drupal\custom_block\CustomBlockTypeListController"
* },
* config_prefix = "custom_block.type",
* entity_keys = {
diff --git a/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php b/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php
index 2d8c638..e720226 100644
--- a/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php
+++ b/core/modules/block/lib/Drupal/block/Plugin/Core/Entity/Block.php
@@ -19,12 +19,14 @@
* id = "block",
* label = @Translation("Block"),
* module = "block",
- * controller_class = "Drupal\block\BlockStorageController",
- * access_controller_class = "Drupal\block\BlockAccessController",
- * render_controller_class = "Drupal\block\BlockRenderController",
- * list_controller_class = "Drupal\block\BlockListController",
- * form_controller_class = {
- * "default" = "Drupal\block\BlockFormController"
+ * controllers = {
+ * "storage" = "Drupal\block\BlockStorageController",
+ * "access" = "Drupal\block\BlockAccessController",
+ * "render" = "Drupal\block\BlockRenderController",
+ * "list" = "Drupal\block\BlockListController",
+ * "form" = {
+ * "default" = "Drupal\block\BlockFormController"
+ * }
* },
* config_prefix = "block.block",
* fieldable = FALSE,
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php
index 57e4b61..545f832 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/Breakpoint.php
@@ -23,7 +23,9 @@
* id = "breakpoint",
* label = @Translation("Breakpoint"),
* module = "breakpoint",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "breakpoint.breakpoint",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php
index 1b25224..8967081 100644
--- a/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php
+++ b/core/modules/breakpoint/lib/Drupal/breakpoint/Plugin/Core/Entity/BreakpointGroup.php
@@ -20,7 +20,9 @@
* id = "breakpoint_group",
* label = @Translation("Breakpoint group"),
* module = "breakpoint",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "breakpoint.breakpoint_group",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
index e8af942..b57c74f 100644
--- a/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
+++ b/core/modules/comment/lib/Drupal/comment/Plugin/Core/Entity/Comment.php
@@ -20,13 +20,15 @@
* label = @Translation("Comment"),
* bundle_label = @Translation("Content type"),
* module = "comment",
- * controller_class = "Drupal\comment\CommentStorageController",
- * access_controller_class = "Drupal\comment\CommentAccessController",
- * render_controller_class = "Drupal\comment\CommentRenderController",
- * form_controller_class = {
- * "default" = "Drupal\comment\CommentFormController"
+ * controllers = {
+ * "storage" = "Drupal\comment\CommentStorageController",
+ * "access" = "Drupal\comment\CommentAccessController",
+ * "render" = "Drupal\comment\CommentRenderController",
+ * "form" = {
+ * "default" = "Drupal\comment\CommentFormController"
+ * },
+ * "translation" = "Drupal\comment\CommentTranslationController"
* },
- * translation_controller_class = "Drupal\comment\CommentTranslationController",
* base_table = "comment",
* uri_callback = "comment_uri",
* fieldable = TRUE,
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverrideWebTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverrideWebTest.php
index 9f9579d..204e274 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverrideWebTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLocaleOverrideWebTest.php
@@ -14,7 +14,7 @@
*/
class ConfigLocaleOverrideWebTest extends WebTestBase {
- public static $modules = array('locale', 'language', 'system', 'config_test');
+ public static $modules = array('locale', 'language', 'system');
public static function getInfo() {
return array(
@@ -35,26 +35,35 @@ function testSiteNameTranslation() {
$adminUser = $this->drupalCreateUser(array('administer site configuration', 'administer languages'));
$this->drupalLogin($adminUser);
- // Add French and make it the site default language.
- $this->drupalPost('admin/config/regional/language/add', array('predefined_langcode' => 'fr'), t('Add language'));
+ // Add a custom lanugage.
+ $langcode = 'xx';
+ $name = $this->randomName(16);
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+
+ // Save an override for the XX language.
+ config('locale.config.xx.system.site')->set('name', 'XX site name')->save();
$this->drupalLogout();
// The home page in English should not have the override.
$this->drupalGet('');
- $this->assertNoText('French site name');
+ $this->assertNoText('XX site name');
// During path resolution the system.site configuration object is used to
// determine the front page. This occurs before language negotiation causing
// the configuration factory to cache an object without the correct
- // overrides. The config_test module includes a
- // locale.config.fr.system.site.yml which overrides the site name to 'French
- // site name' to test that the configuration factory is re-initialised
- // language negotiation. Ensure that it applies when we access the French
- // front page.
+ // overrides. We are testing that the configuration factory is
+ // re-initialised after language negotiation. Ensure that it applies when
+ // we access the XX front page.
// @see \Drupal\Core\PathProcessor::processInbound()
- $this->drupalGet('fr');
- $this->assertText('French site name');
+ $this->drupalGet('xx');
+ $this->assertText('XX site name');
}
}
diff --git a/core/modules/config/tests/config_test/config/locale.config.fr.system.site.yml b/core/modules/config/tests/config_test/config/locale.config.fr.system.site.yml
deleted file mode 100644
index 0e4081b..0000000
--- a/core/modules/config/tests/config_test/config/locale.config.fr.system.site.yml
+++ /dev/null
@@ -1 +0,0 @@
-name: 'French site name'
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigQueryTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigQueryTest.php
index 823840b..b62538e 100644
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigQueryTest.php
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigQueryTest.php
@@ -17,10 +17,12 @@
* id = "config_query_test",
* label = @Translation("Test configuration for query"),
* module = "config_test",
- * controller_class = "Drupal\config_test\ConfigTestStorageController",
- * list_controller_class = "Drupal\Core\Config\Entity\ConfigEntityListController",
- * form_controller_class = {
- * "default" = "Drupal\config_test\ConfigTestFormController"
+ * controllers = {
+ * "storage" = "Drupal\config_test\ConfigTestStorageController",
+ * "list" = "Drupal\Core\Config\Entity\ConfigEntityListController",
+ * "form" = {
+ * "default" = "Drupal\config_test\ConfigTestFormController"
+ * }
* },
* uri_callback = "config_test_uri",
* config_prefix = "config_query_test.dynamic",
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
index ba2e8a7..2983a44 100644
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTest.php
@@ -18,10 +18,12 @@
* id = "config_test",
* label = @Translation("Test configuration"),
* module = "config_test",
- * controller_class = "Drupal\config_test\ConfigTestStorageController",
- * list_controller_class = "Drupal\Core\Config\Entity\ConfigEntityListController",
- * form_controller_class = {
- * "default" = "Drupal\config_test\ConfigTestFormController"
+ * controllers = {
+ * "storage" = "Drupal\config_test\ConfigTestStorageController",
+ * "list" = "Drupal\Core\Config\Entity\ConfigEntityListController",
+ * "form" = {
+ * "default" = "Drupal\config_test\ConfigTestFormController"
+ * }
* },
* uri_callback = "config_test_uri",
* config_prefix = "config_test.dynamic",
diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTestEmptyManifest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTestEmptyManifest.php
index a8afe02..a122204 100644
--- a/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTestEmptyManifest.php
+++ b/core/modules/config/tests/config_test/lib/Drupal/config_test/Plugin/Core/Entity/ConfigTestEmptyManifest.php
@@ -18,7 +18,9 @@
* id = "config_test_empty_manifest",
* label = @Translation("Test empty manifest creation"),
* module = "config_test",
- * controller_class = "Drupal\config_test\ConfigTestStorageController",
+ * controllers = {
+ * "storage" = "Drupal\config_test\ConfigTestStorageController"
+ * },
* config_prefix = "config_test.empty_manifest",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php
index f6afcfe..afb3ff0 100644
--- a/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php
+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Category.php
@@ -18,10 +18,12 @@
* id = "contact_category",
* label = @Translation("Contact category"),
* module = "contact",
- * controller_class = "Drupal\contact\CategoryStorageController",
- * list_controller_class = "Drupal\contact\CategoryListController",
- * form_controller_class = {
- * "default" = "Drupal\contact\CategoryFormController"
+ * controllers = {
+ * "storage" = "Drupal\contact\CategoryStorageController",
+ * "list" = "Drupal\contact\CategoryListController",
+ * "form" = {
+ * "default" = "Drupal\contact\CategoryFormController"
+ * }
* },
* uri_callback = "contact_category_uri",
* config_prefix = "contact.category",
diff --git a/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.php b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.php
index ed67a45..4bc9dbc 100644
--- a/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.php
+++ b/core/modules/contact/lib/Drupal/contact/Plugin/Core/Entity/Message.php
@@ -18,10 +18,13 @@
* id = "contact_message",
* label = @Translation("Contact message"),
* module = "contact",
- * form_controller_class = {
- * "default" = "Drupal\contact\MessageFormController"
+ * controllers = {
+ * "storage" = "Drupal\Core\Entity\DatabaseStorageController",
+ * "render" = "Drupal\contact\MessageRenderController",
+ * "form" = {
+ * "default" = "Drupal\contact\MessageFormController"
+ * }
* },
- * render_controller_class = "Drupal\contact\MessageRenderController",
* entity_keys = {
* "bundle" = "category"
* },
diff --git a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
index bdf56c5..d3737bb 100644
--- a/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
+++ b/core/modules/edit/lib/Drupal/edit/Tests/EditTestBase.php
@@ -13,7 +13,6 @@
* Parent class for Edit tests.
*/
class EditTestBase extends DrupalUnitTestBase {
- var $default_storage = 'field_sql_storage';
/**
* Modules to enable.
@@ -29,9 +28,7 @@ function setUp() {
$this->installSchema('system', 'variable');
$this->installSchema('entity_test', array('entity_test', 'entity_test_rev'));
-
- // Set default storage backend.
- variable_set('field_storage_default', $this->default_storage);
+ $this->installConfig(array('field'));
}
/**
diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/Core/Entity/Editor.php b/core/modules/editor/lib/Drupal/editor/Plugin/Core/Entity/Editor.php
index a1c2329..99ffd93 100644
--- a/core/modules/editor/lib/Drupal/editor/Plugin/Core/Entity/Editor.php
+++ b/core/modules/editor/lib/Drupal/editor/Plugin/Core/Entity/Editor.php
@@ -18,7 +18,9 @@
* id = "editor",
* label = @Translation("Editor"),
* module = "editor",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "editor.editor",
* entity_keys = {
* "id" = "format",
diff --git a/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php
index 757094d..af01eb4 100644
--- a/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php
+++ b/core/modules/entity/lib/Drupal/entity/Plugin/Core/Entity/EntityDisplay.php
@@ -19,7 +19,9 @@
* id = "entity_display",
* label = @Translation("Entity display"),
* module = "entity",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "entity.display",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
index 1257f48..11f6da7 100644
--- a/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
+++ b/core/modules/entity/lib/Drupal/entity/Tests/EntityDisplayTest.php
@@ -24,6 +24,11 @@ public static function getInfo() {
);
}
+ protected function setUp() {
+ parent::setUp();
+ $this->installConfig(array('field'));
+ }
+
/**
* Tests basic CRUD operations on EntityDisplay objects.
*/
diff --git a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
index cba9a82..ab60905 100644
--- a/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
+++ b/core/modules/entity_reference/lib/Drupal/entity_reference/Plugin/entity_reference/selection/SelectionBase.php
@@ -113,7 +113,7 @@ public static function settingsForm(&$field, &$instance) {
// @todo Use Entity::getPropertyDefinitions() when all entity types are
// converted to the new Field API.
- $fields = drupal_map_assoc($entity_info['schema_fields_sql']['base_table']);
+ $fields = drupal_map_assoc(drupal_schema_fields_sql($entity_info['base_table']));
foreach (field_info_instances($field['settings']['target_type']) as $bundle_instances) {
foreach ($bundle_instances as $instance_name => $instance_info) {
$field_info = field_info_field($instance_name);
diff --git a/core/modules/field/config/field.settings.yml b/core/modules/field/config/field.settings.yml
index b6172c1..5ca0000 100644
--- a/core/modules/field/config/field.settings.yml
+++ b/core/modules/field/config/field.settings.yml
@@ -1 +1,3 @@
+default_storage: field_sql_storage
+language_fallback: '1'
purge_batch_size: 10
diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml
index b9e51d6..51cfc8f 100644
--- a/core/modules/field/config/schema/field.schema.yml
+++ b/core/modules/field/config/schema/field.schema.yml
@@ -4,6 +4,140 @@ field.settings:
type: mapping
label: 'Field settings'
mapping:
+ default_storage:
+ type: string
+ label: 'The default storage backend for a field'
+ language_fallback:
+ type: boolean
+ label: 'Whether the field display falls back to global language fallback configuration'
purge_batch_size:
type: integer
label: 'Maximum number of field data records to purge'
+
+field.field.*:
+ type: mapping
+ label: 'Field'
+ mapping:
+ id:
+ type: string
+ label: 'Machine name'
+ uuid:
+ type: string
+ label: 'UUID'
+ status:
+ type: boolean
+ label: 'Status'
+ langcode:
+ type: string
+ label: 'Default language'
+ type:
+ type: string
+ label: 'Type'
+ settings:
+ type: field.[%parent.type].settings
+ module:
+ type: string
+ label: 'Module'
+ active:
+ type: boolean
+ label: 'Active'
+ entity_types:
+ type: sequence
+ label: 'Allowed entity types'
+ sequence:
+ - type: string
+ label: 'Entity type'
+ storage:
+ type: mapping
+ label: 'Storage'
+ mapping:
+ type:
+ type: string
+ label: 'Type'
+ settings:
+ type: field_storage.[%parent.type].settings
+ label: 'Settings'
+ module:
+ type: string
+ label: 'Module'
+ active:
+ type: boolean
+ label: 'Active'
+ locked:
+ type: boolean
+ label: 'Locked'
+ cardinality:
+ type: integer
+ label: 'Maximum number of values users can enter'
+ translatable:
+ type: boolean
+ label: 'Translatable'
+ indexes:
+ type: sequence
+ label: 'Indexes'
+ sequence:
+ - type: sequence
+ label: 'Indexes'
+ sequence:
+ - type: string
+ label: 'Column'
+
+field.instance.*.*.*:
+ type: mapping
+ label: 'Field instance'
+ mapping:
+ id:
+ type: string
+ label: 'ID'
+ uuid:
+ type: string
+ label: 'UUID'
+ status:
+ type: boolean
+ label: 'Status'
+ langcode:
+ type: string
+ label: 'Default language'
+ field_uuid:
+ type: string
+ label: 'Field UUID'
+ entity_type:
+ type: string
+ label: 'Allowed entity types'
+ bundle:
+ type: string
+ label: 'Bundle'
+ label:
+ type: label
+ label: 'Label'
+ description:
+ type: text
+ label: 'Help text'
+ required:
+ type: boolean
+ label: 'Required field'
+ default_value:
+ type: field.[%parent.field_type].value
+ default_value_function:
+ type: string
+ label: 'Default value funtion'
+ settings:
+ type: field.[%parent.field_type].instance_settings
+ widget:
+ type: mapping
+ label: 'Widget'
+ mapping:
+ weight:
+ type: integer
+ label: 'Weight'
+ type:
+ type: string
+ label: 'Widget type'
+ settings:
+ type: field_widget.[%parent.type].settings
+ module:
+ type: string
+ label: 'Module'
+ field_type:
+ type: string
+ label: 'Field type'
diff --git a/core/modules/field/field.api.php b/core/modules/field/field.api.php
index 7b95762..5ba15c9 100644
--- a/core/modules/field/field.api.php
+++ b/core/modules/field/field.api.php
@@ -153,8 +153,7 @@ function hook_field_extra_fields_alter(&$info) {
* the field type module depends on).
* - no_ui: (optional) A boolean specifying that users should not be allowed
* to create fields and instances of this field type through the UI. Such
- * fields can only be created programmatically with field_create_field()
- * and field_create_instance(). Defaults to FALSE.
+ * fields can only be created programmatically. Defaults to FALSE.
*
* @see hook_field_info_alter()
*/
@@ -229,10 +228,10 @@ function hook_field_info_alter(&$info) {
* storage.
* - indexes: (optional) An array of Schema API index definitions. Only
* columns that appear in the 'columns' array are allowed. Those indexes
- * will be used as default indexes. Callers of field_create_field() can
- * specify additional indexes or, at their own risk, modify the default
- * indexes specified by the field-type module. Some storage engines might
- * not support indexes.
+ * will be used as default indexes. Individual field definitions can
+ * specify additional indexes or modify, at their own risk, the indexes
+ * specified by the field type. Some storage engines might not support
+ * indexes.
* - foreign keys: (optional) An array of Schema API foreign key definitions.
* Note, however, that the field data is not necessarily stored in SQL.
* Also, the possible usage is limited, as you cannot specify another field
@@ -534,11 +533,10 @@ function hook_field_update(\Drupal\Core\Entity\EntityInterface $entity, $field,
/**
* Update the storage information for a field.
*
- * This is invoked on the field's storage module from field_update_field(),
- * before the new field information is saved to the database. The field storage
- * module should update its storage tables to agree with the new field
- * information. If there is a problem, the field storage module should throw an
- * exception.
+ * This is invoked on the field's storage module when updating the field,
+ * before the new definition is saved to the database. The field storage module
+ * should update its storage tables according to the new field definition. If
+ * there is a problem, the field storage module should throw an exception.
*
* @param $field
* The updated field structure to be saved.
@@ -1142,7 +1140,7 @@ function hook_field_attach_prepare_translation_alter(\Drupal\Core\Entity\EntityI
function hook_field_language_alter(&$display_langcode, $context) {
// Do not apply core language fallback rules if they are disabled or if Locale
// is not registered as a translation handler.
- if (variable_get('field_language_fallback', TRUE) && field_has_translation_handler($context['entity']->entityType())) {
+ if (field_language_fallback_enabled() && field_has_translation_handler($context['entity']->entityType())) {
field_language_fallback($display_langcode, $context['entity'], $context['langcode']);
}
}
@@ -1626,7 +1624,7 @@ function hook_field_storage_query($query) {
/**
* Act on creation of a new field.
*
- * This hook is invoked from field_create_field() to ask the field storage
+ * This hook is invoked during the creation of a field to ask the field storage
* module to save field information and prepare for storing field instances. If
* there is a problem, the field storage module should throw an exception.
*
@@ -1644,7 +1642,7 @@ function hook_field_storage_create_field($field) {
/**
* Act on deletion of a field.
*
- * This hook is invoked from field_delete_field() to ask the field storage
+ * This hook is invoked during the deletion of a field to ask the field storage
* module to mark all information stored in the field for deletion.
*
* @param $field
@@ -1671,8 +1669,9 @@ function hook_field_storage_delete_field($field) {
/**
* Act on deletion of a field instance.
*
- * This hook is invoked from field_delete_instance() to ask the field storage
- * module to mark all information stored for the field instance for deletion.
+ * This hook is invoked during the deletion of a field instance to ask the
+ * field storage module to mark all information stored for the field instance
+ * for deletion.
*
* @param $instance
* The instance being deleted.
@@ -1885,8 +1884,8 @@ function hook_field_widget_properties_ENTITY_TYPE_alter(array &$widget_propertie
/**
* Act on a field being created.
*
- * This hook is invoked from field_create_field() after the field is created, to
- * allow modules to act on field creation.
+ * This hook lets modules react to the creation of a field. It is called after
+ * the definition is saved, so it cannot be used to modify the field itself.
*
* @param $field
* The field just created.
@@ -1898,8 +1897,9 @@ function hook_field_create_field($field) {
/**
* Act on a field instance being created.
*
- * This hook is invoked from field_create_instance() after the instance record
- * is saved, so it cannot be used to modify the instance itself.
+ * This hook lets modules react to the creation of a field instance. It is
+ * called after the definition is saved, so it cannot be used to modify the
+ * instance itself.
*
* @param $instance
* The instance just created.
@@ -1951,7 +1951,9 @@ function hook_field_update_forbid($field, $prior_field, $has_data) {
/**
* Act on a field being updated.
*
- * This hook is invoked just after field is updated in field_update_field().
+ * This hook lets modules react to the update of an existing field. It is
+ * called after the definition is saved, so it cannot be used to modify the
+ * field itself.
*
* @param $field
* The field as it is post-update.
@@ -1968,7 +1970,8 @@ function hook_field_update_field($field, $prior_field, $has_data) {
/**
* Act on a field being deleted.
*
- * This hook is invoked just after a field is deleted by field_delete_field().
+ * This hook lets modules react to the deletion of an existing field. It is
+ * called after the definition is deleted.
*
* @param $field
* The field just deleted.
@@ -1980,8 +1983,9 @@ function hook_field_delete_field($field) {
/**
* Act on a field instance being updated.
*
- * This hook is invoked from field_update_instance() after the instance record
- * is saved, so it cannot be used by a module to modify the instance itself.
+ * This hook lets modules react to the update of an existing field instance. It
+ * is called after the definition is saved, so it cannot be used to modify the
+ * instance itself.
*
* @param $instance
* The instance as it is post-update.
@@ -1995,8 +1999,8 @@ function hook_field_update_instance($instance, $prior_instance) {
/**
* Act on a field instance being deleted.
*
- * This hook is invoked from field_delete_instance() after the instance is
- * deleted.
+ * This hook lets modules react to the deletion of an existing field instance.
+ * It is called after the definition is deleted.
*
* @param $instance
* The instance just deleted.
diff --git a/core/modules/field/field.attach.inc b/core/modules/field/field.attach.inc
index 51d2d57..cde15f4 100644
--- a/core/modules/field/field.attach.inc
+++ b/core/modules/field/field.attach.inc
@@ -22,8 +22,9 @@
* the data in SQL differently or use a completely different storage mechanism
* such as a cloud-based database.
*
- * Each field defines which storage backend it uses. The Drupal system variable
- * 'field_storage_default' identifies the storage backend used by default.
+ * Each field defines which storage backend it uses. The Drupal configuration
+ * 'field.settings.default_storage' identifies the storage backend used by
+ * default.
*
* See @link field Field API @endlink for information about the other parts of
* the Field API.
diff --git a/core/modules/field/field.crud.inc b/core/modules/field/field.crud.inc
index 14566f2..dba30fa 100644
--- a/core/modules/field/field.crud.inc
+++ b/core/modules/field/field.crud.inc
@@ -45,8 +45,8 @@
* - settings: each omitted setting is given the default value defined in
* hook_field_info().
* - storage:
- * - type: the storage backend specified in the 'field_storage_default'
- * system variable.
+ * - type: the storage backend specified in the
+ * 'field.settings.default_storage' configuration.
* - settings: each omitted setting is given the default value specified in
* hook_field_storage_info().
*
@@ -55,6 +55,9 @@
*
* @throws Drupal\field\FieldException
*
+ * @deprecated as of Drupal 8.0. Use
+ * entity_create('field_entity', $definition)->save().
+ *
* See: @link field Field API data structures @endlink.
*/
function field_create_field(array $field) {
@@ -83,6 +86,8 @@ function field_create_field(array $field) {
*
* @throws Drupal\field\FieldException
*
+ * @deprecated as of Drupal 8.0. Use $field->save().
+ *
* @see field_create_field()
*/
function field_update_field($field) {
@@ -214,6 +219,8 @@ function field_read_fields($conditions = array(), $include_additional = array())
*
* @param $field_name
* The field name to delete.
+ *
+ * @deprecated as of Drupal 8.0. Use $field->delete().
*/
function field_delete_field($field_name) {
if ($field = field_info_field($field_name)) {
@@ -244,6 +251,9 @@ function field_delete_field($field_name) {
*
* @throws Drupal\field\FieldException
*
+ * @deprecated as of Drupal 8.0. Use
+ * entity_create('field_instance', $definition)->save().
+ *
* See: @link field Field API data structures @endlink.
*/
function field_create_instance(array $instance) {
@@ -268,6 +278,8 @@ function field_create_instance(array $instance) {
*
* @throws Drupal\field\FieldException
*
+ * @deprecated as of Drupal 8.0. Use $instance->save().
+ *
* @see field_create_instance()
*/
function field_update_instance($instance) {
@@ -423,6 +435,8 @@ function field_read_instances($conditions = array(), $include_additional = array
* 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.
+ *
+ * @deprecated as of Drupal 8.0. Use $instance->delete().
*/
function field_delete_instance(FieldInstance $instance, $field_cleanup = TRUE) {
$instance->delete($field_cleanup);
diff --git a/core/modules/field/field.install b/core/modules/field/field.install
index 663ae69..dcf2b38 100644
--- a/core/modules/field/field.install
+++ b/core/modules/field/field.install
@@ -9,16 +9,6 @@
use Drupal\field\Plugin\Core\Entity\Field;
/**
- * Implements hook_schema().
- */
-function field_schema() {
- $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.';
-
- return $schema;
-}
-
-/**
* Creates a field by writing directly to the database.
*
* @ingroup update_api
@@ -360,7 +350,7 @@ function field_update_8003() {
$deleted_fields = $state->get('field.field.deleted') ?: array();
$deleted_instances = $state->get('field.instance.deleted') ?: array();
- $field_uuids = array();
+ $field_data = array();
// Migrate field definitions.
$records = db_query("SELECT * FROM {field_config}")->fetchAll(PDO::FETCH_ASSOC);
@@ -421,8 +411,12 @@ function field_update_8003() {
}
}
- // Store the UUID with the old field_id so that we can map the instances.
- $field_uuids[$record['id']] = $config['uuid'];
+ // Store the UUID and field type, they will be used when processing
+ // instances.
+ $field_data[$record['id']] = array(
+ 'uuid' => $config['uuid'],
+ 'type' => $record['type'],
+ );
}
// Migrate instance definitions.
@@ -433,7 +427,8 @@ function field_update_8003() {
$config = array(
'id' => $record['entity_type'] . '.' . $record['bundle'] . '.' . $record['field_name'],
'uuid' => $uuid->generate(),
- 'field_uuid' => $field_uuids[$record['field_id']],
+ 'field_uuid' => $field_data[$record['field_id']]['uuid'],
+ 'field_type' => $field_data[$record['field_id']]['type'],
'entity_type' => $record['entity_type'],
'bundle' => $record['bundle'],
'label' => $record['data']['label'],
@@ -481,6 +476,19 @@ function field_update_8003() {
}
/**
+ * Moves field_storage_default and field_language_fallback to config.
+ *
+ * @ingroup config_upgrade
+ */
+function field_update_8004() {
+ update_variable_set('field_language_fallback', TRUE);
+ update_variables_to_config('field.settings', array(
+ 'field_storage_default' => 'default_storage',
+ 'field_language_fallback' => 'language_fallback',
+ ));
+}
+
+/**
* @} End of "addtogroup updates-7.x-to-8.x".
* The next series of updates should start at 9000.
*/
diff --git a/core/modules/field/field.module b/core/modules/field/field.module
index 1e31484..8f459a3 100644
--- a/core/modules/field/field.module
+++ b/core/modules/field/field.module
@@ -333,7 +333,7 @@ function field_field_widget_info_alter(&$info) {
* otherwise all the fallback candidates are inspected to see if there is a
* field translation available in another language.
* By default this is called by field_field_language_alter(), but this
- * behavior can be disabled by setting the 'field_language_fallback'
+ * behavior can be disabled by setting the 'field.settings.language_fallback'
* variable to FALSE.
*
* @param $field_langcodes
diff --git a/core/modules/field/field.multilingual.inc b/core/modules/field/field.multilingual.inc
index 8e5ee39..55b364e 100644
--- a/core/modules/field/field.multilingual.inc
+++ b/core/modules/field/field.multilingual.inc
@@ -64,41 +64,6 @@
*/
/**
- * Implements hook_language_insert().
- */
-function field_language_insert() {
- field_info_cache_clear();
- // If the number of languages is bigger than 1, enable the core language
- // fallback rules.
- // Because the language_count is updated only after the hook is invoked, we
- // check if the language_count is bigger or equal with 1 at the current time.
- if (variable_get('language_count', 1) >= 1) {
- variable_set('field_language_fallback', TRUE);
- }
-}
-
-/**
- * Implements hook_language_update().
- */
-function field_language_update() {
- field_info_cache_clear();
-}
-
-/**
- * Implements hook_language_delete().
- */
-function field_language_delete() {
- field_info_cache_clear();
- // If the number of languages is less than 2, disable the core language
- // fallback rules.
- // Because the language_count is updated after the hook is invoked, we check
- // if the language_count is less or equal with 2 at the current time.
- if (variable_get('language_count', 1) <= 2) {
- variable_set('field_language_fallback', FALSE);
- }
-}
-
-/**
* Collects the available language codes for the given entity type and field.
*
* If the given field has language support enabled, an array of available
@@ -186,6 +151,13 @@ function field_content_languages() {
}
/**
+ * Checks whether field language fallback is enabled.
+ */
+function field_language_fallback_enabled() {
+ return language_multilingual() && config('field.settings')->get('language_fallback');
+}
+
+/**
* Checks whether a field has language support.
*
* A field has language support enabled if its 'translatable' property is set to
@@ -317,7 +289,7 @@ function field_language(EntityInterface $entity, $field_name = NULL, $langcode =
);
// Do not apply core language fallback rules if they are disabled or if
// the entity does not have a translation handler registered.
- if (variable_get('field_language_fallback', FALSE) && field_has_translation_handler($entity_type)) {
+ if (field_language_fallback_enabled() && field_has_translation_handler($entity_type)) {
field_language_fallback($display_langcode, $context['entity'], $context['langcode']);
}
drupal_alter('field_language', $display_langcode, $context);
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
index 14d02e8..b6afefd 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/Field.php
@@ -22,7 +22,9 @@
* id = "field_entity",
* label = @Translation("Field"),
* module = "field",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "field.field",
* entity_keys = {
* "id" = "id",
@@ -323,7 +325,7 @@ public function save() {
// Provide default storage.
$this->storage += array(
- 'type' => variable_get('field_storage_default', 'field_sql_storage'),
+ 'type' => config('field.settings')->get('default_storage'),
'settings' => array(),
);
// Check that the storage type is known.
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
index 363d54f..b9393c0 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/Core/Entity/FieldInstance.php
@@ -19,7 +19,9 @@
* id = "field_instance",
* label = @Translation("Field instance"),
* module = "field",
- * controller_class = "Drupal\field\FieldInstanceStorageController",
+ * controllers = {
+ * "storage" = "Drupal\field\FieldInstanceStorageController"
+ * },
* config_prefix = "field.instance",
* entity_keys = {
* "id" = "id",
@@ -251,6 +253,10 @@ public function __construct(array $values, $entity_type = 'field_instance') {
unset($values['field_name']);
$this->field = $field;
+ // Discard the 'field_type' entry that is added in config records to ease
+ // schema generation. See getExportProperties().
+ unset($values['field_type']);
+
// Check required properties.
if (empty($values['entity_type'])) {
throw new FieldException(format_string('Attempt to create an instance of field @field_id without an entity type.', array('@field_id' => $this->field->id)));
@@ -306,6 +312,11 @@ public function getExportProperties() {
foreach ($names as $name) {
$properties[$name] = $this->get($name);
}
+
+ // Additionally, include the field type, that is needed to be able to
+ // generate the field-type-dependant parts of the config schema.
+ $properties['field_type'] = $this->field->type;
+
return $properties;
}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterLegacyDiscoveryDecorator.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterLegacyDiscoveryDecorator.php
deleted file mode 100644
index 20f3313..0000000
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Formatter/FormatterLegacyDiscoveryDecorator.php
+++ /dev/null
@@ -1,41 +0,0 @@
-discovery = new AnnotatedClassDiscovery('field', 'formatter', $namespaces);
- $this->discovery = new FormatterLegacyDiscoveryDecorator($this->discovery);
$this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
$this->discovery = new AlterDecorator($this->discovery, 'field_formatter_info');
$this->discovery = new CacheDecorator($this->discovery, 'field_formatter_types', 'field');
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/LegacyDiscoveryDecorator.php b/core/modules/field/lib/Drupal/field/Plugin/Type/LegacyDiscoveryDecorator.php
deleted file mode 100644
index 1f94d7c..0000000
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/LegacyDiscoveryDecorator.php
+++ /dev/null
@@ -1,91 +0,0 @@
-decorated = $decorated;
- }
-
- /**
- * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinition().
- */
- public function getDefinition($plugin_id) {
- $definitions = $this->getDefinitions();
- return isset($definitions[$plugin_id]) ? $definitions[$plugin_id] : NULL;
- }
-
- /**
- * Implements Drupal\Component\Plugin\Discovery\DiscoveryInterface::getDefinitions().
- */
- public function getDefinitions() {
- $definitions = $this->decorated->getDefinitions();
-
- $legacy_discovery = new HookDiscovery($this->hook);
- if ($legacy_definitions = $legacy_discovery->getDefinitions()) {
- foreach ($legacy_definitions as $plugin_id => $definition) {
- $this->processDefinition($definition);
-
- if (isset($definition['behaviors']['default value'])) {
- $definition['default_value'] = $definition['behaviors']['default value'];
- unset($definition['behaviors']['default value']);
- }
-
- // Legacy widgets also need a plugin id.
- $definition['id'] = $plugin_id;
-
- $definitions[$plugin_id] = $definition;
- }
- }
- return $definitions;
- }
-
- /**
- * Massages a legacy plugin definition.
- *
- * @var array $definition
- * A plugin definition, as discovered by
- * Drupal\Core\Plugin\Discovery\HookDiscovery.
- *
- * @return array
- * The massaged plugin definition.
- */
- abstract public function processDefinition(array &$definition);
-
-}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetLegacyDiscoveryDecorator.php b/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetLegacyDiscoveryDecorator.php
deleted file mode 100644
index 48dee26..0000000
--- a/core/modules/field/lib/Drupal/field/Plugin/Type/Widget/WidgetLegacyDiscoveryDecorator.php
+++ /dev/null
@@ -1,44 +0,0 @@
-discovery = new AnnotatedClassDiscovery('field', 'widget', $namespaces);
- $this->discovery = new WidgetLegacyDiscoveryDecorator($this->discovery);
$this->discovery = new ProcessDecorator($this->discovery, array($this, 'processDefinition'));
$this->discovery = new AlterDecorator($this->discovery, 'field_widget_info');
$this->discovery = new CacheDecorator($this->discovery, 'field_widget_types', 'field');
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/formatter/LegacyFormatter.php b/core/modules/field/lib/Drupal/field/Plugin/field/formatter/LegacyFormatter.php
deleted file mode 100644
index 17c69fc..0000000
--- a/core/modules/field/lib/Drupal/field/Plugin/field/formatter/LegacyFormatter.php
+++ /dev/null
@@ -1,118 +0,0 @@
-getDefinition();
- $function = $definition['module'] . '_field_formatter_settings_form';
-
- // hook_field_formatter_settings_form() implementations read display
- // properties directly from $instance. Put the actual properties we use
- // here.
- $instance = clone $this->instance;
- $instance['display'][$this->viewMode] = array(
- 'type' => $this->getPluginId(),
- 'settings' => $this->getSettings(),
- 'label' => $this->label,
- );
-
- if (function_exists($function)) {
- return $function($this->field, $instance, $this->viewMode, $form, $form_state);
- }
- return array();
- }
-
- /**
- * Implements Drupal\field\Plugin\Type\Formatter\FormatterInterface::settingsSummary().
- */
- public function settingsSummary() {
- $definition = $this->getDefinition();
- $function = $definition['module'] . '_field_formatter_settings_summary';
-
- // hook_field_formatter_settings_summary() implementations read display
- // properties directly from $instance. Put the actual properties we use
- // here.
- $instance = clone $this->instance;
- $instance['display'][$this->viewMode] = array(
- 'type' => $this->getPluginId(),
- 'settings' => $this->getSettings(),
- 'label' => $this->label,
- );
-
- if (function_exists($function)) {
- return $function($this->field, $instance, $this->viewMode);
- }
- }
-
- /**
- * Implements Drupal\field\Plugin\Type\Formatter\FormatterInterface::prepareView().
- */
- public function prepareView(array $entities, $langcode, array &$items) {
- $definition = $this->getDefinition();
- $function = $definition['module'] . '_field_formatter_prepare_view';
- if (function_exists($function)) {
- // Grab the entity type from the first entity.
- $entity = current($entities);
- $entity_type = $entity->entityType();
-
- // hook_field_formatter_prepare_view() received an array of display properties,
- // for each entity (the same hook could end up being called for different formatters,
- // since one hook implementation could provide several formatters).
- $display = array(
- 'type' => $this->getPluginId(),
- 'settings' => $this->getSettings(),
- 'label' => $this->label,
- );
- $displays = array();
- foreach ($entities as $entity) {
- $displays[$entity->id()] = $display;
- }
-
- $function($entity_type, $entities, $this->field, $this->instance, $langcode, $items, $displays);
- }
- }
-
- /**
- * Implements Drupal\field\Plugin\Type\Formatter\FormatterInterface::viewElements().
- */
- public function viewElements(EntityInterface $entity, $langcode, array $items) {
- $definition = $this->getDefinition();
- $function = $definition['module'] . '_field_formatter_view';
- if (function_exists($function)) {
- // hook_field_formatter_view() received an array of display properties,
- $display = array(
- 'type' => $this->getPluginId(),
- 'settings' => $this->getSettings(),
- 'label' => $this->label,
- );
-
- return $function($entity, $this->field, $this->instance, $langcode, $items, $display);
- }
- }
-
-}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php b/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php
deleted file mode 100644
index a03cb5e..0000000
--- a/core/modules/field/lib/Drupal/field/Plugin/field/widget/LegacyWidget.php
+++ /dev/null
@@ -1,107 +0,0 @@
-getDefinition();
- $function = $definition['module'] . '_field_widget_settings_form';
- if (function_exists($function)) {
- return $function($this->field, $this->instance);
- }
- return array();
- }
-
- /**
- * Implements Drupal\field\Plugin\Type\Widget\WidgetInterface::formElement().
- */
- public function formElement(array $items, $delta, array $element, $langcode, array &$form, array &$form_state) {
- $definition = $this->getDefinition();
- $function = $definition['module'] . '_field_widget_form';
-
- if (function_exists($function)) {
- // hook_field_widget_form() implementations read widget properties directly
- // from $instance. Put the actual properties we use here, which might have
- // been altered by hook_field_widget_property().
- $instance = clone $this->instance;
- $instance['widget']['type'] = $this->getPluginId();
- $instance['widget']['settings'] = $this->getSettings();
-
- return $function($form, $form_state, $this->field, $instance, $langcode, $items, $delta, $element);
- }
- return array();
- }
-
- /**
- * Overrides Drupal\field\Plugin\Type\Widget\WidgetBase::flagErrors().
- *
- * In D7, hook_field_widget_error() was supposed to call form_error() itself,
- * whereas the new errorElement() method simply returns the element to flag.
- * So we override the flagError() method to be more similar to the previous
- * code in field_default_form_errors().
- */
- public function flagErrors(EntityInterface $entity, $langcode, array $items, array $form, array &$form_state) {
- $field_name = $this->field['field_name'];
-
- $field_state = field_form_get_state($form['#parents'], $field_name, $langcode, $form_state);
-
- if (!empty($field_state['errors'])) {
- // Locate the correct element in the form.
- $element = NestedArray::getValue($form_state['complete_form'], $field_state['array_parents']);
- // Only set errors if the element is accessible.
- if (!isset($element['#access']) || $element['#access']) {
- $definition = $this->getDefinition();
- $is_multiple = $definition['multiple_values'];
- $function = $definition['module'] . '_field_widget_error';
- $function_exists = function_exists($function);
-
- foreach ($field_state['errors'] as $delta => $delta_errors) {
- // For multiple single-value widgets, pass errors by delta.
- // For a multiple-value widget, pass all errors to the main widget.
- $error_element = $is_multiple ? $element : $element[$delta];
- foreach ($delta_errors as $error) {
- if ($function_exists) {
- $function($error_element, $error, $form, $form_state);
- }
- else {
- // Make sure that errors are reported (even incorrectly flagged) if
- // the widget module fails to implement hook_field_widget_error().
- form_error($error_element, $error['message']);
- }
- }
- }
- // Reinitialize the errors list for the next submit.
- $field_state['errors'] = array();
- field_form_set_state($form['#parents'], $field_name, $langcode, $form_state, $field_state);
- }
- }
- }
-
-}
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index c288750..2e91a31 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -193,7 +193,7 @@ public function query($use_groupby = FALSE) {
$this->view->display_handler->options['field_langcode']);
$placeholder = $this->placeholder();
$langcode_fallback_candidates = array($langcode);
- if (variable_get('locale_field_language_fallback', TRUE)) {
+ if (field_language_fallback_enabled()) {
require_once DRUPAL_ROOT . '/includes/language.inc';
$langcode_fallback_candidates = array_merge($langcode_fallback_candidates, language_fallback_get_candidates());
}
diff --git a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
index a0afa3f..9ee6019 100644
--- a/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
+++ b/core/modules/field/lib/Drupal/field/Tests/CrudTest.php
@@ -63,7 +63,7 @@ function testCreateField() {
$this->assertEqual($field_config['settings'], $field_type['settings'], 'Default field settings have been written.');
// Ensure that default storage was set.
- $this->assertEqual($field_config['storage']['type'], variable_get('field_storage_default'), 'The field type is properly saved.');
+ $this->assertEqual($field_config['storage']['type'], config('field.settings')->get('default_storage'), 'The field type is properly saved.');
// Guarantee that the name is unique.
try {
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldTestBase.php
index a34586e..3944639 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldTestBase.php
@@ -14,17 +14,6 @@
* Parent class for Field API tests.
*/
abstract class FieldTestBase extends WebTestBase {
- var $default_storage = 'field_sql_storage';
-
- /**
- * Set the default field storage backend for fields created during tests.
- */
- function setUp() {
- parent::setUp();
-
- // Set default storage backend.
- variable_set('field_storage_default', $this->default_storage);
- }
/**
* Generate random values for a field_test field.
diff --git a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
index 3e1bf38..223e0ac 100644
--- a/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
+++ b/core/modules/field/lib/Drupal/field/Tests/FieldUnitTestBase.php
@@ -14,7 +14,6 @@
* Parent class for Field API unit tests.
*/
abstract class FieldUnitTestBase extends DrupalUnitTestBase {
- var $default_storage = 'field_sql_storage';
/**
* Modules to enable.
@@ -40,7 +39,7 @@ function setUp() {
$this->installSchema('field_test', array('test_entity', 'test_entity_revision', 'test_entity_bundle'));
// Set default storage backend.
- variable_set('field_storage_default', $this->default_storage);
+ $this->installConfig(array('field'));
}
/**
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php
index 6f6f03e..7f524d6 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleKeyTestEntity.php
@@ -17,9 +17,11 @@
* id = "test_entity_bundle_key",
* label = @Translation("Test Entity with a bundle key"),
* module = "field_test",
- * controller_class = "Drupal\field_test\TestEntityController",
- * form_controller_class = {
- * "default" = "Drupal\field_test\TestEntityFormController"
+ * controllers = {
+ * "storage" = "Drupal\field_test\TestEntityController",
+ * "form" = {
+ * "default" = "Drupal\field_test\TestEntityFormController"
+ * }
* },
* field_cache = FALSE,
* base_table = "test_entity_bundle_key",
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php
index 1b39900..7875bc4 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/BundleTestEntity.php
@@ -17,9 +17,11 @@
* id = "test_entity_bundle",
* label = @Translation("Test Entity with a specified bundle"),
* module = "field_test",
- * controller_class = "Drupal\field_test\TestEntityController",
- * form_controller_class = {
- * "default" = "Drupal\field_test\TestEntityFormController"
+ * controllers = {
+ * "storage" = "Drupal\field_test\TestEntityController",
+ * "form" = {
+ * "default" = "Drupal\field_test\TestEntityFormController"
+ * }
* },
* field_cache = FALSE,
* base_table = "test_entity_bundle",
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php
index 79e82ff..976690a 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/CacheableTestEntity.php
@@ -17,7 +17,9 @@
* id = "test_cacheable_entity",
* label = @Translation("Test Entity, cacheable"),
* module = "field_test",
- * controller_class = "Drupal\field_test\TestEntityController",
+ * controllers = {
+ * "storage" = "Drupal\field_test\TestEntityController"
+ * },
* field_cache = TRUE,
* base_table = "test_entity",
* revision_table = "test_entity_revision",
diff --git a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php
index 16f5921..112ba6c 100644
--- a/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php
+++ b/core/modules/field/tests/modules/field_test/lib/Drupal/field_test/Plugin/Core/Entity/TestEntity.php
@@ -18,10 +18,12 @@
* id = "test_entity",
* label = @Translation("Test Entity"),
* module = "field_test",
- * controller_class = "Drupal\field_test\TestEntityController",
- * render_controller_class = "Drupal\Core\Entity\EntityRenderController",
- * form_controller_class = {
- * "default" = "Drupal\field_test\TestEntityFormController"
+ * controllers = {
+ * "storage" = "Drupal\field_test\TestEntityController",
+ * "render" = "Drupal\Core\Entity\EntityRenderController",
+ * "form" = {
+ * "default" = "Drupal\field_test\TestEntityFormController"
+ * }
* },
* field_cache = FALSE,
* base_table = "test_entity",
diff --git a/core/modules/field/tests/modules/field_test_config/config/field.instance.test_entity.test_bundle.field_test_import.yml b/core/modules/field/tests/modules/field_test_config/config/field.instance.test_entity.test_bundle.field_test_import.yml
index dfb0987..4817a77 100644
--- a/core/modules/field/tests/modules/field_test_config/config/field.instance.test_entity.test_bundle.field_test_import.yml
+++ b/core/modules/field/tests/modules/field_test_config/config/field.instance.test_entity.test_bundle.field_test_import.yml
@@ -17,3 +17,4 @@ widget:
module: text
settings:
size: '60'
+field_type: text
diff --git a/core/modules/field/tests/modules/field_test_config/staging/field.instance.test_entity.test_bundle.field_test_import_staging.yml b/core/modules/field/tests/modules/field_test_config/staging/field.instance.test_entity.test_bundle.field_test_import_staging.yml
index 5745277..2e4229d 100644
--- a/core/modules/field/tests/modules/field_test_config/staging/field.instance.test_entity.test_bundle.field_test_import_staging.yml
+++ b/core/modules/field/tests/modules/field_test_config/staging/field.instance.test_entity.test_bundle.field_test_import_staging.yml
@@ -18,3 +18,4 @@ widget:
settings:
size: '60'
module: text
+field_type: text
diff --git a/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml b/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml
new file mode 100644
index 0000000..9a68dd9
--- /dev/null
+++ b/core/modules/field_sql_storage/config/schema/field_sql_storage.schema.yml
@@ -0,0 +1,7 @@
+# Schema for configuration files of the Field SQL Storage module.
+
+field_storage.field_sql_storage.settings:
+ type: sequence
+ label: 'Settings'
+ sequence:
+ - type: string
diff --git a/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php b/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
index 9a99208..6e10b39 100644
--- a/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
+++ b/core/modules/file/lib/Drupal/file/Plugin/Core/Entity/File.php
@@ -19,8 +19,10 @@
* id = "file",
* label = @Translation("File"),
* module = "file",
- * controller_class = "Drupal\file\FileStorageController",
- * render_controller_class = "Drupal\Core\Entity\EntityRenderController",
+ * controllers = {
+ * "storage" = "Drupal\file\FileStorageController",
+ * "render" = "Drupal\Core\Entity\EntityRenderController"
+ * },
* base_table = "file_managed",
* entity_keys = {
* "id" = "fid",
diff --git a/core/modules/filter/filter.install b/core/modules/filter/filter.install
index 536d4f6..45955ad 100644
--- a/core/modules/filter/filter.install
+++ b/core/modules/filter/filter.install
@@ -8,16 +8,6 @@
use Drupal\Component\Uuid\Uuid;
/**
- * Implements hook_schema().
- */
-function filter_schema() {
- $schema['cache_filter'] = drupal_get_schema_unprocessed('system', 'cache');
- $schema['cache_filter']['description'] = 'Cache table for the Filter module to store already filtered pieces of text, identified by text format and hash of the text.';
-
- return $schema;
-}
-
-/**
* @addtogroup updates-7.x-to-8.x
* @{
*/
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
index 1a0d531..202c0fe 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Core/Entity/FilterFormat.php
@@ -18,7 +18,9 @@
* id = "filter_format",
* label = @Translation("Text format"),
* module = "filter",
- * controller_class = "Drupal\filter\FilterFormatStorageController",
+ * controllers = {
+ * "storage" = "Drupal\filter\FilterFormatStorageController"
+ * },
* config_prefix = "filter.format",
* entity_keys = {
* "id" = "format",
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
index 4d28fa6..81d5321 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAdminTest.php
@@ -143,8 +143,7 @@ function testFilterAdmin() {
$this->drupalGet('admin/config/content/formats/' . $basic);
$this->assertFieldByName('filters[filter_html][settings][allowed_html]', $edit['filters[filter_html][settings][allowed_html]'], 'Allowed HTML tag added.');
- $result = db_query('SELECT * FROM {cache_filter}')->fetchObject();
- $this->assertFalse($result, 'Cache cleared.');
+ $this->assertTrue(cache('filter')->isEmpty(), 'Cache cleared.');
$elements = $this->xpath('//select[@name=:first]/following::select[@name=:second]', array(
':first' => 'filters[' . $first_filter . '][weight]',
diff --git a/core/modules/forum/forum.admin.inc b/core/modules/forum/forum.admin.inc
index 001dd97..8ac5f22 100644
--- a/core/modules/forum/forum.admin.inc
+++ b/core/modules/forum/forum.admin.inc
@@ -5,6 +5,8 @@
* Administrative page callbacks for the Forum module.
*/
+use Drupal\taxonomy\Form\OverviewTerms;
+
/**
* Page callback: Returns a form for creating a new forum or container.
*
@@ -235,7 +237,9 @@ function forum_overview($form, &$form_state) {
$vid = $config->get('vocabulary');
$vocabulary = taxonomy_vocabulary_load($vid);
- $form = taxonomy_overview_terms($form, $form_state, $vocabulary);
+ // @todo temporary, will be fixed in http://drupal.org/node/1974210.
+ $overview = OverviewTerms::create(Drupal::getContainer());
+ $form = $overview->buildForm($form, $form_state, $vocabulary);
foreach (element_children($form['terms']) as $key) {
if (isset($form['terms'][$key]['#term'])) {
@@ -263,11 +267,13 @@ function forum_overview($form, &$form_state) {
unset($form['actions']['reset_alphabetical']);
// The form needs to have submit and validate handlers set explicitly.
- $form['#submit'] = array('taxonomy_overview_terms_submit'); // Use the existing taxonomy overview submit handler.
+ // Use the existing taxonomy overview submit handler.
+ $form['#submit'] = array(array($overview, 'submitForm'));
$form['terms']['#empty'] = t('No containers or forums available. Add container or Add forum.', array('@container' => url('admin/structure/forum/add/container'), '@forum' => url('admin/structure/forum/add/forum')));
return $form;
}
+
/**
* Returns a select box for available parent terms.
*
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
index 8391d0e..b40bffa 100644
--- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
@@ -62,6 +62,7 @@ function setUp() {
$this->installSchema('user', array('users'));
$this->installSchema('language', array('language'));
$this->installSchema('entity_test', array('entity_test'));
+ $this->installConfig(array('field'));
// Add English as a language.
$english = new Language(array(
diff --git a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
index 957c009..0678407 100644
--- a/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
+++ b/core/modules/image/lib/Drupal/image/Plugin/Core/Entity/ImageStyle.php
@@ -18,7 +18,9 @@
* id = "image_style",
* label = @Translation("Image style"),
* module = "image",
- * controller_class = "Drupal\image\ImageStyleStorageController",
+ * controllers = {
+ * "storage" = "Drupal\image\ImageStyleStorageController"
+ * },
* uri_callback = "image_style_entity_uri",
* config_prefix = "image.style",
* entity_keys = {
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
index c4c199a..adaf260 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/Display.php
@@ -21,7 +21,9 @@
* id = "display",
* label = @Translation("Display"),
* module = "layout",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "display.bound",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
index cb13887..d2f4880 100644
--- a/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
+++ b/core/modules/layout/lib/Drupal/layout/Plugin/Core/Entity/UnboundDisplay.php
@@ -24,7 +24,9 @@
* id = "unbound_display",
* label = @Translation("Unbound Display"),
* module = "layout",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "display.unbound",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/locale/lib/Drupal/locale/Locale.php b/core/modules/locale/lib/Drupal/locale/Locale.php
new file mode 100644
index 0000000..f4f41c1
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Locale.php
@@ -0,0 +1,30 @@
+installStorage = $installStorage;
+ $this->localeStorage = $localeStorage ?: locale_storage();
+ }
+
+ /**
+ * Gets locale wrapper with typed configuration data.
+ *
+ * @param string $name
+ * Configuration object name.
+ *
+ * @return \Drupal\locale\LocaleTypedConfig
+ * Locale-wrapped configuration element.
+ */
+ public function get($name) {
+ // Read default and current configuration data.
+ $default = $this->installStorage->read($name);
+ $updated = $this->configStorage->read($name);
+ // We get only the data that didn't change from default.
+ $data = $this->compareConfigData($default, $updated);
+ $definition = $this->getDefinition($name);
+ // Unless the configuration has a explicit language code we assume English.
+ $langcode = isset($default['langcode']) ? $default['langcode'] : 'en';
+ $wrapper = new LocaleTypedConfig($definition, $name, $langcode, $this);
+ $wrapper->setValue($data);
+ return $wrapper;
+ }
+
+ /**
+ * Compares default configuration with updated data.
+ *
+ * @param array $default
+ * Default configuration data.
+ * @param array|false $updated
+ * Current configuration data, or FALSE if no configuration data existed.
+ *
+ * @return array
+ * The elements of default configuration that haven't changed.
+ */
+ protected function compareConfigData(array $default, $updated) {
+ // Speed up comparison, specially for install operations.
+ if ($default === $updated) {
+ return $default;
+ }
+ $result = array();
+ foreach ($default as $key => $value) {
+ if (isset($updated[$key])) {
+ if (is_array($value)) {
+ $result[$key] = $this->compareConfigData($value, $updated[$key]);
+ }
+ elseif ($value === $updated[$key]) {
+ $result[$key] = $value;
+ }
+ }
+ }
+ return $result;
+ }
+
+ /**
+ * Saves translated configuration data.
+ *
+ * @param string $name
+ * Configuration object name.
+ * @param string $langcode
+ * Language code.
+ * @param array $data
+ * Configuration data to be saved, that will be only the translated values.
+ */
+ public function saveTranslationData($name, $langcode, array $data) {
+ $locale_name = self::localeConfigName($langcode, $name);
+ $this->configStorage->write($locale_name, $data);
+ }
+
+ /**
+ * Deletes translated configuration data.
+ *
+ * @param string $name
+ * Configuration object name.
+ * @param string $langcode
+ * Language code.
+ */
+ public function deleteTranslationData($name, $langcode) {
+ $locale_name = self::localeConfigName($langcode, $name);
+ $this->configStorage->delete($locale_name);
+ }
+
+ /**
+ * Gets configuration names associated with components.
+ *
+ * @param array $components
+ * (optional) Array of component lists indexed by type. If not present or it
+ * is an empty array, it will update all components.
+ *
+ * @return array
+ * Array of configuration object names.
+ */
+ public function getComponentNames(array $components) {
+ $components = array_filter($components);
+ if ($components) {
+ $names = array();
+ foreach ($components as $type => $list) {
+ // InstallStorage::getComponentNames returns a list of folders keyed by
+ // config name.
+ $names = array_merge($names, array_keys($this->installStorage->getComponentNames($type, $list)));
+ }
+ return $names;
+ }
+ else {
+ return $this->installStorage->listAll();
+ }
+ }
+
+ /**
+ * Deletes configuration translations for uninstalled components.
+ *
+ * @param array $components
+ * Array with string identifiers.
+ * @param array $langcodes
+ * Array of language codes.
+ */
+ public function deleteComponentTranslations(array $components, array $langcodes) {
+ $names = $this->getComponentNames($components);
+ if ($names && $langcodes) {
+ foreach ($names as $name) {
+ foreach ($langcodes as $langcode) {
+ $this->deleteTranslationData($name, $langcode);
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets configuration names associated with strings.
+ *
+ * @param array $lids
+ * Array with string identifiers.
+ *
+ * @return array
+ * Array of configuration object names.
+ */
+ public function getStringNames(array $lids) {
+ $names = array();
+ $locations = $this->localeStorage->getLocations(array('sid' => $lids, 'type' => 'configuration'));
+ foreach ($locations as $location) {
+ $names[$location->name] = $location->name;
+ }
+ return $names;
+ }
+
+ /**
+ * Deletes configuration for language.
+ *
+ * @param string $langcode
+ * Language code to delete.
+ */
+ public function deleteLanguageTranslations($langcode) {
+ $locale_name = self::localeConfigName($langcode);
+ foreach ($this->configStorage->listAll($locale_name) as $name) {
+ $this->configStorage->delete($name);
+ }
+ }
+
+ /**
+ * Translates string using the localization system.
+ *
+ * So far we only know how to translate strings from English so the source
+ * string should be in English.
+ * Unlike regular t() translations, strings will be added to the source
+ * tables only if this is marked as default data.
+ *
+ * @param string $name
+ * Name of the configuration location.
+ * @param string $langcode
+ * Language code to translate to.
+ * @param string $source
+ * The source string, should be English.
+ * @param string $context
+ * The string context.
+ *
+ * @return string|false
+ * Translated string if there is a translation, FALSE if not.
+ */
+ public function translateString($name, $langcode, $source, $context) {
+ if ($source) {
+ // If translations for a language have not been loaded yet.
+ if (!isset($this->translations[$name][$langcode])) {
+ // Preload all translations for this configuration name and language.
+ $this->translations[$name][$langcode] = array();
+ foreach ($this->localeStorage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => $name)) as $string){
+ $this->translations[$name][$langcode][$string->context][$string->source] = $string;
+ }
+ }
+ if (!isset($this->translations[$name][$langcode][$context][$source])) {
+ // There is no translation of the source string in this config location
+ // to this language for this context.
+ if ($translation = $this->localeStorage->findTranslation(array('source' => $source, 'context' => $context, 'language' => $langcode))) {
+ // Look for a translation of the string. It might have one, but not
+ // be saved in this configuration location yet.
+ // If the string has a translation for this context to this language,
+ // save it in the configuration location so it can be looked up faster
+ // next time.
+ $string = $this->localeStorage->createString((array) $translation)
+ ->addLocation('configuration', $name)
+ ->save();
+ }
+ else {
+ // No translation was found. Add the source to the configuration
+ // location so it can be translated, and the string is faster to look
+ // for next time.
+ $translation = $this->localeStorage
+ ->createString(array('source' => $source, 'context' => $context))
+ ->addLocation('configuration', $name)
+ ->save();
+ }
+
+ // Add an entry, either the translation found, or a blank string object
+ // to track the source string, to this configuration location, language,
+ // and context.
+ $this->translations[$name][$langcode][$context][$source] = $translation;
+ }
+
+ // Return the string only when the string object had a translation.
+ if ($this->translations[$name][$langcode][$context][$source]->isTranslation()) {
+ return $this->translations[$name][$langcode][$context][$source]->getString();
+ }
+ }
+ return FALSE;
+ }
+
+ /**
+ * Provides configuration data location for given langcode and name.
+ *
+ * @param string $langcode
+ * The language code.
+ * @param string|NULL $name
+ * Name of the original configuration. Set to NULL to get the name prefix
+ * for all $langcode overrides.
+ *
+ * @return string
+ */
+ public static function localeConfigName($langcode, $name = NULL) {
+ return rtrim('locale.config.' . $langcode . '.' . $name, '.');
+ }
+}
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
new file mode 100644
index 0000000..5d6b200
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php
@@ -0,0 +1,187 @@
+langcode = $langcode;
+ $this->localeConfig = $localeConfig;
+ }
+
+ /**
+ * Gets wrapped typed config object.
+ */
+ public function getTypedConfig() {
+ return $this->localeConfig->create($this->definition, $this->value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getTranslation($langcode) {
+ $options = array(
+ 'source' => $this->langcode,
+ 'target' => $langcode,
+ );
+ $data = $this->getElementTranslation($this->getTypedConfig(), $options);
+ return $this->localeConfig->create($this->definition, $data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function language() {
+ return language_load($this->langcode);
+ }
+
+ /**
+ * Checks whether we can translate these languages.
+ *
+ * @param string $from_langcode
+ * Source language code.
+ * @param string $to_langcode
+ * Destination language code.
+ *
+ * @return bool
+ * TRUE if this translator supports translations for these languages.
+ */
+ protected function canTranslate($from_langcode, $to_langcode) {
+ if ($from_langcode == 'en') {
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ /**
+ * Gets translated configuration data for a typed configuration element.
+ *
+ * @param mixed $element
+ * Typed configuration element, either \Drupal\Core\Config\Schema\Element or
+ * \Drupal\Core\Config\Schema\ArrayElement.
+ * @param array $options
+ * Array with translation options that must contain the keys defined in
+ * \Drupal\locale\LocaleTypedConfig::translateElement()
+ *
+ * @return array
+ * Configuration data translated to the requested language if available,
+ * an empty array otherwise.
+ */
+ protected function getElementTranslation($element, array $options) {
+ $translation = array();
+ if ($element instanceof ArrayElement) {
+ $translation = $this->getArrayTranslation($element, $options);
+ }
+ elseif ($this->translateElement($element, $options)) {
+ $translation = $element->getValue();
+ }
+ return $translation;
+ }
+
+ /**
+ * Gets translated configuration data for an element of type ArrayElement.
+ *
+ * @param \Drupal\Core\Config\Schema\ArrayElement $element
+ * Typed configuration array element.
+ * @param array $options
+ * Array with translation options that must contain the keys defined in
+ * \Drupal\locale\LocaleTypedConfig::translateElement()
+ *
+ * @return array
+ * Configuration data translated to the requested language.
+ */
+ protected function getArrayTranslation(ArrayElement $element, array $options) {
+ $translation = array();
+ foreach ($element as $key => $property) {
+ $value = $this->getElementTranslation($property, $options);
+ if (!empty($value)) {
+ $translation[$key] = $value;
+ }
+ }
+ return $translation;
+ }
+
+ /**
+ * Translates element's value if it fits our translation criteria.
+ *
+ * For an element to be translatable by locale module it needs to be of base
+ * type 'string' and have 'translatable = TRUE' in the element's definition.
+ * Translatable elements may use these additional keys in their data
+ * definition:
+ * - 'translatable', FALSE to opt out of translation.
+ * - 'locale context', to define the string context.
+ *
+ * @param \Drupal\Core\TypedData\TypedDataInterface $element
+ * Configuration element.
+ * @param array $options
+ * Array with translation options that must contain the following keys:
+ * - 'source', Source language code.
+ * - 'target', Target language code.
+ *
+ * @return bool
+ * Whether the element fits the translation criteria.
+ */
+ protected function translateElement(\Drupal\Core\TypedData\TypedDataInterface $element, array $options) {
+ if ($this->canTranslate($options['source'], $options['target'])) {
+ $definition = $element->getDefinition();
+ $value = $element->getValue();
+ if ($value && !empty($definition['translatable'])) {
+ $context = isset($definition['locale context']) ? $definition['locale context'] : '';
+ if ($translation = $this->localeConfig->translateString($this->name, $options['target'], $value, $context)) {
+ $element->setValue($translation);
+ return TRUE;
+ }
+ }
+ }
+ // The element does not have a translation.
+ return FALSE;
+ }
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
index 618b419..126ee04 100644
--- a/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
+++ b/core/modules/locale/lib/Drupal/locale/PoDatabaseWriter.php
@@ -102,6 +102,7 @@ function setReport($report = array()) {
'updates' => 0,
'deletes' => 0,
'skips' => 0,
+ 'strings' => array(),
);
$this->_report = $report;
}
@@ -259,6 +260,7 @@ private function importString(PoItem $item) {
$string->save();
$this->_report['updates']++;
}
+ $this->_report['strings'][] = $string->getId();
return $string->lid;
}
else {
@@ -273,6 +275,7 @@ private function importString(PoItem $item) {
))->save();
$this->_report['additions']++;
+ $this->_report['strings'][] = $string->getId();
return $string->lid;
}
}
@@ -280,6 +283,7 @@ private function importString(PoItem $item) {
// Empty translation, remove existing if instructed.
$string->delete();
$this->_report['deletes']++;
+ $this->_report['strings'][] = $string->lid;
return $string->lid;
}
}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php
new file mode 100644
index 0000000..f17404a
--- /dev/null
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php
@@ -0,0 +1,150 @@
+ 'Configuration translation',
+ 'description' => 'Tests translation of configuration strings.',
+ 'group' => 'Locale',
+ );
+ }
+
+ public function setUp() {
+ parent::setUp();
+ // Add a default locale storage for all these tests.
+ $this->storage = locale_storage();
+ }
+
+ /**
+ * Tests basic configuration translation.
+ */
+ function testConfigTranslation() {
+ // Add custom language.
+ $langcode = 'xx';
+ $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface', 'administer modules'));
+ $this->drupalLogin($admin_user);
+ $name = $this->randomName(16);
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $name,
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+ $language = new Language(array('langcode' => $langcode));
+ // Set path prefix.
+ $edit = array( "prefix[$langcode]" => $langcode );
+ $this->drupalPost('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
+
+ // Check site name string exists and create translation for it.
+ $string = $this->storage->findString(array('source' => 'Drupal', 'context' => '', 'type' => 'configuration'));
+ $this->assertTrue($string, 'Configuration strings have been created upon installation.');
+
+ // Translate using the UI so configuration is refreshed.
+ $site_name = $this->randomName(20);
+ $search = array(
+ 'string' => $string->source,
+ 'langcode' => $langcode,
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $textareas = $this->xpath('//textarea');
+ $textarea = current($textareas);
+ $lid = (string) $textarea[0]['name'];
+ $edit = array(
+ $lid => $site_name,
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
+
+ $wrapper = $this->container->get('locale.config.typed')->get('system.site');
+
+ // Get translation and check we've only got the site name.
+ $translation = $wrapper->getTranslation($langcode);
+ $properties = $translation->getProperties();
+ $this->assertEqual(count($properties), 1, 'Got the right number of properties after translation');
+ $this->assertEqual($properties['name']->getValue(), $site_name, 'Got the right translation for site name after translation');
+
+ // Check the translated site name is displayed.
+ $this->drupalGet($langcode);
+ $this->assertText($site_name, 'The translated site name is displayed after translations refreshed.');
+
+ // Assert strings from image module config are not available.
+ $string = $this->storage->findString(array('source' => 'Medium (220x220)', 'context' => '', 'type' => 'configuration'));
+ $this->assertFalse($string, 'Configuration strings have been created upon installation.');
+
+ // Enable the image module.
+ $this->drupalPost('admin/modules', array('modules[Core][image][enable]' => "1"), t('Save configuration'));
+ $this->resetAll();
+
+ $string = $this->storage->findString(array('source' => 'Medium (220x220)', 'context' => '', 'type' => 'configuration'));
+ $this->assertTrue($string, 'Configuration strings have been created upon installation.');
+ $locations = $string->getLocations();
+ $this->assertTrue(isset($locations['configuration']) && isset($locations['configuration']['image.style.medium']), 'Configuration string has been created with the right location');
+
+ // Check the string is unique and has no translation yet.
+ $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium'));
+ $translation = reset($translations);
+ $this->assertTrue(count($translations) == 1 && $translation->source == $string->source && empty($translation->translation), 'Got only one string for image configuration and has no translation.');
+
+ // Translate using the UI so configuration is refreshed.
+ $image_style_label = $this->randomName(20);
+ $search = array(
+ 'string' => $string->source,
+ 'langcode' => $langcode,
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $textarea = current($this->xpath('//textarea'));
+ $lid = (string) $textarea[0]['name'];
+ $edit = array(
+ $lid => $image_style_label,
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations'));
+
+ // Check the right single translation has been created.
+ $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium'));
+ $translation = reset($translations);
+ $this->assertTrue(count($translations) == 1 && $translation->source == $string->source && $translation->translation == $image_style_label, 'Got only one translation for image configuration.');
+
+ // Try more complex configuration data.
+ $wrapper = $this->container->get('locale.config.typed')->get('image.style.medium');
+
+ $translation = $wrapper->getTranslation($langcode);
+ $property = $translation->get('label');
+ $this->assertEqual($property->getValue(), $image_style_label, 'Got the right translation for image style name after translation');
+
+ // Quick test to ensure translation file exists.
+ $this->assertEqual(config('locale.config.xx.image.style.medium')->get('label'), $image_style_label);
+
+ // Disable and uninstall the module.
+ $this->drupalPost('admin/modules', array('modules[Core][image][enable]' => FALSE), t('Save configuration'));
+ $this->drupalPost('admin/modules/uninstall', array('uninstall[image]' => "image"), t('Uninstall'));
+ $this->drupalPost(NULL, array(), t('Uninstall'));
+
+ // Ensure that the translated configuration has been removed.
+ $this->assertFalse(config('locale.config.xx.image.style.medium')->get('label'), 'Translated configuration for image module removed.');
+ }
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
index 06269ab..0392c0f 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php
@@ -238,6 +238,71 @@ function testEmptyMsgstr() {
}
/**
+ * Tests .po file import with configuration translation.
+ */
+ function testConfigPoFile() {
+ // Values for translations to assert. Config key, original string,
+ // translation and config property name.
+ $config_strings = array(
+ 'system.maintenance' => array(
+ '@site is currently under maintenance. We should be back shortly. Thank you for your patience.',
+ '@site karbantartás alatt áll. Rövidesen visszatérünk. Köszönjük a türelmet.',
+ 'message',
+ ),
+ 'user.role.anonymous' => array(
+ 'Anonymous user',
+ 'Névtelen felhasználó',
+ 'label',
+ ),
+ );
+
+ // Add custom language for testing.
+ $langcode = 'xx';
+ $edit = array(
+ 'predefined_langcode' => 'custom',
+ 'langcode' => $langcode,
+ 'name' => $this->randomName(16),
+ 'direction' => '0',
+ );
+ $this->drupalPost('admin/config/regional/language/add', $edit, t('Add custom language'));
+
+ // Check for the source strings we are going to translate. Adding the
+ // custom language should have made the process to export configuration
+ // strings to interface translation executed.
+ $locale_storage = locale_storage();
+ foreach ($config_strings as $config_string) {
+ $string = $locale_storage->findString(array('source' => $config_string[0], 'context' => '', 'type' => 'configuration'));
+ $this->assertTrue($string, 'Configuration strings have been created upon installation.');
+ }
+
+ // Import a .po file to translate.
+ $this->importPoFile($this->getPoFileWithConfig(), array(
+ 'langcode' => $langcode,
+ ));
+
+ // Translations got recorded in the interface translation system.
+ foreach ($config_strings as $config_string) {
+ $search = array(
+ 'string' => $config_string[0],
+ 'langcode' => $langcode,
+ 'translation' => 'all',
+ );
+ $this->drupalPost('admin/config/regional/translate/translate', $search, t('Filter'));
+ $this->assertText($config_string[1], format_string('Translation of @string found.', array('@string' => $config_string[0])));
+ }
+
+ $locale_config = $this->container->get('locale.config.typed');
+ // Translations got recorded in the config system.
+ foreach ($config_strings as $config_key => $config_string) {
+ $wrapper = $locale_config->get($config_key);
+ $translation = $wrapper->getTranslation($langcode);
+ $properties = $translation->getProperties();
+ $this->assertEqual(count($properties), 1, 'Got the right number of properties with strict translation');
+ $this->assertEqual($properties[$config_string[2]]->getValue(), $config_string[1]);
+ }
+ }
+
+ /**
* Helper function: import a standalone .po file in a given language.
*
* @param $contents
@@ -449,6 +514,7 @@ function getPoFileWithEmptyMsgstr() {
EOF;
}
+
/**
* Helper function that returns a .po file with an empty last item.
*/
@@ -471,4 +537,26 @@ function getPoFileWithMsgstr() {
EOF;
}
+ /**
+ * Helper function that returns a .po file with configuration translations.
+ */
+ function getPoFileWithConfig() {
+ return <<< EOF
+msgid ""
+msgstr ""
+"Project-Id-Version: Drupal 8\\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\n"
+"Content-Transfer-Encoding: 8bit\\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
+
+msgid "@site is currently under maintenance. We should be back shortly. Thank you for your patience."
+msgstr "@site karbantartás alatt áll. Rövidesen visszatérünk. Köszönjük a türelmet."
+
+msgid "Anonymous user"
+msgstr "Névtelen felhasználó"
+
+EOF;
+ }
+
}
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 49273d8..5173132 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -84,7 +84,7 @@ function locale_translate_import_form($form, &$form_state) {
'#type' => 'checkbox',
'#states' => array(
'checked' => array(
- ':input[name="customized"]' => array('checked' => TRUE),
+ ':input[name="customized"]' => array('checked' => TRUE),
),
),
);
@@ -181,7 +181,7 @@ function locale_translate_export_form($form, &$form_state) {
'#tree' => TRUE,
'#states' => array(
'invisible' => array(
- ':input[name="langcode"]' => array('value' => LANGUAGE_SYSTEM),
+ ':input[name="langcode"]' => array('value' => LANGUAGE_SYSTEM),
),
),
);
@@ -400,6 +400,9 @@ function locale_translate_batch_build($files, $options) {
// Save the translation status of all files.
$operations[] = array('locale_translate_batch_import_save', array());
+ // Add a final step to refresh JavaScript and configuration strings.
+ $operations[] = array('locale_translate_batch_refresh', array());
+
$batch = array(
'operations' => $operations,
'title' => $t('Importing interface translations'),
@@ -537,20 +540,74 @@ function locale_translate_batch_import_save($context) {
}
/**
+ * Refreshs translations after importing strings.
+ *
+ * @param array $context
+ * Contains a list of strings updated and information about the progress.
+ */
+function locale_translate_batch_refresh(array &$context) {
+ if (!isset($context['sandbox']['refresh'])) {
+ $strings = $langcodes = array();
+ if (isset($context['results']['stats'])) {
+ // Get list of unique string identifiers and language codes updated.
+ $langcodes = array_unique(array_values($context['results']['languages']));
+ foreach ($context['results']['stats'] as $filepath => $report) {
+ $strings = array_merge($strings, $report['strings']);
+ }
+ }
+ if ($strings) {
+ // Initialize multi-step string refresh.
+ $context['message'] = t('Updating translations for JavaScript and configuration strings.');
+ $context['sandbox']['refresh']['strings'] = array_unique($strings);
+ $context['sandbox']['refresh']['languages'] = $langcodes;
+ $context['sandbox']['refresh']['names'] = array();
+ $context['results']['stats']['config'] = 0;
+ $context['sandbox']['refresh']['count'] = count($strings);
+
+ // We will update strings on later steps.
+ $context['finished'] = 1 - 1 / $context['sandbox']['refresh']['count'];
+ }
+ else {
+ $context['finished'] = 1;
+ }
+ }
+ elseif ($name = array_shift($context['sandbox']['refresh']['names'])) {
+ // Refresh all languages for one object at a time.
+ $count = locale_config_update_multiple(array($name), $context['sandbox']['refresh']['languages']);
+ $context['results']['stats']['config'] += $count;
+ }
+ elseif (!empty($context['sandbox']['refresh']['strings'])) {
+ // Not perfect but will give some indication of progress.
+ $context['finished'] = 1 - count($context['sandbox']['refresh']['strings']) / $context['sandbox']['refresh']['count'];
+ // Pending strings, refresh 100 at a time, get next pack.
+ $next = array_slice($context['sandbox']['refresh']['strings'], 0, 100);
+ array_splice($context['sandbox']['refresh']['strings'], 0, count($next));
+ // Clear cache and force refresh of JavaScript translations.
+ _locale_refresh_translations($context['sandbox']['refresh']['languages'], $next);
+ // Check whether we need to refresh configuration objects.
+ if ($names = \Drupal\locale\Locale::config()->getStringNames($next)) {
+ $context['sandbox']['refresh']['names'] = $names;
+ }
+ }
+ else {
+ $context['finished'] = 1;
+ }
+}
+
+/**
* Finished callback of system page locale import batch.
*/
function locale_translate_batch_finished($success, $results) {
if ($success) {
- $additions = $updates = $deletes = $skips = 0;
- $langcodes = array();
+ $additions = $updates = $deletes = $skips = $config = 0;
if (isset($results['failed_files'])) {
- if (module_exists('dblog')) {
- $message = format_plural(count($results['failed_files']), 'One translation file could not be imported. See the log for details.', '@count translation files could not be imported. See the log for details.', array('@url' => url('admin/reports/dblog')));
- }
- else {
- $message = format_plural(count($results['failed_files']), 'One translation files could not be imported. See the log for details.', '@count translation files could not be imported. See the log for details.');
- }
- drupal_set_message($message, 'error');
+ if (Drupal::moduleHandler()->moduleExists('dblog')) {
+ $message = format_plural(count($results['failed_files']), 'One translation file could not be imported. See the log for details.', '@count translation files could not be imported. See the log for details.', array('@url' => url('admin/reports/dblog')));
+ }
+ else {
+ $message = format_plural(count($results['failed_files']), 'One translation files could not be imported. See the log for details.', '@count translation files could not be imported. See the log for details.');
+ }
+ drupal_set_message($message, 'error');
}
if (isset($results['files'])) {
$skipped_files = array();
@@ -566,8 +623,6 @@ function locale_translate_batch_finished($success, $results) {
$skipped_files[] = $filepath;
}
}
- // Get list of unique language codes updated.
- $langcodes = array_unique(array_values($results['languages']));
}
drupal_set_message(format_plural(count($results['files']),
'One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.',
@@ -586,11 +641,12 @@ function locale_translate_batch_finished($success, $results) {
drupal_set_message($message, 'warning');
watchdog('locale', '@count disallowed HTML string(s) in files: @files.', array('@count' => $skips, '@files' => implode(',', $skipped_files)), WATCHDOG_WARNING);
}
-
- // Clear cache and force refresh of JavaScript translations.
- _locale_refresh_translations($langcodes);
}
}
+ // Add messages for configuration too.
+ if (isset($results['stats']['config'])) {
+ locale_config_batch_finished($success, $results);
+ }
}
/**
@@ -611,7 +667,7 @@ function locale_translate_file_create($filepath) {
}
/**
- * Generate file properties from filename and options.
+ * Generates file properties from filename and options.
*
* An attempt is made to determine the translation language, project name and
* project version from the file name. Supported file name patterns are:
@@ -685,3 +741,138 @@ function locale_translate_delete_translation_files($projects = array(), $langcod
}
return !$fail;
}
+
+/**
+ * Builds a locale batch to refresh configuration.
+ *
+ * @param array $options
+ * An array with options that can have the following elements:
+ * - 'finish_feedback': (optional) Whether or not to give feedback to the user
+ * when the batch is finished. Defaults to TRUE.
+ * @param array $langcodes
+ * (optional) Array of language codes. Defaults to all translatable languages.
+ * @param array $components
+ * (optional) Array of component lists indexed by type. If not present or it
+ * is an empty array, it will update all components.
+ *
+ * @return array
+ * The batch definition.
+ */
+function locale_config_batch_update_components(array $options, $langcodes = array(), $components = array()) {
+ $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
+ if ($langcodes && $names = \Drupal\locale\Locale::config()->getComponentNames($components)) {
+ return locale_config_batch_build($names, $langcodes, $options);
+ }
+}
+
+/**
+ * Creates a locale batch to refresh specific configuration.
+ *
+ * @param array $names
+ * List of configuration object names (which are strings) to update.
+ * @param array $langcodes
+ * List of language codes to refresh.
+ * @param array $options
+ * (optional) An array with options that can have the following elements:
+ * - 'finish_feedback': Whether or not to give feedback to the user when the
+ * batch is finished. Defaults to TRUE.
+ *
+ * @return array
+ * The batch definition.
+ *
+ * @see locale_config_batch_refresh_name()
+ */
+function locale_config_batch_build(array $names, array $langcodes, $options = array()) {
+ $options += array('finish_feedback' => TRUE);
+ $t = get_t();
+ foreach ($names as $name) {
+ $operations[] = array('locale_config_batch_refresh_name', array($name, $langcodes));
+ }
+ $batch = array(
+ 'operations' => $operations,
+ 'title' => $t('Updating configuration translations'),
+ 'init_message' => $t('Starting configuration update'),
+ 'error_message' => $t('Error updating configuration translations'),
+ 'file' => drupal_get_path('module', 'locale') . '/locale.bulk.inc',
+ );
+ if (!empty($options['finish_feedback'])) {
+ $batch['completed'] = 'locale_config_batch_finished';
+ }
+ return $batch;
+}
+
+/**
+ * Performs configuration translation refresh as a batch step.
+ *
+ * @param string $name
+ * Name of configuration object to update.
+ * @param array $langcodes
+ * (optional) Array of language codes to update. Defaults to all languages.
+ * @param array $context
+ * Contains a list of files imported.
+ *
+ * @see locale_config_batch_build()
+ */
+function locale_config_batch_refresh_name($name, array $langcodes, array &$context) {
+ if (!isset($context['result']['stats']['config'])) {
+ $context['result']['stats']['config'] = 0;
+ }
+ $context['result']['stats']['config'] += locale_config_update_multiple(array($name), $langcodes);
+ $context['result']['names'][] = $name;
+ $context['result']['langcodes'] = $langcodes;
+ $context['finished'] = 1;
+}
+
+/**
+ * Finishes callback of system page locale import batch.
+ *
+ * @see locale_config_batch_build()
+ *
+ * @param bool $success
+ * Information about the success of the batch import.
+ * @param array $results
+ * Information about the results of the batch import.
+ */
+function locale_config_batch_finished($success, array $results) {
+ if ($success) {
+ $configuration = isset($results['stats']['config']) ? $results['stats']['config'] : 0;
+ if ($configuration) {
+ drupal_set_message(t('The configuration was successfully updated. There are %number configuration objects updated.', array('%number' => $configuration)));
+ watchdog('locale', 'The configuration was successfully updated. %number configuration objects updated.', array('%number' => $configuration));
+ }
+ else {
+ drupal_set_message(t('No configuration objects have been updated.'));
+ watchdog('locale', 'No configuration objects have been updated.', array(), WATCHDOG_WARNING);
+ }
+ }
+}
+
+/**
+ * Updates all configuration for names / languages.
+ *
+ * @param array $names
+ * Array of names of configuration objects to update.
+ * @param array $langcodes
+ * (optional) Array of language codes to update. Defaults to all languages.
+ *
+ * @return int
+ * Number of configuration objects retranslated.
+ */
+function locale_config_update_multiple(array $names, $langcodes = array()) {
+ $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
+ $count = 0;
+ foreach ($names as $name) {
+ $wrapper = \Drupal\locale\Locale::config()->get($name);
+ foreach ($langcodes as $langcode) {
+ $translation = $wrapper->getValue() ? $wrapper->getTranslation($langcode)->getValue() : NULL;
+ if ($translation) {
+ \Drupal\locale\Locale::config()->saveTranslationData($name, $langcode, $translation);
+ $count++;
+ }
+ else {
+ \Drupal\locale\Locale::config()->deleteTranslationData($name, $langcode);
+ }
+ }
+ }
+ return $count;
+}
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 880e335..c2da3b5 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -319,6 +319,9 @@ function locale_language_delete($language) {
module_load_include('inc', 'locale', 'locale.bulk');
locale_translate_delete_translation_files(array(), array($language->langcode));
+ // Remove translated configuration objects.
+ \Drupal\locale\Locale::config()->deleteLanguageTranslations($language->langcode);
+
// Changing the language settings impacts the interface:
_locale_invalidate_js($language->langcode);
cache('page')->deleteAll();
@@ -473,14 +476,16 @@ function locale_get_plural($count, $langcode = NULL) {
* Implements hook_modules_installed().
*/
function locale_modules_installed($modules) {
- locale_system_update($modules);
+ $components['module'] = $modules;
+ locale_system_update($components);
}
/**
* Implements hook_modules_uninstalled().
*/
function locale_modules_uninstalled($modules) {
- locale_system_remove($modules);
+ $components['module'] = $modules;
+ locale_system_remove($components);
}
/**
@@ -490,14 +495,16 @@ function locale_modules_uninstalled($modules) {
* initial installation. The theme system is missing an installation hook.
*/
function locale_themes_enabled($themes) {
- locale_system_update($themes);
+ $components['theme'] = $themes;
+ locale_system_update($components);
}
/**
* Implements hook_themes_disabled().
*/
function locale_themes_disabled($themes) {
- locale_system_remove($themes);
+ $components['theme'] = $themes;
+ locale_system_remove($components);
}
/**
@@ -507,10 +514,14 @@ function locale_themes_disabled($themes) {
* components.
*
* @param array $components
- * An array of component (theme and/or module) names to import
- * translations for.
+ * An array of arrays of component (theme and/or module) names to import
+ * translations for, indexed by type.
*/
-function locale_system_update($components) {
+function locale_system_update(array $components) {
+
+ $components += array('module' => array(), 'theme' => array());
+ $list = array_merge($components['module'], $components['theme']);
+
// Skip running the translation imports if in the installer,
// because it would break out of the installer flow. We have
// built-in support for translation imports in the installer.
@@ -521,11 +532,15 @@ function locale_system_update($components) {
// Only when new projects are added the update batch will be triggered. Not
// each enabled module will introduce a new project. E.g. sub modules.
$projects = array_keys(locale_translation_build_projects());
- if ($components = array_intersect($components, $projects)) {
+ if ($list = array_intersect($list, $projects)) {
module_load_include('fetch.inc', 'locale');
// Get translation status of the projects, download and update translations.
$options = _locale_translation_default_update_options();
- $batch = locale_translation_batch_update_build($components, array(), $options);
+ $batch = locale_translation_batch_update_build($list, array(), $options);
+ batch_set($batch);
+ }
+ Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+ if ($batch = locale_config_batch_update_components(array(), array(), $components)) {
batch_set($batch);
}
}
@@ -539,37 +554,41 @@ function locale_system_update($components) {
* modules and we have no record of which string is used by which module.
*
* @param array $components
- * An array of component (theme and/or module) names to remove
- * translation history.
+ * An array of arrays of component (theme and/or module) names to import
+ * translations for, indexed by type.
*/
function locale_system_remove($components) {
- if (locale_translatable_language_list()) {
+ $components += array('module' => array(), 'theme' => array());
+ $list = array_merge($components['module'], $components['theme']);
+ if ($language_list = locale_translatable_language_list()) {
module_load_include('compare.inc', 'locale');
+ Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+ // Delete configuration translations.
+ \Drupal\locale\Locale::config()->deleteComponentTranslations($components, array_keys($language_list));
// Only when projects are removed, the translation files and records will be
// deleted. Not each disabled module will remove a project. E.g. sub modules.
$projects = array_keys(locale_translation_get_projects());
- if ($components = array_intersect($components, $projects)) {
- locale_translation_file_history_delete($components);
+ if ($list = array_intersect($list, $projects)) {
+ locale_translation_file_history_delete($list);
// Remove translation files.
- module_load_include('inc', 'locale', 'locale.bulk');
- locale_translate_delete_translation_files($components, array());
+ locale_translate_delete_translation_files($list, array());
// Remove translatable projects.
// Followup issue http://drupal.org/node/1842362 to replace the
// {locale_project} table. Then change this to a function call.
db_delete('locale_project')
- ->condition('name', $components)
+ ->condition('name', $list)
->execute();
// Clear the translation status.
- locale_translation_status_delete_projects($components);
+ locale_translation_status_delete_projects($list);
}
+
}
}
-
/**
* Implements hook_js_alter().
*
@@ -772,6 +791,12 @@ function locale_form_language_admin_add_form_alter_submit($form, $form_state) {
$options = _locale_translation_default_update_options();
$batch = locale_translation_batch_update_build(array(), array($langcode), $options);
batch_set($batch);
+
+ // Create or update all configuration translations for this language.
+ Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+ if ($batch = locale_config_batch_update_components($options, array($langcode))) {
+ batch_set($batch);
+ }
}
/**
@@ -1051,6 +1076,25 @@ function _locale_refresh_translations($langcodes, $lids = array()) {
}
/**
+ * Refreshes configuration after string translations have been updated.
+ *
+ * The information that will be refreshed includes:
+ * - JavaScript translations.
+ * - Locale cache.
+ *
+ * @param array $langcodes
+ * Language codes for updated translations.
+ * @param array $lids
+ * List of string identifiers that have been updated / created.
+ */
+function _locale_refresh_configuration(array $langcodes, array $lids) {
+ if ($lids && $langcodes && $names = \Drupal\locale\Locale::config()->getStringNames($lids)) {
+ Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
+ locale_config_update_multiple($names, $langcodes);
+ }
+}
+
+/**
* Parses a JavaScript file, extracts strings wrapped in Drupal.t() and
* Drupal.formatPlural() and inserts them into the database.
*
@@ -1294,8 +1338,8 @@ function _locale_rebuild_js($langcode = NULL) {
return TRUE;
case 'rebuilt':
watchdog('locale', 'JavaScript translation file %file.js was lost.', array('%file' => $locale_javascripts[$language->langcode]), WATCHDOG_WARNING);
- // Proceed to the 'created' case as the JavaScript translation file has
- // been created again.
+ // Proceed to the 'created' case as the JavaScript translation file has
+ // been created again.
case 'created':
watchdog('locale', 'Created JavaScript translation file for the language %language.', array('%language' => $language->name));
return TRUE;
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index fc68a51..a109f5f 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -452,8 +452,9 @@ function locale_translate_edit_form_submit($form, &$form_state) {
}
if ($updated) {
- // Clear cache and force refresh of JavaScript translations.
+ // Clear cache and refresh configuration and JavaScript translations.
_locale_refresh_translations(array($langcode), $updated);
+ _locale_refresh_configuration(array($langcode), $updated);
}
}
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index 012a7d9..15763a4 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -4,3 +4,6 @@ services:
tags:
- { name: event_subscriber }
arguments: ['@language_manager', '@config.context']
+ locale.config.typed:
+ class: Drupal\locale\LocaleConfigManager
+ arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer']
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index d13655e..ece3aac 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -146,9 +146,9 @@ function menu_menu() {
* Implements hook_entity_info_alter().
*/
function menu_entity_info_alter(&$entity_info) {
- $entity_info['menu']['list_controller_class'] = 'Drupal\menu\MenuListController';
+ $entity_info['menu']['controllers']['list'] = 'Drupal\menu\MenuListController';
$entity_info['menu']['uri_callback'] = 'menu_uri';
- $entity_info['menu']['form_controller_class'] = array(
+ $entity_info['menu']['controllers']['form'] = array(
'default' => 'Drupal\menu\MenuFormController',
);
}
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php
index 85cca64..305fc33 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/Plugin/Core/Entity/MenuLink.php
@@ -21,10 +21,12 @@
* id = "menu_link",
* label = @Translation("Menu link"),
* module = "menu_link",
- * controller_class = "Drupal\menu_link\MenuLinkStorageController",
- * render_controller_class = "Drupal\Core\Entity\EntityRenderController",
- * form_controller_class = {
- * "default" = "Drupal\menu_link\MenuLinkFormController"
+ * controllers = {
+ * "storage" = "Drupal\menu_link\MenuLinkStorageController",
+ * "render" = "Drupal\Core\Entity\EntityRenderController",
+ * "form" = {
+ * "default" = "Drupal\menu_link\MenuLinkFormController"
+ * }
* },
* static_cache = FALSE,
* base_table = "menu_links",
diff --git a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
index c4d8109..99c6b88 100644
--- a/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Plugin/Core/Entity/Node.php
@@ -20,13 +20,15 @@
* label = @Translation("Content"),
* bundle_label = @Translation("Content type"),
* module = "node",
- * controller_class = "Drupal\node\NodeStorageController",
- * render_controller_class = "Drupal\node\NodeRenderController",
- * access_controller_class = "Drupal\node\NodeAccessController",
- * form_controller_class = {
- * "default" = "Drupal\node\NodeFormController"
+ * controllers = {
+ * "storage" = "Drupal\node\NodeStorageController",
+ * "render" = "Drupal\node\NodeRenderController",
+ * "access" = "Drupal\node\NodeAccessController",
+ * "form" = {
+ * "default" = "Drupal\node\NodeFormController"
+ * },
+ * "translation" = "Drupal\node\NodeTranslationController"
* },
- * translation_controller_class = "Drupal\node\NodeTranslationController",
* base_table = "node",
* revision_table = "node_revision",
* uri_callback = "node_uri",
diff --git a/core/modules/picture/lib/Drupal/picture/Plugin/Core/Entity/PictureMapping.php b/core/modules/picture/lib/Drupal/picture/Plugin/Core/Entity/PictureMapping.php
index 2c30145..64e57fb 100644
--- a/core/modules/picture/lib/Drupal/picture/Plugin/Core/Entity/PictureMapping.php
+++ b/core/modules/picture/lib/Drupal/picture/Plugin/Core/Entity/PictureMapping.php
@@ -18,13 +18,15 @@
* id = "picture_mapping",
* label = @Translation("Picture mapping"),
* module = "picture",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
- * form_controller_class = {
- * "default" = "Drupal\picture\PictureMappingFormController",
- * "add" = "Drupal\picture\PictureMappingFormController",
- * "duplicate" = "Drupal\picture\PictureMappingFormController"
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * "list" = "Drupal\picture\PictureMappingListController",
+ * "form" = {
+ * "default" = "Drupal\picture\PictureMappingFormController",
+ * "add" = "Drupal\picture\PictureMappingFormController",
+ * "duplicate" = "Drupal\picture\PictureMappingFormController"
+ * }
* },
- * list_controller_class = "Drupal\picture\PictureMappingListController",
* list_path = "admin/config/media/picturemapping",
* uri_callback = "picture_mapping_uri",
* config_prefix = "picture.mappings",
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php b/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php
index 5afc882..f6c106e 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/EntitySerializationTest.php
@@ -63,6 +63,8 @@ protected function setUp() {
$this->entity->save();
$this->serializer = $this->container->get('serializer');
+
+ $this->installConfig(array('field'));
}
/**
diff --git a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
index d485a5b..1d9bff6 100644
--- a/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
+++ b/core/modules/serialization/lib/Drupal/serialization/Tests/NormalizerTestBase.php
@@ -23,6 +23,7 @@ protected function setUp() {
$this->installSchema('entity_test', array('entity_test_mulrev', 'entity_test_mulrev_property_revision', 'entity_test_mulrev_property_data'));
$this->installSchema('system', array('url_alias'));
+ $this->installConfig(array('field'));
// Auto-create a field for testing.
field_create_field(array(
diff --git a/core/modules/shortcut/config/schema/shortcut.schema.yml b/core/modules/shortcut/config/schema/shortcut.schema.yml
index ace2525..81192bc 100644
--- a/core/modules/shortcut/config/schema/shortcut.schema.yml
+++ b/core/modules/shortcut/config/schema/shortcut.schema.yml
@@ -16,3 +16,12 @@ shortcut.set.*:
langcode:
type: string
label: 'Default language'
+ links:
+ type: sequence
+ label: 'Shortcuts'
+ sequence:
+ - type: string
+ label: 'Shortcut'
+ status:
+ type: boolean
+ label: 'Status'
diff --git a/core/modules/shortcut/lib/Drupal/shortcut/Plugin/Core/Entity/Shortcut.php b/core/modules/shortcut/lib/Drupal/shortcut/Plugin/Core/Entity/Shortcut.php
index 3e5146f..57b75df 100644
--- a/core/modules/shortcut/lib/Drupal/shortcut/Plugin/Core/Entity/Shortcut.php
+++ b/core/modules/shortcut/lib/Drupal/shortcut/Plugin/Core/Entity/Shortcut.php
@@ -18,11 +18,12 @@
* id = "shortcut",
* label = @Translation("Shortcut set"),
* module = "shortcut",
- * controller_class = "Drupal\shortcut\ShortcutStorageController",
- * list_controller_class = "Drupal\shortcut\ShortcutListController",
- * access_controller_class = "Drupal\shortcut\ShortcutAccessController",
- * form_controller_class = {
- * "default" = "Drupal\shortcut\ShortcutFormController"
+ * controllers = {
+ * "storage" = "Drupal\shortcut\ShortcutStorageController",
+ * "list" = "Drupal\shortcut\ShortcutListController",
+ * "form" = {
+ * "default" = "Drupal\shortcut\ShortcutFormController"
+ * }
* },
* config_prefix = "shortcut.set",
* entity_keys = {
diff --git a/core/modules/system/config/schema/system.data_types.schema.yml b/core/modules/system/config/schema/system.data_types.schema.yml
index 8b7c899..d3b5037 100644
--- a/core/modules/system/config/schema/system.data_types.schema.yml
+++ b/core/modules/system/config/schema/system.data_types.schema.yml
@@ -37,6 +37,7 @@ default:
label:
type: string
label: 'Label'
+ translatable: true
# Internal Drupal path
path:
@@ -47,6 +48,7 @@ path:
text:
type: string
label: 'Text'
+ translatable: true
# Complex extended data types:
diff --git a/core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Menu.php b/core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Menu.php
index cfb5b7a..b014331 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Menu.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/Core/Entity/Menu.php
@@ -18,7 +18,9 @@
* id = "menu",
* label = @Translation("Menu"),
* module = "system",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController"
+ * },
* config_prefix = "menu.menu",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php
index f7a6c65..3b607a5 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Cache/DatabaseBackendUnitTest.php
@@ -40,17 +40,4 @@ protected function createCacheBackend($bin) {
return new DatabaseBackend($this->container->get('database'), $bin);
}
- /**
- * Installs system schema.
- */
- public function setUpCacheBackend() {
- $this->installSchema('system', array('cache', 'cache_page', 'cache_tags', 'cache_path', 'cache_bootstrap'));
- }
-
- /**
- * Uninstalls system schema.
- */
- public function tearDownCacheBackend() {
- drupal_uninstall_schema('system');
- }
}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
index 6135bf0..cb0ea4d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityApiInfoTest.php
@@ -54,6 +54,6 @@ function testEntityInfoCacheWatchdog() {
module_enable(array('entity_cache_test'));
$info = state()->get('entity_cache_test');
$this->assertEqual($info['label'], 'Entity Cache Test', 'Entity info label is correct.');
- $this->assertEqual($info['controller_class'], 'Drupal\Core\Entity\DatabaseStorageController', 'Entity controller class info is correct.');
+ $this->assertEqual($info['controllers']['storage'], 'Drupal\Core\Entity\DatabaseStorageController', 'Entity controller class info is correct.');
}
}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityManagerTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityManagerTest.php
index b56b24f..b7559b9 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityManagerTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityManagerTest.php
@@ -29,13 +29,13 @@ public function testMethods() {
// Tests the has controller method.
$entity_manager = $this->container->get('plugin.manager.entity');
- $this->assertFalse($entity_manager->hasController('non_existent', 'controller_class'), 'A non existent entity type has no controller.');
- $this->assertFalse($entity_manager->hasController('non_existent', 'non_existent_controller_class'), 'A non existent entity type has no controller.');
+ $this->assertFalse($entity_manager->hasController('non_existent', 'storage'), 'A non existent entity type has no controller.');
+ $this->assertFalse($entity_manager->hasController('non_existent', 'non_existent'), 'A non existent entity type has no controller.');
- $this->assertFalse($entity_manager->hasController('entity_test', 'non_existent_controller_class'), 'An existent entity type does not have a non existent controller.');
- $this->assertFalse($entity_manager->hasController('entity_test', 'render_controller_class'), 'The test entity does not have specified the render controller.');
+ $this->assertFalse($entity_manager->hasController('entity_test', 'non_existent'), 'An existent entity type does not have a non existent controller.');
+ $this->assertFalse($entity_manager->hasController('entity_test', 'render'), 'The test entity does not have specified the render controller.');
- $this->assertTrue($entity_manager->hasController('entity_test', 'controller_class'), 'The test entity has specified the controller class');
+ $this->assertTrue($entity_manager->hasController('entity_test', 'storage'), 'The test entity has specified the controller class');
}
}
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
index c3f236b..41918d3 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityUnitTestBase.php
@@ -26,6 +26,7 @@ public function setUp() {
$this->installSchema('user', 'users');
$this->installSchema('system', 'sequences');
$this->installSchema('entity_test', 'entity_test');
+ $this->installConfig(array('field'));
}
/**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php
index 5e08eed..a460b6f 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/FieldAccessTest.php
@@ -41,6 +41,8 @@ public static function getInfo() {
protected function setUp() {
parent::setUp();
+ // Install field configuration.
+ $this->installConfig(array('field'));
// The users table is needed for creating dummy user accounts.
$this->installSchema('user', array('users'));
// Register entity_test text field.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
index 38c57d6..0936aa5 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/FieldUpgradePathTest.php
@@ -137,6 +137,7 @@ function testFieldUpgradeToConfig() {
$this->assertEqual($config, array(
'id' => "node.$node_type.body",
'field_uuid' => $field_uuid,
+ 'field_type' => 'text_with_summary',
'entity_type' => 'node',
'bundle' => $node_type,
'label' => 'Body',
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index e0be14f..8c14060 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1841,38 +1841,33 @@ function system_image_toolkit_settings($form, &$form_state) {
// If we have available toolkits allow the user to select the image toolkit to
// use and load the settings forms.
- if (count($toolkits_available)) {
- $options = array();
- foreach($toolkits_available as $id => $definition) {
- $options[$id] = $definition['title'];
- }
-
- $form['image_toolkit'] = array(
- '#type' => 'radios',
- '#title' => t('Select an image processing toolkit'),
- '#default_value' => $current_toolkit,
- '#options' => $options,
- );
+ $options = array();
+ foreach($toolkits_available as $id => $definition) {
+ $options[$id] = $definition['title'];
+ }
- // Get the toolkit settings forms.
- foreach ($toolkits_available as $id => $definition) {
- $toolkit = $manager->createInstance($id);
- $form['image_toolkit_settings'][$id] = array(
- '#type' => 'fieldset',
- '#title' => t('@toolkit settings', array('@toolkit' => $definition['title'])),
- '#collapsible' => TRUE,
- '#collapsed' => ($id == $current_toolkit) ? FALSE : TRUE,
- '#tree' => TRUE,
- );
- $form['image_toolkit_settings'][$id] += $toolkit->settingsForm();
- }
+ $form['image_toolkit'] = array(
+ '#type' => 'radios',
+ '#title' => t('Select an image processing toolkit'),
+ '#default_value' => $current_toolkit,
+ '#options' => $options,
+ );
- $form = system_config_form($form, $form_state);
- }
- else {
- form_set_error('image_toolkit', t('There are no image toolkits available. Drupal comes with support for PHP\'s GD image toolkit. This requires that the GD module for PHP be installed and configured properly. For more information see PHP\'s image documentation.', array('@url' => 'http://php.net/image')));
+ // Get the toolkit settings forms.
+ foreach ($toolkits_available as $id => $definition) {
+ $toolkit = $manager->createInstance($id);
+ $form['image_toolkit_settings'][$id] = array(
+ '#type' => 'fieldset',
+ '#title' => t('@toolkit settings', array('@toolkit' => $definition['title'])),
+ '#collapsible' => TRUE,
+ '#collapsed' => ($id == $current_toolkit) ? FALSE : TRUE,
+ '#tree' => TRUE,
+ );
+ $form['image_toolkit_settings'][$id] += $toolkit->settingsForm();
}
+ $form = system_config_form($form, $form_state);
+
return $form;
}
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 73ec88e..339cdba 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -1934,12 +1934,12 @@ function hook_mail($key, &$message, $params) {
/**
* Flush all persistent and static caches.
*
- * This hook asks your module to clear all of its persistent (database) and
- * static caches, in order to ensure a clean environment for subsequently
+ * This hook asks your module to clear all of its static caches,
+ * in order to ensure a clean environment for subsequently
* invoked data rebuilds.
*
* Do NOT use this hook for rebuilding information. Only use it to flush custom
- * caches and return the names of additional cache bins to flush.
+ * caches.
*
* Static caches using drupal_static() do not need to be reset manually.
* However, all other static variables that do not use drupal_static() must be
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 493db9b..e23bf8e 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -597,102 +597,6 @@ function system_schema() {
),
);
- $schema['cache_tags'] = array(
- 'description' => 'Cache table for tracking cache tags related to the cache bin.',
- 'fields' => array(
- 'tag' => array(
- 'description' => 'Namespace-prefixed tag string.',
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- ),
- 'invalidations' => array(
- 'description' => 'Number incremented when the tag is invalidated.',
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- ),
- 'deletions' => array(
- 'description' => 'Number incremented when the tag is deleted.',
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- ),
- ),
- 'primary key' => array('tag'),
- );
-
- $schema['cache'] = array(
- 'description' => 'Generic cache table for caching things not separated out into their own tables. Contributed modules may also use this to store cached items.',
- 'fields' => array(
- 'cid' => array(
- 'description' => 'Primary Key: Unique cache ID.',
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- 'default' => '',
- ),
- 'data' => array(
- 'description' => 'A collection of data to cache.',
- 'type' => 'blob',
- 'not null' => FALSE,
- 'size' => 'big',
- ),
- 'expire' => array(
- 'description' => 'A Unix timestamp indicating when the cache entry should expire, or 0 for never.',
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- ),
- 'created' => array(
- 'description' => 'A Unix timestamp indicating when the cache entry was created.',
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- ),
- 'serialized' => array(
- 'description' => 'A flag to indicate whether content is serialized (1) or not (0).',
- 'type' => 'int',
- 'size' => 'small',
- 'not null' => TRUE,
- 'default' => 0,
- ),
- 'tags' => array(
- 'description' => 'Space-separated list of cache tags for this entry.',
- 'type' => 'text',
- 'size' => 'big',
- 'not null' => FALSE,
- ),
- 'checksum_invalidations' => array(
- 'description' => 'The tag invalidation sum when this entry was saved.',
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- ),
- 'checksum_deletions' => array(
- 'description' => 'The tag deletion sum when this entry was saved.',
- 'type' => 'int',
- 'not null' => TRUE,
- 'default' => 0,
- ),
- ),
- 'indexes' => array(
- 'expire' => array('expire'),
- ),
- 'primary key' => array('cid'),
- );
- $schema['cache_bootstrap'] = $schema['cache'];
- $schema['cache_bootstrap']['description'] = 'Cache table for data required to bootstrap Drupal, may be routed to a shared memory cache.';
- $schema['cache_config'] = $schema['cache'];
- $schema['cache_config']['description'] = 'Cache table for configuration data.';
- $schema['cache_page'] = $schema['cache'];
- $schema['cache_page']['description'] = 'Cache table used to store compressed pages for anonymous users, if page caching is enabled.';
- $schema['cache_menu'] = $schema['cache'];
- $schema['cache_menu']['description'] = 'Cache table for the menu system to store router information as well as generated link trees for various menu/page/user combinations.';
- $schema['cache_path'] = $schema['cache'];
- $schema['cache_path']['description'] = 'Cache table for path alias lookup.';
-
$schema['flood'] = array(
'description' => 'Flood controls the threshold of events, such as the number of contact attempts.',
'fields' => array(
diff --git a/core/modules/system/tests/modules/cache_test/cache_test.install b/core/modules/system/tests/modules/cache_test/cache_test.install
deleted file mode 100644
index 5b50e11..0000000
--- a/core/modules/system/tests/modules/cache_test/cache_test.install
+++ /dev/null
@@ -1,15 +0,0 @@
-entityManager = $entity_manager;
+ $this->config = $config_factory->get('taxonomy.settings');
+ $this->database = $connection;
+ $this->request = $request;
+ $this->moduleHandler = $module_handler;
+ }
+
+ /**
+ * Injects plugin manager.
+ */
+ public static function create(ContainerInterface $container) {
+ return new static(
+ $container->get('plugin.manager.entity'),
+ $container->get('config.factory'),
+ $container->get('database'),
+ $container->get('request'),
+ $container->get('module_handler')
+ );
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getFormId() {
+ return 'taxonomy_overview_terms';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function buildForm(array $form, array &$form_state, Vocabulary $taxonomy_vocabulary = NULL) {
+ // @todo entity_page_label does not work with new routing system.
+ drupal_set_title($taxonomy_vocabulary->label());
+ global $pager_page_array, $pager_total, $pager_total_items;
+
+ // Check for confirmation forms.
+ if (isset($form_state['confirm_reset_alphabetical'])) {
+ // @todo this needs to be a separate route/controller for a confirm form.
+ // Will be fixed in http://drupal.org/node/1976296.
+ $this->moduleHandler->loadInclude('taxonomy', 'admin.inc');
+ return taxonomy_vocabulary_confirm_reset_alphabetical($form, $form_state, $taxonomy_vocabulary->id());
+ }
+
+ $form_state['taxonomy']['vocabulary'] = $taxonomy_vocabulary;
+ $parent_fields = FALSE;
+
+ $page = isset($_GET['page']) ? $_GET['page'] : 0;
+ // Number of terms per page.
+ $page_increment = $this->config->get('terms_per_page_admin');
+ // Elements shown on this page.
+ $page_entries = 0;
+ // Elements at the root level before this page.
+ $before_entries = 0;
+ // Elements at the root level after this page.
+ $after_entries = 0;
+ // Elements at the root level on this page.
+ $root_entries = 0;
+
+ // Terms from previous and next pages are shown if the term tree would have
+ // been cut in the middle. Keep track of how many extra terms we show on each
+ // page of terms.
+ $back_step = NULL;
+ $forward_step = 0;
+
+ // An array of the terms to be displayed on this page.
+ $current_page = array();
+
+ $delta = 0;
+ $term_deltas = array();
+ // @todo taxonomy_get_tree needs to be converted to a service and injected.
+ // Will be fixed in http://drupal.org/node/1976298.
+ $tree = taxonomy_get_tree($taxonomy_vocabulary->id(), 0, NULL, TRUE);
+ $term = current($tree);
+ do {
+ // In case this tree is completely empty.
+ if (empty($term)) {
+ break;
+ }
+ $delta++;
+ // Count entries before the current page.
+ if ($page && ($page * $page_increment) > $before_entries && !isset($back_step)) {
+ $before_entries++;
+ continue;
+ }
+ // Count entries after the current page.
+ elseif ($page_entries > $page_increment && isset($complete_tree)) {
+ $after_entries++;
+ continue;
+ }
+
+ // Do not let a term start the page that is not at the root.
+ if (isset($term->depth) && ($term->depth > 0) && !isset($back_step)) {
+ $back_step = 0;
+ while ($pterm = prev($tree)) {
+ $before_entries--;
+ $back_step++;
+ if ($pterm->depth == 0) {
+ prev($tree);
+ // Jump back to the start of the root level parent.
+ continue 2;
+ }
+ }
+ }
+ $back_step = isset($back_step) ? $back_step : 0;
+
+ // Continue rendering the tree until we reach the a new root item.
+ if ($page_entries >= $page_increment + $back_step + 1 && $term->depth == 0 && $root_entries > 1) {
+ $complete_tree = TRUE;
+ // This new item at the root level is the first item on the next page.
+ $after_entries++;
+ continue;
+ }
+ if ($page_entries >= $page_increment + $back_step) {
+ $forward_step++;
+ }
+
+ // Finally, if we've gotten down this far, we're rendering a term on this page.
+ $page_entries++;
+ $term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ? $term_deltas[$term->tid] + 1 : 0;
+ $key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid];
+
+ // Keep track of the first term displayed on this page.
+ if ($page_entries == 1) {
+ $form['#first_tid'] = $term->tid;
+ }
+ // Keep a variable to make sure at least 2 root elements are displayed.
+ if ($term->parents[0] == 0) {
+ $root_entries++;
+ }
+ $current_page[$key] = $term;
+ } while ($term = next($tree));
+
+ // Because we didn't use a pager query, set the necessary pager variables.
+ $total_entries = $before_entries + $page_entries + $after_entries;
+ $pager_total_items[0] = $total_entries;
+ $pager_page_array[0] = $page;
+ $pager_total[0] = ceil($total_entries / $page_increment);
+
+ // If this form was already submitted once, it's probably hit a validation
+ // error. Ensure the form is rebuilt in the same order as the user submitted.
+ if (!empty($form_state['input'])) {
+ // Get the $_POST order.
+ $order = array_flip(array_keys($form_state['input']['terms']));
+ // Update our form with the new order.
+ $current_page = array_merge($order, $current_page);
+ foreach ($current_page as $key => $term) {
+ // Verify this is a term for the current page and set at the current
+ // depth.
+ if (is_array($form_state['input']['terms'][$key]) && is_numeric($form_state['input']['terms'][$key]['term']['tid'])) {
+ $current_page[$key]->depth = $form_state['input']['terms'][$key]['term']['depth'];
+ }
+ else {
+ unset($current_page[$key]);
+ }
+ }
+ }
+
+ $errors = form_get_errors() != FALSE ? form_get_errors() : array();
+ $destination = drupal_get_destination();
+ $row_position = 0;
+ // Build the actual form.
+ $form['terms'] = array(
+ '#type' => 'table',
+ '#header' => array(t('Name'), t('Weight'), t('Operations')),
+ '#empty' => t('No terms available. Add term.', array('@link' => url('admin/structure/taxonomy/' . $taxonomy_vocabulary->id() . '/add'))),
+ '#attributes' => array(
+ 'id' => 'taxonomy',
+ ),
+ );
+ foreach ($current_page as $key => $term) {
+ // Save the term for the current page so we don't have to load it a second
+ // time.
+ $form['terms'][$key]['#term'] = (array) $term;
+ if (isset($term->parents)) {
+ $form['terms'][$key]['#term']['parent'] = $term->parent = $term->parents[0];
+ unset($form['terms'][$key]['#term']['parents'], $term->parents);
+ }
+
+ $form['terms'][$key]['term'] = array(
+ '#prefix' => isset($term->depth) && $term->depth > 0 ? theme('indentation', array('size' => $term->depth)) : '',
+ '#type' => 'link',
+ '#title' => $term->label(),
+ '#href' => "taxonomy/term/$term->tid",
+ );
+ if ($taxonomy_vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
+ $parent_fields = TRUE;
+ $form['terms'][$key]['term']['tid'] = array(
+ '#type' => 'hidden',
+ '#value' => $term->tid,
+ '#attributes' => array(
+ 'class' => array('term-id'),
+ ),
+ );
+ $form['terms'][$key]['term']['parent'] = array(
+ '#type' => 'hidden',
+ // Yes, default_value on a hidden. It needs to be changeable by the
+ // javascript.
+ '#default_value' => $term->parent,
+ '#attributes' => array(
+ 'class' => array('term-parent'),
+ ),
+ );
+ $form['terms'][$key]['term']['depth'] = array(
+ '#type' => 'hidden',
+ // Same as above, the depth is modified by javascript, so it's a
+ // default_value.
+ '#default_value' => $term->depth,
+ '#attributes' => array(
+ 'class' => array('term-depth'),
+ ),
+ );
+ }
+ $form['terms'][$key]['weight'] = array(
+ '#type' => 'weight',
+ '#delta' => $delta,
+ '#title_display' => 'invisible',
+ '#title' => t('Weight for added term'),
+ '#default_value' => $term->weight,
+ '#attributes' => array(
+ 'class' => array('term-weight'),
+ ),
+ );
+ $operations = array(
+ 'edit' => array(
+ 'title' => t('edit'),
+ 'href' => 'taxonomy/term/' . $term->tid . '/edit',
+ 'query' => $destination,
+ ),
+ 'delete' => array(
+ 'title' => t('delete'),
+ 'href' => 'taxonomy/term/' . $term->tid . '/delete',
+ 'query' => $destination,
+ ),
+ );
+ if (module_invoke('translation_entity', 'translate_access', $term)) {
+ $operations['translate'] = array(
+ 'title' => t('translate'),
+ 'href' => 'taxonomy/term/' . $term->tid . '/translations',
+ 'query' => $destination,
+ );
+ }
+ $form['terms'][$key]['operations'] = array(
+ '#type' => 'operations',
+ '#links' => $operations,
+ );
+
+ $form['terms'][$key]['#attributes']['class'] = array();
+ if ($parent_fields) {
+ $form['terms'][$key]['#attributes']['class'][] = 'draggable';
+ }
+
+ // Add classes that mark which terms belong to previous and next pages.
+ if ($row_position < $back_step || $row_position >= $page_entries - $forward_step) {
+ $form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-preview';
+ }
+
+ if ($row_position !== 0 && $row_position !== count($tree) - 1) {
+ if ($row_position == $back_step - 1 || $row_position == $page_entries - $forward_step - 1) {
+ $form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-divider-top';
+ }
+ elseif ($row_position == $back_step || $row_position == $page_entries - $forward_step) {
+ $form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-divider-bottom';
+ }
+ }
+
+ // Add an error class if this row contains a form error.
+ foreach ($errors as $error_key => $error) {
+ if (strpos($error_key, $key) === 0) {
+ $form['terms'][$key]['#attributes']['class'][] = 'error';
+ }
+ }
+ $row_position++;
+ }
+
+ if ($parent_fields) {
+ $form['terms']['#tabledrag'][] = array(
+ 'match',
+ 'parent',
+ 'term-parent',
+ 'term-parent',
+ 'term-id',
+ FALSE
+ );
+ $form['terms']['#tabledrag'][] = array(
+ 'depth',
+ 'group',
+ 'term-depth',
+ NULL,
+ NULL,
+ FALSE
+ );
+ $form['terms']['#attached']['library'][] = array('taxonomy', 'drupal.taxonomy');
+ $form['terms']['#attached']['js'][] = array(
+ 'data' => array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)),
+ 'type' => 'setting',
+ );
+ }
+ $form['terms']['#tabledrag'][] = array('order', 'sibling', 'term-weight');
+
+ if ($taxonomy_vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
+ $form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save'),
+ '#button_type' => 'primary',
+ );
+ $form['actions']['reset_alphabetical'] = array(
+ '#type' => 'submit',
+ '#value' => t('Reset to alphabetical')
+ );
+ $form_state['redirect'] = array($this->request->attributes->get('system_path'), (isset($_GET['page']) ? array('query' => array('page' => $_GET['page'])) : array()));
+ }
+
+ return $form;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function validateForm(array &$form, array &$form_state) {}
+
+ /**
+ * {@inheritdoc}
+ */
+ public function submitForm(array &$form, array &$form_state) {
+ // @todo this needs to be in its own method.
+ if ($form_state['triggering_element']['#value'] == t('Reset to alphabetical')) {
+ // Execute the reset action.
+ if ($form_state['values']['reset_alphabetical'] === TRUE) {
+ // @todo this needs to be a method on the vocabulary storage controller.
+ // Will be fixed in http://drupal.org/node/1976296.
+ $this->moduleHandler->loadInclude('taxonomy', 'admin.inc');
+ return taxonomy_vocabulary_confirm_reset_alphabetical_submit($form, $form_state);
+ }
+ // Rebuild the form to confirm the reset action.
+ $form_state['rebuild'] = TRUE;
+ $form_state['confirm_reset_alphabetical'] = TRUE;
+ return;
+ }
+
+ // Sort term order based on weight.
+ uasort($form_state['values']['terms'], 'drupal_sort_weight');
+
+ $vocabulary = $form_state['taxonomy']['vocabulary'];
+ // Update the current hierarchy type as we go.
+ $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
+
+ $changed_terms = array();
+ // @todo convert taxonomy_get_tree() to a service and inject it.
+ // Will be fixed in http://drupal.org/node/1976298.
+ $tree = taxonomy_get_tree($vocabulary->id());
+
+ if (empty($tree)) {
+ return;
+ }
+
+ // Build a list of all terms that need to be updated on previous pages.
+ $weight = 0;
+ $term = (array) $tree[0];
+ while ($term['tid'] != $form['#first_tid']) {
+ if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
+ $term['parent'] = $term['parents'][0];
+ $term['weight'] = $weight;
+ $changed_terms[$term['tid']] = $term;
+ }
+ $weight++;
+ $hierarchy = $term['parents'][0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
+ $term = (array) $tree[$weight];
+ }
+
+ // Renumber the current page weights and assign any new parents.
+ $level_weights = array();
+ foreach ($form_state['values']['terms'] as $tid => $values) {
+ if (isset($form['terms'][$tid]['#term'])) {
+ $term = $form['terms'][$tid]['#term'];
+ // Give terms at the root level a weight in sequence with terms on previous pages.
+ if ($values['term']['parent'] == 0 && $term['weight'] != $weight) {
+ $term['weight'] = $weight;
+ $changed_terms[$term['tid']] = $term;
+ }
+ // Terms not at the root level can safely start from 0 because they're all on this page.
+ elseif ($values['term']['parent'] > 0) {
+ $level_weights[$values['term']['parent']] = isset($level_weights[$values['term']['parent']]) ? $level_weights[$values['term']['parent']] + 1 : 0;
+ if ($level_weights[$values['term']['parent']] != $term['weight']) {
+ $term['weight'] = $level_weights[$values['term']['parent']];
+ $changed_terms[$term['tid']] = $term;
+ }
+ }
+ // Update any changed parents.
+ if ($values['term']['parent'] != $term['parent']) {
+ $term['parent'] = $values['term']['parent'];
+ $changed_terms[$term['tid']] = $term;
+ }
+ $hierarchy = $term['parent'] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
+ $weight++;
+ }
+ }
+
+ // Build a list of all terms that need to be updated on following pages.
+ for ($weight; $weight < count($tree); $weight++) {
+ $term = (array) $tree[$weight];
+ if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
+ $term['parent'] = $term['parents'][0];
+ $term['weight'] = $weight;
+ $changed_terms[$term['tid']] = $term;
+ }
+ $hierarchy = $term['parents'][0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
+ }
+
+ // Save all updated terms.
+ foreach ($changed_terms as $changed) {
+ $term = (object) $changed;
+ // Update term_hierachy and term_data directly since we don't have a
+ // fully populated term object to save.
+ $this->database->update('taxonomy_term_hierarchy')
+ ->fields(array('parent' => $term->parent))
+ ->condition('tid', $term->tid, '=')
+ ->execute();
+
+ $this->database->update('taxonomy_term_data')
+ ->fields(array('weight' => $term->weight))
+ ->condition('tid', $term->tid, '=')
+ ->execute();
+ }
+
+ // Update the vocabulary hierarchy to flat or single hierarchy.
+ if ($vocabulary->hierarchy != $hierarchy) {
+ $vocabulary->hierarchy = $hierarchy;
+ $vocabulary->save();
+ }
+ drupal_set_message(t('The configuration options have been saved.'));
+ }
+
+}
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
index 07f2a81..60811db 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Term.php
@@ -20,13 +20,15 @@
* label = @Translation("Taxonomy term"),
* bundle_label = @Translation("Vocabulary"),
* module = "taxonomy",
- * controller_class = "Drupal\taxonomy\TermStorageController",
- * render_controller_class = "Drupal\taxonomy\TermRenderController",
- * access_controller_class = "Drupal\taxonomy\TermAccessController",
- * form_controller_class = {
- * "default" = "Drupal\taxonomy\TermFormController"
+ * controllers = {
+ * "storage" = "Drupal\taxonomy\TermStorageController",
+ * "render" = "Drupal\taxonomy\TermRenderController",
+ * "access" = "Drupal\taxonomy\TermAccessController",
+ * "form" = {
+ * "default" = "Drupal\taxonomy\TermFormController"
+ * },
+ * "translation" = "Drupal\taxonomy\TermTranslationController"
* },
- * translation_controller_class = "Drupal\taxonomy\TermTranslationController",
* base_table = "taxonomy_term_data",
* uri_callback = "taxonomy_term_uri",
* fieldable = TRUE,
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
index c9ede6a..c69171d 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Plugin/Core/Entity/Vocabulary.php
@@ -18,10 +18,12 @@
* id = "taxonomy_vocabulary",
* label = @Translation("Taxonomy vocabulary"),
* module = "taxonomy",
- * controller_class = "Drupal\taxonomy\VocabularyStorageController",
- * access_controller_class = "Drupal\taxonomy\VocabularyAccessController",
- * form_controller_class = {
- * "default" = "Drupal\taxonomy\VocabularyFormController"
+ * controllers = {
+ * "storage" = "Drupal\taxonomy\VocabularyStorageController",
+ * "access" = "Drupal\taxonomy\VocabularyAccessController",
+ * "form" = {
+ * "default" = "Drupal\taxonomy\VocabularyFormController"
+ * }
* },
* config_prefix = "taxonomy.vocabulary",
* entity_keys = {
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php
index 6785335..3b19851 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/VocabularyFormController.php
@@ -24,6 +24,10 @@ public function form(array $form, array &$form_state, EntityInterface $vocabular
if (isset($form_state['confirm_delete']) && isset($form_state['values']['vid'])) {
return taxonomy_vocabulary_confirm_delete($form, $form_state, $form_state['values']['vid']);
}
+ // @todo Title callbacks don't work with routes.
+ if ($vocabulary->name) {
+ drupal_set_title($vocabulary->name);
+ }
$form['name'] = array(
'#type' => 'textfield',
'#title' => t('Name'),
diff --git a/core/modules/taxonomy/taxonomy.admin.inc b/core/modules/taxonomy/taxonomy.admin.inc
index d26501f..5d03c00 100644
--- a/core/modules/taxonomy/taxonomy.admin.inc
+++ b/core/modules/taxonomy/taxonomy.admin.inc
@@ -103,395 +103,6 @@ function taxonomy_vocabulary_add() {
}
/**
- * Form builder for the taxonomy terms overview.
- *
- * Display a tree of all the terms in a vocabulary, with options to edit
- * each one. The form is made drag and drop by the theme function.
- *
- * @param Drupal\taxonomy\Plugin\Core\Entity\Vocabulary $vocabulary
- * The taxonomy vocabulary entity to list terms for.
- *
- * @ingroup forms
- * @see taxonomy_overview_terms_submit()
- * @see theme_taxonomy_overview_terms()
- */
-function taxonomy_overview_terms($form, &$form_state, Vocabulary $vocabulary) {
- global $pager_page_array, $pager_total, $pager_total_items;
-
- // Check for confirmation forms.
- if (isset($form_state['confirm_reset_alphabetical'])) {
- return taxonomy_vocabulary_confirm_reset_alphabetical($form, $form_state, $vocabulary->id());
- }
-
- $form_state['taxonomy']['vocabulary'] = $vocabulary;
- $parent_fields = FALSE;
-
- $page = isset($_GET['page']) ? $_GET['page'] : 0;
- $page_increment = config('taxonomy.settings')->get('terms_per_page_admin'); // Number of terms per page.
- $page_entries = 0; // Elements shown on this page.
- $before_entries = 0; // Elements at the root level before this page.
- $after_entries = 0; // Elements at the root level after this page.
- $root_entries = 0; // Elements at the root level on this page.
-
- // Terms from previous and next pages are shown if the term tree would have
- // been cut in the middle. Keep track of how many extra terms we show on each
- // page of terms.
- $back_step = NULL;
- $forward_step = 0;
-
- // An array of the terms to be displayed on this page.
- $current_page = array();
-
- $delta = 0;
- $term_deltas = array();
- $tree = taxonomy_get_tree($vocabulary->id(), 0, NULL, TRUE);
- $term = current($tree);
- do {
- // In case this tree is completely empty.
- if (empty($term)) {
- break;
- }
- $delta++;
- // Count entries before the current page.
- if ($page && ($page * $page_increment) > $before_entries && !isset($back_step)) {
- $before_entries++;
- continue;
- }
- // Count entries after the current page.
- elseif ($page_entries > $page_increment && isset($complete_tree)) {
- $after_entries++;
- continue;
- }
-
- // Do not let a term start the page that is not at the root.
- if (isset($term->depth) && ($term->depth > 0) && !isset($back_step)) {
- $back_step = 0;
- while ($pterm = prev($tree)) {
- $before_entries--;
- $back_step++;
- if ($pterm->depth == 0) {
- prev($tree);
- continue 2; // Jump back to the start of the root level parent.
- }
- }
- }
- $back_step = isset($back_step) ? $back_step : 0;
-
- // Continue rendering the tree until we reach the a new root item.
- if ($page_entries >= $page_increment + $back_step + 1 && $term->depth == 0 && $root_entries > 1) {
- $complete_tree = TRUE;
- // This new item at the root level is the first item on the next page.
- $after_entries++;
- continue;
- }
- if ($page_entries >= $page_increment + $back_step) {
- $forward_step++;
- }
-
- // Finally, if we've gotten down this far, we're rendering a term on this page.
- $page_entries++;
- $term_deltas[$term->tid] = isset($term_deltas[$term->tid]) ? $term_deltas[$term->tid] + 1 : 0;
- $key = 'tid:' . $term->tid . ':' . $term_deltas[$term->tid];
-
- // Keep track of the first term displayed on this page.
- if ($page_entries == 1) {
- $form['#first_tid'] = $term->tid;
- }
- // Keep a variable to make sure at least 2 root elements are displayed.
- if ($term->parents[0] == 0) {
- $root_entries++;
- }
- $current_page[$key] = $term;
- } while ($term = next($tree));
-
- // Because we didn't use a pager query, set the necessary pager variables.
- $total_entries = $before_entries + $page_entries + $after_entries;
- $pager_total_items[0] = $total_entries;
- $pager_page_array[0] = $page;
- $pager_total[0] = ceil($total_entries / $page_increment);
-
- // If this form was already submitted once, it's probably hit a validation
- // error. Ensure the form is rebuilt in the same order as the user submitted.
- if (!empty($form_state['input'])) {
- $order = array_flip(array_keys($form_state['input']['terms'])); // Get the $_POST order.
- $current_page = array_merge($order, $current_page); // Update our form with the new order.
- foreach ($current_page as $key => $term) {
- // Verify this is a term for the current page and set at the current
- // depth.
- if (is_array($form_state['input']['terms'][$key]) && is_numeric($form_state['input']['terms'][$key]['term']['tid'])) {
- $current_page[$key]->depth = $form_state['input']['terms'][$key]['term']['depth'];
- }
- else {
- unset($current_page[$key]);
- }
- }
- }
-
- $errors = form_get_errors() != FALSE ? form_get_errors() : array();
- $destination = drupal_get_destination();
- $row_position = 0;
- // Build the actual form.
- $form['terms'] = array(
- '#type' => 'table',
- '#header' => array(t('Name'), t('Weight'), t('Operations')),
- '#empty' => t('No terms available. Add term.', array('@link' => url('admin/structure/taxonomy/' . $vocabulary->id() . '/add'))),
- '#attributes' => array(
- 'id' => 'taxonomy',
- ),
- );
- foreach ($current_page as $key => $term) {
- // Save the term for the current page so we don't have to load it a second
- // time.
- $form['terms'][$key]['#term'] = (array) $term;
- if (isset($term->parents)) {
- $form['terms'][$key]['#term']['parent'] = $term->parent = $term->parents[0];
- unset($form['terms'][$key]['#term']['parents'], $term->parents);
- }
-
- $form['terms'][$key]['term'] = array(
- '#prefix' => isset($term->depth) && $term->depth > 0 ? theme('indentation', array('size' => $term->depth)) : '',
- '#type' => 'link',
- '#title' => $term->label(),
- '#href' => "taxonomy/term/$term->tid",
- );
- if ($vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
- $parent_fields = TRUE;
- $form['terms'][$key]['term']['tid'] = array(
- '#type' => 'hidden',
- '#value' => $term->tid,
- '#attributes' => array(
- 'class' => array('term-id'),
- ),
- );
- $form['terms'][$key]['term']['parent'] = array(
- '#type' => 'hidden',
- // Yes, default_value on a hidden. It needs to be changeable by the
- // javascript.
- '#default_value' => $term->parent,
- '#attributes' => array(
- 'class' => array('term-parent'),
- ),
- );
- $form['terms'][$key]['term']['depth'] = array(
- '#type' => 'hidden',
- // Same as above, the depth is modified by javascript, so it's a
- // default_value.
- '#default_value' => $term->depth,
- '#attributes' => array(
- 'class' => array('term-depth'),
- ),
- );
- }
- $form['terms'][$key]['weight'] = array(
- '#type' => 'weight',
- '#delta' => $delta,
- '#title_display' => 'invisible',
- '#title' => t('Weight for added term'),
- '#default_value' => $term->weight,
- '#attributes' => array(
- 'class' => array('term-weight'),
- ),
- );
- $operations = array(
- 'edit' => array(
- 'title' => t('edit'),
- 'href' => 'taxonomy/term/' . $term->tid . '/edit',
- 'query' => $destination,
- ),
- 'delete' => array(
- 'title' => t('delete'),
- 'href' => 'taxonomy/term/' . $term->tid . '/delete',
- 'query' => $destination,
- ),
- );
- if (module_invoke('translation_entity', 'translate_access', $term)) {
- $operations['translate'] = array(
- 'title' => t('translate'),
- 'href' => 'taxonomy/term/' . $term->tid . '/translations',
- 'query' => $destination,
- );
- }
- $form['terms'][$key]['operations'] = array(
- '#type' => 'operations',
- '#links' => $operations,
- );
-
- $form['terms'][$key]['#attributes']['class'] = array();
- if ($parent_fields) {
- $form['terms'][$key]['#attributes']['class'][] = 'draggable';
- }
-
- // Add classes that mark which terms belong to previous and next pages.
- if ($row_position < $back_step || $row_position >= $page_entries - $forward_step) {
- $form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-preview';
- }
-
- if ($row_position !== 0 && $row_position !== count($tree) - 1) {
- if ($row_position == $back_step - 1 || $row_position == $page_entries - $forward_step - 1) {
- $form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-divider-top';
- }
- elseif ($row_position == $back_step || $row_position == $page_entries - $forward_step) {
- $form['terms'][$key]['#attributes']['class'][] = 'taxonomy-term-divider-bottom';
- }
- }
-
- // Add an error class if this row contains a form error.
- foreach ($errors as $error_key => $error) {
- if (strpos($error_key, $key) === 0) {
- $form['terms'][$key]['#attributes']['class'][] = 'error';
- }
- }
- $row_position++;
- }
-
-
- if ($parent_fields) {
- $form['terms']['#tabledrag'][] = array('match', 'parent', 'term-parent', 'term-parent', 'term-id', FALSE);
- $form['terms']['#tabledrag'][] = array('depth', 'group', 'term-depth', NULL, NULL, FALSE);
- $form['terms']['#attached']['library'][] = array('taxonomy', 'drupal.taxonomy');
- $form['terms']['#attached']['js'][] = array(
- 'data' => array('taxonomy' => array('backStep' => $back_step, 'forwardStep' => $forward_step)),
- 'type' => 'setting',
- );
- }
- $form['terms']['#tabledrag'][] = array('order', 'sibling', 'term-weight');
-
- if ($vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
- $form['actions'] = array('#type' => 'actions', '#tree' => FALSE);
- $form['actions']['submit'] = array(
- '#type' => 'submit',
- '#value' => t('Save'),
- '#button_type' => 'primary',
- );
- $form['actions']['reset_alphabetical'] = array(
- '#type' => 'submit',
- '#value' => t('Reset to alphabetical')
- );
- $form_state['redirect'] = array(current_path(), (isset($_GET['page']) ? array('query' => array('page' => $_GET['page'])) : array()));
- }
-
- return $form;
-}
-
-/**
- * Submit handler for terms overview form.
- *
- * Rather than using a textfield or weight field, this form depends entirely
- * upon the order of form elements on the page to determine new weights.
- *
- * Because there might be hundreds or thousands of taxonomy terms that need to
- * be ordered, terms are weighted from 0 to the number of terms in the
- * vocabulary, rather than the standard -10 to 10 scale. Numbers are sorted
- * lowest to highest, but are not necessarily sequential. Numbers may be skipped
- * when a term has children so that reordering is minimal when a child is
- * added or removed from a term.
- *
- * @see taxonomy_overview_terms()
- */
-function taxonomy_overview_terms_submit($form, &$form_state) {
- if ($form_state['triggering_element']['#value'] == t('Reset to alphabetical')) {
- // Execute the reset action.
- if ($form_state['values']['reset_alphabetical'] === TRUE) {
- return taxonomy_vocabulary_confirm_reset_alphabetical_submit($form, $form_state);
- }
- // Rebuild the form to confirm the reset action.
- $form_state['rebuild'] = TRUE;
- $form_state['confirm_reset_alphabetical'] = TRUE;
- return;
- }
-
- // Sort term order based on weight.
- uasort($form_state['values']['terms'], 'drupal_sort_weight');
-
- $vocabulary = $form_state['taxonomy']['vocabulary'];
- // Update the current hierarchy type as we go.
- $hierarchy = TAXONOMY_HIERARCHY_DISABLED;
-
- $changed_terms = array();
- $tree = taxonomy_get_tree($vocabulary->id());
-
- if (empty($tree)) {
- return;
- }
-
- // Build a list of all terms that need to be updated on previous pages.
- $weight = 0;
- $term = (array) $tree[0];
- while ($term['tid'] != $form['#first_tid']) {
- if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
- $term['parent'] = $term['parents'][0];
- $term['weight'] = $weight;
- $changed_terms[$term['tid']] = $term;
- }
- $weight++;
- $hierarchy = $term['parents'][0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
- $term = (array) $tree[$weight];
- }
-
- // Renumber the current page weights and assign any new parents.
- $level_weights = array();
- foreach ($form_state['values']['terms'] as $tid => $values) {
- if (isset($form['terms'][$tid]['#term'])) {
- $term = $form['terms'][$tid]['#term'];
- // Give terms at the root level a weight in sequence with terms on previous pages.
- if ($values['term']['parent'] == 0 && $term['weight'] != $weight) {
- $term['weight'] = $weight;
- $changed_terms[$term['tid']] = $term;
- }
- // Terms not at the root level can safely start from 0 because they're all on this page.
- elseif ($values['term']['parent'] > 0) {
- $level_weights[$values['term']['parent']] = isset($level_weights[$values['term']['parent']]) ? $level_weights[$values['term']['parent']] + 1 : 0;
- if ($level_weights[$values['term']['parent']] != $term['weight']) {
- $term['weight'] = $level_weights[$values['term']['parent']];
- $changed_terms[$term['tid']] = $term;
- }
- }
- // Update any changed parents.
- if ($values['term']['parent'] != $term['parent']) {
- $term['parent'] = $values['term']['parent'];
- $changed_terms[$term['tid']] = $term;
- }
- $hierarchy = $term['parent'] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
- $weight++;
- }
- }
-
- // Build a list of all terms that need to be updated on following pages.
- for ($weight; $weight < count($tree); $weight++) {
- $term = (array) $tree[$weight];
- if ($term['parents'][0] == 0 && $term['weight'] != $weight) {
- $term['parent'] = $term['parents'][0];
- $term['weight'] = $weight;
- $changed_terms[$term['tid']] = $term;
- }
- $hierarchy = $term['parents'][0] != 0 ? TAXONOMY_HIERARCHY_SINGLE : $hierarchy;
- }
-
- // Save all updated terms.
- foreach ($changed_terms as $changed) {
- $term = (object) $changed;
- // Update term_hierachy and term_data directly since we don't have a
- // fully populated term object to save.
- db_update('taxonomy_term_hierarchy')
- ->fields(array('parent' => $term->parent))
- ->condition('tid', $term->tid, '=')
- ->execute();
-
- db_update('taxonomy_term_data')
- ->fields(array('weight' => $term->weight))
- ->condition('tid', $term->tid, '=')
- ->execute();
- }
-
- // Update the vocabulary hierarchy to flat or single hierarchy.
- if ($vocabulary->hierarchy != $hierarchy) {
- $vocabulary->hierarchy = $hierarchy;
- taxonomy_vocabulary_save($vocabulary);
- }
- drupal_set_message(t('The configuration options have been saved.'));
-}
-
-/**
* Returns a rendered edit form to create a new term associated to the given vocabulary.
*/
function taxonomy_term_add($vocabulary) {
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 89f334b..48f7b1a 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -327,13 +327,7 @@ function taxonomy_menu() {
);
$items['admin/structure/taxonomy/%taxonomy_vocabulary'] = array(
- 'title callback' => 'entity_page_label',
- 'title arguments' => array(3),
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('taxonomy_overview_terms', 3),
- 'access callback' => 'entity_page_access',
- 'access arguments' => array(3, 'view'),
- 'file' => 'taxonomy.admin.inc',
+ 'route_name' => 'taxonomy_overview_terms',
);
$items['admin/structure/taxonomy/%taxonomy_vocabulary/list'] = array(
'title' => 'List',
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
new file mode 100644
index 0000000..f886eb3
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -0,0 +1,8 @@
+taxonomy_overview_terms:
+ pattern: admin/structure/taxonomy/{taxonomy_vocabulary}
+ defaults:
+ _form: 'Drupal\taxonomy\Form\OverviewTerms'
+ requirements:
+ _entity_access: 'taxonomy_vocabulary.view'
+ # Don't allow add or list.
+ taxonomy_vocabulary: "^(?:(?!add|list).)*"
diff --git a/core/modules/text/config/schema/text.schema.yml b/core/modules/text/config/schema/text.schema.yml
index 4fbe45a..0dd0cf9 100644
--- a/core/modules/text/config/schema/text.schema.yml
+++ b/core/modules/text/config/schema/text.schema.yml
@@ -7,3 +7,140 @@ text.settings:
default_summary_length:
type: integer
label: 'Default summary length'
+
+field.text.settings:
+ type: mapping
+ label: 'Text settings'
+ mapping:
+ max_length:
+ type: integer
+ label: 'Maximum length'
+
+field.text.instance_settings:
+ type: mapping
+ label: 'Text settings'
+ mapping:
+ text_processing:
+ type: string
+ label: 'Text processing'
+ user_register_form:
+ type: boolean
+ label: 'Display on user registration form.'
+
+field.text.value:
+ type: sequence
+ label: 'Default value'
+ sequence:
+ - type: mapping
+ label: 'Default'
+ mapping:
+ value:
+ type: label
+ label: 'Value'
+ format:
+ type: string
+ label: 'Text format'
+
+field.text_long.settings:
+ type: sequence
+ label: 'Settings'
+ sequence:
+ - type: string
+
+field.text_long.instance_settings:
+ type: mapping
+ label: 'Long text settings'
+ mapping:
+ text_processing:
+ type: string
+ label: 'Text processing'
+ user_register_form:
+ type: boolean
+ label: 'Display on user registration form.'
+
+field.text_long.value:
+ type: sequence
+ label: 'Default value'
+ sequence:
+ - type: mapping
+ label: 'Default'
+ mapping:
+ value:
+ type: text
+ label: 'Value'
+ format:
+ type: string
+ label: 'Text format'
+
+field.text_with_summary.settings:
+ type: sequence
+ label: 'Default'
+ sequence:
+ - type: string
+
+field.text_with_summary.instance_settings:
+ type: mapping
+ label: 'Text area with a summary'
+ mapping:
+ text_processing:
+ type: boolean
+ label: 'Text processing'
+ display_summary:
+ type: boolean
+ label: 'Summary input'
+ user_register_form:
+ type: boolean
+ label: 'Display on user registration form.'
+
+field.text_with_summary.value:
+ type: sequence
+ label: 'Default value'
+ sequence:
+ - type: mapping
+ label: 'Default'
+ mapping:
+ value:
+ type: text
+ label: 'Body'
+ summary:
+ type: string
+ label: 'Summary'
+ format:
+ type: string
+ label: 'Text format'
+
+field_widget.text_textfield.settings:
+ type: mapping
+ label: 'Text field widget settings'
+ mapping:
+ size:
+ type: integer
+ label: 'Size of textfield'
+ placeholder:
+ type: label
+ label: 'Placeholder'
+
+field_widget.text_textarea.settings:
+ type: mapping
+ label: 'Long text widget settings'
+ mapping:
+ rows:
+ type: integer
+ label: 'Rows'
+ placeholder:
+ type: label
+ label: 'Placeholder'
+
+field_widget.text_textarea_with_summary.settings:
+ type: mapping
+ label: 'Text area widget settings'
+ mapping:
+ rows:
+ type: integer
+ label: 'Rows'
+ summary_rows:
+ type: integer
+ label: 'Summary rows'
+ placeholder:
+ type: label
+ label: 'Placeholder'
diff --git a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
index 2000ffd..7126384 100644
--- a/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/Formatter/TextPlainUnitTest.php
@@ -45,6 +45,8 @@ public static function getInfo() {
function setUp() {
parent::setUp();
+ $this->installConfig(array('field'));
+
// @todo Add helper methods for all of the following.
$this->entity_type = 'test_entity';
diff --git a/core/modules/toolbar/toolbar.install b/core/modules/toolbar/toolbar.install
index dc65611..6732bb9 100644
--- a/core/modules/toolbar/toolbar.install
+++ b/core/modules/toolbar/toolbar.install
@@ -6,35 +6,17 @@
*/
/**
- * Implements hook_schema().
- */
-function toolbar_schema() {
- $schema['cache_toolbar'] = drupal_get_schema_unprocessed('system', 'cache');
- $schema['cache_toolbar']['description'] = 'Cache table for the Toolbar module to store per-user hashes of rendered toolbar subtrees.';
- return $schema;
-}
-
-/**
* @addtogroup updates-7.x-to-8.x
* @{
*/
/**
- * Creates the {cache_toolbar} cache table.
- */
-function toolbar_update_8000() {
- $schema['cache_toolbar'] = drupal_get_schema_unprocessed('system', 'cache');
- $schema['cache_toolbar']['description'] = 'Cache table for the Toolbar module to store per-user hashes of rendered toolbar subtrees.';
- db_create_table('cache_toolbar', $schema['cache_toolbar']);
-}
-
-/**
* Enable the Breakpoint and Config modules.
*
* The 7.x version of the Toolbar module had no dependencies. The 8.x version
* depends on the Breakpoint and Configuration manager modules.
*/
-function toolbar_update_8001() {
+function toolbar_update_8000() {
// Enable the modules without re-installing the schema.
update_module_enable(array('breakpoint'));
}
diff --git a/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php b/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php
index 1f83011..c53ff76 100644
--- a/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php
+++ b/core/modules/tour/lib/Drupal/tour/Plugin/Core/Entity/Tour.php
@@ -19,8 +19,10 @@
* id = "tour",
* label = @Translation("Tour"),
* module = "tour",
- * controller_class = "Drupal\Core\Config\Entity\ConfigStorageController",
- * render_controller_class = "Drupal\tour\TourRenderController",
+ * controllers = {
+ * "storage" = "Drupal\Core\Config\Entity\ConfigStorageController",
+ * "render" = "Drupal\tour\TourRenderController"
+ * },
* config_prefix = "tour.tour",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php
index aee3c67..dbac33b 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationControllerInterface.php
@@ -36,7 +36,7 @@
* path wildcard' info key needs to be defined.
*
* Every entity type needs a translation controller to be translated. This can
- * be specified through the 'translation_controller_class' key in the entity
+ * be specified through the "controllers['translation']" key in the entity
* info. If an entity type is enabled for translation and no translation
* controller is defined, Drupal\translation_entity\EntityTranslationController
* will be assumed. Every translation controller class must implement
@@ -63,7 +63,6 @@
* $info['myentity'] += array(
* 'menu_base_path' => 'mymodule/myentity/%my_entity_loader',
* 'menu_path_wildcard' => '%my_entity_loader',
- * 'translation_controller_class' => 'Drupal\mymodule\MyEntityTranslationController',
* 'translation' => array(
* 'translation_entity' => array(
* 'access_callback' => 'mymodule_myentity_translate_access',
@@ -71,6 +70,7 @@
* ),
* ),
* );
+ * $info['myentity']['controllers'] += array('translation' => 'Drupal\mymodule\MyEntityTranslationController');
* }
* @endcode
*
diff --git a/core/modules/translation_entity/translation_entity.admin.js b/core/modules/translation_entity/translation_entity.admin.js
index 5be6606..2b934e8 100644
--- a/core/modules/translation_entity/translation_entity.admin.js
+++ b/core/modules/translation_entity/translation_entity.admin.js
@@ -39,7 +39,7 @@ Drupal.behaviors.translationEntityDependentOptions = {
if(!$element) {
$fields.each(function() {
- if($(this).val() == $column) {
+ if($(this).val() === $column) {
$element = $(this);
return false;
}
diff --git a/core/modules/translation_entity/translation_entity.module b/core/modules/translation_entity/translation_entity.module
index cb75cb2..e4dca3e 100644
--- a/core/modules/translation_entity/translation_entity.module
+++ b/core/modules/translation_entity/translation_entity.module
@@ -86,7 +86,7 @@ function translation_entity_entity_info_alter(array &$entity_info) {
// matter if it is enabled for translation or not. As a matter of fact we
// might need it to correctly switch field translatability when a field is
// shared accross different entities.
- $info += array('translation_controller_class' => 'Drupal\translation_entity\EntityTranslationController');
+ $info['controllers'] += array('translation' => 'Drupal\translation_entity\EntityTranslationController');
// If no menu base path is provided we default to the usual
// "entity_type/%entity_type" pattern.
@@ -504,7 +504,7 @@ function translation_entity_types_translatable() {
function translation_entity_controller($entity_type) {
$entity_info = entity_get_info($entity_type);
// @todo Throw an exception if the key is missing.
- return new $entity_info['translation_controller_class']($entity_type, $entity_info);
+ return new $entity_info['controllers']['translation']($entity_type, $entity_info);
}
/**
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php
index ed75a43..662d365 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/Role.php
@@ -18,7 +18,9 @@
* id = "user_role",
* label = @Translation("Role"),
* module = "user",
- * controller_class = "Drupal\user\RoleStorageController",
+ * controllers = {
+ * "storage" = "Drupal\user\RoleStorageController"
+ * },
* config_prefix = "user.role",
* entity_keys = {
* "id" = "id",
diff --git a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
index 5b167f4..829babc 100644
--- a/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
+++ b/core/modules/user/lib/Drupal/user/Plugin/Core/Entity/User.php
@@ -18,15 +18,17 @@
* id = "user",
* label = @Translation("User"),
* module = "user",
- * controller_class = "Drupal\user\UserStorageController",
- * render_controller_class = "Drupal\Core\Entity\EntityRenderController",
- * access_controller_class = "Drupal\user\UserAccessController",
- * form_controller_class = {
- * "profile" = "Drupal\user\ProfileFormController",
- * "register" = "Drupal\user\RegisterFormController"
+ * controllers = {
+ * "storage" = "Drupal\user\UserStorageController",
+ * "access" = "Drupal\user\UserAccessController",
+ * "render" = "Drupal\Core\Entity\EntityRenderController",
+ * "form" = {
+ * "profile" = "Drupal\user\ProfileFormController",
+ * "register" = "Drupal\user\RegisterFormController"
+ * },
+ * "translation" = "Drupal\user\ProfileTranslationController"
* },
* default_operation = "profile",
- * translation_controller_class = "Drupal\user\ProfileTranslationController",
* base_table = "users",
* uri_callback = "user_uri",
* label_callback = "user_label",
diff --git a/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
index 8fface7..b8ca3bc 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/Core/Entity/View.php
@@ -22,13 +22,15 @@
* id = "view",
* label = @Translation("View"),
* module = "views",
- * controller_class = "Drupal\views\ViewStorageController",
- * list_controller_class = "Drupal\views_ui\ViewListController",
- * form_controller_class = {
- * "edit" = "Drupal\views_ui\ViewEditFormController",
- * "add" = "Drupal\views_ui\ViewAddFormController",
- * "preview" = "Drupal\views_ui\ViewPreviewFormController",
- * "clone" = "Drupal\views_ui\ViewCloneFormController"
+ * controllers = {
+ * "storage" = "Drupal\views\ViewStorageController",
+ * "list" = "Drupal\views_ui\ViewListController",
+ * "form" = {
+ * "edit" = "Drupal\views_ui\ViewEditFormController",
+ * "add" = "Drupal\views_ui\ViewAddFormController",
+ * "preview" = "Drupal\views_ui\ViewPreviewFormController",
+ * "clone" = "Drupal\views_ui\ViewCloneFormController"
+ * }
* },
* config_prefix = "views.view",
* fieldable = FALSE,
diff --git a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php
index 9cf0134..465ae17 100644
--- a/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php
+++ b/core/modules/views/lib/Drupal/views/Tests/Handler/AreaEntityTest.php
@@ -53,7 +53,7 @@ public function testEntityAreaData() {
$entity_info = $this->container->get('plugin.manager.entity')->getDefinitions();
$expected_entities = array_filter($entity_info, function($info) {
- return !empty($info['render_controller_class']);
+ return !empty($info['controllers']['render']);
});
// Test that all expected entity types have data.
@@ -64,7 +64,7 @@ public function testEntityAreaData() {
}
$expected_entities = array_filter($entity_info, function($info) {
- return empty($info['render_controller_class']);
+ return empty($info['controllers']['render']);
});
// Test that no configuration entity types have data.
diff --git a/core/modules/views/views.install b/core/modules/views/views.install
index 8bda10e..aea8d0c 100644
--- a/core/modules/views/views.install
+++ b/core/modules/views/views.install
@@ -15,19 +15,6 @@ function views_install() {
}
/**
- * Implements hook_schema().
- */
-function views_schema() {
- $schema['cache_views_info'] = drupal_get_schema_unprocessed('system', 'cache');
-
- $schema['cache_views_results'] = drupal_get_schema_unprocessed('system', 'cache');
- $schema['cache_views_results']['description'] = 'Cache table for views to store pre-rendered queries, results, and display output.';
- $schema['cache_views_results']['fields']['serialized']['default'] = 1;
-
- return $schema;
-}
-
-/**
* Provide an initial schema.
*
* @see update_module_enable().
diff --git a/core/modules/views/views.views.inc b/core/modules/views/views.views.inc
index f2178ab..99d23c1 100644
--- a/core/modules/views/views.views.inc
+++ b/core/modules/views/views.views.inc
@@ -109,7 +109,7 @@ function views_views_data() {
// Registers an entity area handler per entity type.
foreach (entity_get_info() as $entity_type => $entity_info) {
// Exclude entity types, which cannot be rendered.
- if (!empty($entity_info['render_controller_class'])) {
+ if (!empty($entity_info['controllers']['render'])) {
$label = $entity_info['label'];
$data['views']['entity_' . $entity_type] = array(
'title' => t('Rendered entity - @label', array('@label' => $label)),