Update to Drupal 8.0.0-beta15. For more information, see: https://www.drupal.org/node/2563023

This commit is contained in:
Pantheon Automation 2015-09-04 13:20:09 -07:00 committed by Greg Anderson
parent 2720a9ec4b
commit f3791f1da3
1898 changed files with 54300 additions and 11481 deletions

View file

@ -166,8 +166,9 @@ class Extension implements \Serializable {
* Serializes the Extension object in the most optimized way.
*/
public function serialize() {
// Don't serialize the app root, since this could change if the install is
// moved.
$data = array(
'root' => $this->root,
'type' => $this->type,
'pathname' => $this->pathname,
'filename' => $this->filename,
@ -188,7 +189,8 @@ class Extension implements \Serializable {
*/
public function unserialize($data) {
$data = unserialize($data);
$this->root = $data['root'];
// Get the app root from the container.
$this->root = DRUPAL_ROOT;
$this->type = $data['type'];
$this->pathname = $data['pathname'];
$this->filename = $data['filename'];

View file

@ -7,14 +7,10 @@
namespace Drupal\Core\Extension;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Utility\SafeMarkup;
/**
* Parses extension .info.yml files.
*/
class InfoParser implements InfoParserInterface {
class InfoParser extends InfoParserDynamic {
/**
* Array of all info keyed by filename.
@ -28,38 +24,9 @@ class InfoParser implements InfoParserInterface {
*/
public function parse($filename) {
if (!isset(static::$parsedInfos[$filename])) {
if (!file_exists($filename)) {
static::$parsedInfos[$filename] = array();
}
else {
try {
static::$parsedInfos[$filename] = Yaml::decode(file_get_contents($filename));
}
catch (InvalidDataTypeException $e) {
$message = SafeMarkup::format("Unable to parse !file: !error", array('!file' => $filename, '!error' => $e->getMessage()));
throw new InfoParserException($message);
}
$missing_keys = array_diff($this->getRequiredKeys(), array_keys(static::$parsedInfos[$filename]));
if (!empty($missing_keys)) {
$message = SafeMarkup::format('Missing required keys (!missing_keys) in !file.', array('!missing_keys' => implode(', ', $missing_keys), '!file' => $filename));
throw new InfoParserException($message);
}
if (isset(static::$parsedInfos[$filename]['version']) && static::$parsedInfos[$filename]['version'] === 'VERSION') {
static::$parsedInfos[$filename]['version'] = \Drupal::VERSION;
}
}
static::$parsedInfos[$filename] = parent::parse($filename);
}
return static::$parsedInfos[$filename];
}
/**
* Returns an array of keys required to exist in .info.yml file.
*
* @return array
* An array of required keys.
*/
protected function getRequiredKeys() {
return array('type', 'core', 'name');
}
}

View file

@ -0,0 +1,56 @@
<?php
/**
* @file
* Contains \Drupal\Core\Extension\InfoParserDynamic.
*/
namespace Drupal\Core\Extension;
use Drupal\Component\Serialization\Yaml;
use Drupal\Component\Serialization\Exception\InvalidDataTypeException;
use Drupal\Component\Utility\SafeMarkup;
/**
* Parses dynamic .info.yml files that might change during the page request.
*/
class InfoParserDynamic implements InfoParserInterface {
/**
* {@inheritdoc}
*/
public function parse($filename) {
if (!file_exists($filename)) {
$parsed_info = array();
}
else {
try {
$parsed_info = Yaml::decode(file_get_contents($filename));
}
catch (InvalidDataTypeException $e) {
$message = SafeMarkup::format("Unable to parse !file: !error", array('!file' => $filename, '!error' => $e->getMessage()));
throw new InfoParserException($message);
}
$missing_keys = array_diff($this->getRequiredKeys(), array_keys($parsed_info));
if (!empty($missing_keys)) {
$message = SafeMarkup::format('Missing required keys (!missing_keys) in !file.', array('!missing_keys' => implode(', ', $missing_keys), '!file' => $filename));
throw new InfoParserException($message);
}
if (isset($parsed_info['version']) && $parsed_info['version'] === 'VERSION') {
$parsed_info['version'] = \Drupal::VERSION;
}
}
return $parsed_info;
}
/**
* Returns an array of keys required to exist in .info.yml file.
*
* @return array
* An array of required keys.
*/
protected function getRequiredKeys() {
return array('type', 'core', 'name');
}
}

View file

@ -10,9 +10,9 @@ namespace Drupal\Core\Extension;
use Drupal\Component\Serialization\Yaml;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\PreExistingConfigException;
use Drupal\Core\Config\StorageInterface;
use Drupal\Core\DrupalKernelInterface;
use Drupal\Core\Entity\EntityStorageException;
use Drupal\Core\Entity\FieldableEntityInterface;
/**
* Default implementation of the module installer.
@ -212,14 +212,34 @@ class ModuleInstaller implements ModuleInstallerInterface {
$version = max(max($versions), $version);
}
// Notify interested components that this module's entity types are new.
// For example, a SQL-based storage handler can use this as an
// opportunity to create the necessary database tables.
// Notify interested components that this module's entity types and
// field storage definitions are new. For example, a SQL-based storage
// handler can use this as an opportunity to create the necessary
// database tables.
// @todo Clean this up in https://www.drupal.org/node/2350111.
$entity_manager = \Drupal::entityManager();
$update_manager = \Drupal::entityDefinitionUpdateManager();
foreach ($entity_manager->getDefinitions() as $entity_type) {
if ($entity_type->getProvider() == $module) {
$entity_manager->onEntityTypeCreate($entity_type);
$update_manager->installEntityType($entity_type);
}
elseif ($entity_type->isSubclassOf(FieldableEntityInterface::CLASS)) {
// The module being installed may be adding new fields to existing
// entity types. Field definitions for any entity type defined by
// the module are handled in the if branch.
foreach ($entity_manager->getFieldStorageDefinitions($entity_type->id()) as $storage_definition) {
if ($storage_definition->getProvider() == $module) {
// If the module being installed is also defining a storage key
// for the entity type, the entity schema may not exist yet. It
// will be created later in that case.
try {
$update_manager->installFieldStorageDefinition($storage_definition->getName(), $entity_type->id(), $module, $storage_definition);
}
catch (EntityStorageException $e) {
watchdog_exception('system', $e, 'An error occurred while notifying the creation of the @name field storage definition: "!message" in %function (line %line of %file).', ['@name' => $storage_definition->getName()]);
}
}
}
}
}
@ -362,9 +382,25 @@ class ModuleInstaller implements ModuleInstallerInterface {
// deleted. For example, a SQL-based storage handler can use this as an
// opportunity to drop the corresponding database tables.
// @todo Clean this up in https://www.drupal.org/node/2350111.
$update_manager = \Drupal::entityDefinitionUpdateManager();
foreach ($entity_manager->getDefinitions() as $entity_type) {
if ($entity_type->getProvider() == $module) {
$entity_manager->onEntityTypeDelete($entity_type);
$update_manager->uninstallEntityType($entity_type);
}
elseif ($entity_type->isSubclassOf(FieldableEntityInterface::CLASS)) {
// The module being installed may be adding new fields to existing
// entity types. Field definitions for any entity type defined by
// the module are handled in the if branch.
$entity_type_id = $entity_type->id();
/** @var \Drupal\Core\Entity\FieldableEntityStorageInterface $storage */
$storage = $entity_manager->getStorage($entity_type_id);
foreach ($entity_manager->getFieldStorageDefinitions($entity_type_id) as $storage_definition) {
// @todo We need to trigger field purging here.
// See https://www.drupal.org/node/2282119.
if ($storage_definition->getProvider() == $module && !$storage->countFieldData($storage_definition, TRUE)) {
$update_manager->uninstallFieldStorageDefinition($storage_definition);
}
}
}
}

View file

@ -5,8 +5,55 @@
* Hooks related to module and update systems.
*/
use Drupal\Core\Utility\UpdateException;
use Drupal\Core\Database\Database;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Url;
use Drupal\Core\Utility\UpdateException;
/**
* @defgroup update_api Update API
* @{
* Updating minor versions of modules
*
* When you update code in a module, you may need to update stored data so that
* the stored data is compatible with the new code. If this update is between
* two minor versions of your module within the same major version of Drupal,
* you can use the Update API to update the data. This API is described in brief
* here; for more details, see https://www.drupal.org/node/2535316. If you are
* updating your module for a major version of Drupal (for instance, Drupal 7 to
* Drupal 8), updates will not run and you will need to use the
* @link migrate Migrate API @endlink instead.
*
* @section sec_when When to write update code
* You need to provide code that performs an update to stored data whenever your
* module makes a change to its data model. A data model change is any change
* that makes stored data on an existing site incompatible with that site's
* updated codebase. Examples:
* - Configuration changes: adding/removing/renaming a config key, changing the
* expected data type or value structure, changing dependencies, schema
* changes, etc.
* - Database schema changes: adding, changing, or removing a database table or
* field; moving stored data to different fields or tables; changing the
* format of stored data.
* - Content entity or field changes: adding, changing, or removing a field
* definition, entity definition, or any of their properties.
*
* @section sec_how How to write update code
* Update code for a module is put into an implementation of hook_update_N(),
* which goes into file mymodule.install (if your module's machine name is
* mymodule). See the documentation of hook_update_N() and
* https://www.drupal.org/node/2535316 for details and examples.
*
* @section sec_test Testing update code
* Update code should be tested both manually and by writing an automated test.
* Automated tests for update code extend
* \Drupal\system\Tests\Update\UpdatePathTestBase -- see that class for details,
* and find classes that extend it for examples.
*
* @see migration
* @}
*/
/**
* @addtogroup hooks
@ -421,94 +468,119 @@ function hook_install_tasks_alter(&$tasks, $install_state) {
}
/**
* Perform a single update.
* Perform a single update between minor versions.
*
* For each change that requires one or more actions to be performed when
* updating a site, add a new hook_update_N(), which will be called by
* update.php. The documentation block preceding this function is stripped of
* newlines and used as the description for the update on the pending updates
* task list. Schema updates should adhere to the
* @link https://www.drupal.org/node/150215 Schema API. @endlink
* hook_update_N() can only be used to update between minor versions of a
* module. To upgrade between major versions of Drupal (for example, between
* Drupal 7 and 8), use the @link migrate Migrate API @endlink instead.
*
* @section sec_naming Naming and documenting your function
* For each change in a module that requires one or more actions to be performed
* when updating a site, add a new implementation of hook_update_N() to your
* mymodule.install file (assuming mymodule is the machine name of your module).
* Implementations of hook_update_N() are named (module name)_update_(number).
* The numbers are composed of three parts:
* - 1 digit for Drupal core compatibility.
* - 1 digit for your module's major release version (e.g., is this the 8.x-1.*
* (1) or 8.x-2.* (2) series of your module).
* - 2 digits for sequential counting, starting with 01.
*
* The numbers are normally composed of three parts:
* - 1 or 2 digits for Drupal core compatibility (Drupal 8, 9, 10, etc.). This
* convention must be followed.
* - 1 digit for your module's major release version; for example, for 8.x-1.*
* use 1, for 8.x-2.* use 2, for Core 8.0.x use 0, and for Core 8.1.x use 1.
* This convention is optional but suggested for clarity.
* - 2 digits for sequential counting, starting with 01. Note that the x000
* number can never be used: the lowest update number that will be recognized
* and run for major version x is x001.
* Examples:
* - mymodule_update_8100(): This is the first update to get the database ready
* to run mymodule 8.x-1.*.
* - mymodule_update_8200(): This is the first update to get the database ready
* to run mymodule 8.x-2.*.
* - node_update_8001(): The first update for the Drupal 8.0.x version of the
* Drupal Core node module.
* - mymodule_update_8101(): The first update for your custom or contributed
* module's 8.x-1.x versions.
* - mymodule_update_8201(): The first update for the 8.x-2.x versions.
*
* As of Drupal 8.0, the database upgrade system no longer supports updating a
* database from an earlier major version of Drupal: update.php can be used to
* upgrade from 7.x-1.x to 7.x-2.x, or 8.x-1.x to 8.x-2.x, but not from 7.x to
* 8.x. Therefore, only update hooks numbered 8001 or later will run for
* Drupal 8. 8000 is reserved for the minimum core schema version and defining
* mymodule_update_8000() will result in an exception. Use the
* @link https://www.drupal.org/node/2127611 Migration API @endlink instead to
* migrate data from an earlier major version of Drupal.
* Never renumber update functions. The numeric part of the hook implementation
* function is stored in the database to keep track of which updates have run,
* so it is important to maintain this information consistently.
*
* For further information about releases and release numbers see:
* @link https://www.drupal.org/node/711070 Maintaining a drupal.org project
* with Git @endlink
* The documentation block preceding this function is stripped of newlines and
* used as the description for the update on the pending updates task list,
* which users will see when they run the update.php script.
*
* Never renumber update functions.
* @section sec_notes Notes about the function body
* Writing hook_update_N() functions is tricky. There are several reasons why
* this is the case:
* - You do not know when updates will be run: someone could be keeping up with
* every update and run them when the database and code are in the same state
* as when you wrote your update function, or they could have waited until a
* few more updates have come out, and run several at the same time.
* - You do not know the state of other modules' updates either.
* - Other modules can use hook_update_dependencies() to run updates between
* your module's updates, so you also cannot count on your functions running
* right after one another.
* - You do not know what environment your update will run in (which modules
* are installed, whether certain hooks are implemented or not, whether
* services are overridden, etc.).
*
* Implementations of this hook should be placed in a mymodule.install file in
* the same directory as mymodule.module. Drupal core's updates are implemented
* using the system module as a name and stored in database/updates.inc.
* Because of these reasons, you'll need to use care in writing your update
* function. Some things to think about:
* - Never assume that the database schema is the same when the update will run
* as it is when you wrote the update function. So, when updating a database
* table or field, put the schema information you want to update to directly
* into your function instead of calling your hook_schema() function to
* retrieve it (this is one case where the right thing to do is copy and paste
* the code).
* - Never assume that the configuration schema is the same when the update will
* run as it is when you wrote the update function. So, when saving
* configuration, use the $has_trusted_data = TRUE parameter so that schema is
* ignored, and make sure that the configuration data you are saving matches
* the configuration schema at the time when you write the update function
* (later updates may change it again to match new schema changes).
* - Never assume your field or entity type definitions are the same when the
* update will run as they are when you wrote the update function. Always
* retrieve the correct version via
* \Drupal::entityDefinitionUpdateManager()::getEntityType() or
* \Drupal::entityDefinitionUpdateManager()::getFieldStorageDefinition(). When
* adding a new definition always replicate it in the update function body as
* you would do with a schema definition.
* - Never call \Drupal::entityDefinitionUpdateManager()::applyUpdates() in an
* update function, as it will apply updates for any module not only yours,
* which will lead to unpredictable results.
* - Be careful about API functions and especially CRUD operations that you use
* in your update function. If they invoke hooks or use services, they may
* not behave as expected, and it may actually not be appropriate to use the
* normal API functions that invoke all the hooks, use the database schema,
* and/or use services in an update function -- you may need to switch to
* using a more direct method (database query, etc.).
* - In particular, loading, saving, or performing any other CRUD operation on
* an entity is never safe to do (they always involve hooks and services).
* - Never rebuild the router during an update function.
*
* Not all module functions are available from within a hook_update_N() function.
* In order to call a function from your mymodule.module or an include file,
* you need to explicitly load that file first.
*
* Implementations must ensure that APIs used are safe during updates. During
* database updates the schema of any module could be out of date. For this
* reason, caution is needed when using any API function within an update
* function - particularly CRUD functions, functions that depend on the schema
* (for example by using \Drupal\Core\Entity\Entity::save()), and any functions
* that invoke hooks.
*
* The following actions are examples that are safe:
* The following actions are examples of things that are safe to do during
* updates:
* - Cache invalidation.
* - Using \Drupal::configFactory()->getEditable() and \Drupal::config().
* Implementations must:
* - Not make any assumption that the config data is valid.
* - Use the correct data type when changing configuration values as specified
* by its configuration schema at the time the update hook is written. If
* the data type changes in a subsequent code change, a subsequent update
* hook is responsible for ensuring the final data type aligns with the
* configuration schema.
* - Use the $has_trusted_data argument for \Drupal\Core\Config\Config::save()
* so that configuration schemas are not used whilst saving configuration.
* - Using \Drupal::configFactory()->getEditable() and \Drupal::config(), as
* long as you make sure that your update data matches the schema, and you
* use the $has_trusted_data argument in the save operation.
* - Marking a container for rebuild.
* - Using the API provided by \Drupal::entityDefinitionUpdateManager() to
* update the entity schema based on changes in entity type or field
* definitions provided by your module.
*
* The following actions are examples that are unsafe:
* - Loading, saving, or performing any other operation on an entity.
* - Rebuilding the router using \Drupal::service('router.builder')->rebuild().
* See https://www.drupal.org/node/2535316 for more on writing update functions.
*
* The $sandbox parameter should be used when a multipass update is needed, in
* circumstances where running the whole update at once could cause PHP to
* timeout. Each pass is run in a way that avoids PHP timeouts, provided each
* pass remains under the timeout limit. To signify that an update requires
* at least one more pass, set $sandbox['#finished'] to a number less than 1
* (you need to do this each pass). The value of $sandbox['#finished'] will be
* unset between passes but all other data in $sandbox will be preserved. The
* system will stop iterating this update when $sandbox['#finished'] is left
* unset or set to a number higher than 1. It is recommended that
* $sandbox['#finished'] is initially set to 0, and then updated each pass to a
* number between 0 and 1 that represents the overall % completed for this
* update, finishing with 1.
* @section sec_bulk Batch updates
* If running your update all at once could possibly cause PHP to time out, use
* the $sandbox parameter to indicate that the Batch API should be used for your
* update. In this case, your update function acts as an implementation of
* callback_batch_operation(), and $sandbox acts as the batch context
* parameter. In your function, read the state information from the previous
* run from $sandbox (or initialize), run a chunk of updates, save the state in
* $sandbox, and set $sandbox['#finished'] to a value between 0 and 1 to
* indicate the percent completed, or 1 if it is finished (you need to do this
* explicitly in each pass).
*
* See the @link batch Batch operations topic @endlink for more information on
* how to use the Batch API.
*
* @param array $sandbox
* Stores information for multipass updates. See above for more information.
* Stores information for batch updates. See above for more information.
*
* @throws \Drupal\Core\Utility\UpdateException|PDOException
* In case of error, update hooks should throw an instance of
@ -521,58 +593,70 @@ function hook_install_tasks_alter(&$tasks, $install_state) {
* displayed to the user after the update has completed. If no message is
* returned, no message will be presented to the user.
*
* @ingroup update_api
*
* @see batch
* @see schemaapi
* @see hook_update_last_removed()
* @see update_get_update_list()
* @see \Drupal\Core\Entity\EntityDefinitionUpdateManagerInterface
* @see node_update_8001
* @see system_update_8004
* @see https://www.drupal.org/node/2535316
*/
function hook_update_N(&$sandbox) {
// For non-multipass updates, the signature can simply be;
// For non-batch updates, the signature can simply be:
// function hook_update_N() {
// For most updates, the following is sufficient.
db_add_field('mytable1', 'newcol', array('type' => 'int', 'not null' => TRUE, 'description' => 'My new integer column.'));
// Example function body for adding a field to a database table, which does
// not require a batch operation:
$spec = array(
'type' => 'varchar',
'description' => "New Col",
'length' => 20,
'not null' => FALSE,
);
$schema = Database::getConnection()->schema();
$schema->addField('mytable1', 'newcol', $spec);
// However, for more complex operations that may take a long time,
// you may hook into Batch API as in the following example.
// Update 3 users at a time to have an exclamation point after their names.
// (They're really happy that we can do batch API in this hook!)
if (!isset($sandbox['progress'])) {
$sandbox['progress'] = 0;
$sandbox['current_uid'] = 0;
// We'll -1 to disregard the uid 0...
$sandbox['max'] = db_query('SELECT COUNT(DISTINCT uid) FROM {users}')->fetchField() - 1;
// Example of what to do if there is an error during your update.
if ($some_error_condition_met) {
throw new UpdateException('Something went wrong; here is what you should do.');
}
$users = db_select('users', 'u')
->fields('u', array('uid', 'name'))
->condition('uid', $sandbox['current_uid'], '>')
->range(0, 3)
->orderBy('uid', 'ASC')
->execute();
// Example function body for a batch update. In this example, the values in
// a database field are updated.
if (!isset($sandbox['progress'])) {
// This must be the first run. Initialize the sandbox.
$sandbox['progress'] = 0;
$sandbox['current_pk'] = 0;
$sandbox['max'] = Database::getConnection()->query('SELECT COUNT(myprimarykey) FROM {mytable1}')->fetchField() - 1;
}
foreach ($users as $user) {
$user->setUsername($user->getUsername() . '!');
db_update('users')
->fields(array('name' => $user->getUsername()))
->condition('uid', $user->id())
// Update in chunks of 20.
$records = Database::getConnection()->select('mytable1', 'm')
->fields('m', array('myprimarykey', 'otherfield'))
->condition('myprimarykey', $sandbox['current_pk'], '>')
->range(0, 20)
->orderBy('myprimarykey', 'ASC')
->execute();
foreach ($records as $record) {
// Here, you would make an update something related to this record. In this
// example, some text is added to the other field.
Database::getConnection()->update('mytable1')
->fields(array('otherfield' => $record->otherfield . '-suffix'))
->condition('myprimarykey', $record->myprimarykey)
->execute();
$sandbox['progress']++;
$sandbox['current_uid'] = $user->id();
$sandbox['current_pk'] = $record->myprimarykey;
}
$sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']);
if ($some_error_condition_met) {
// In case of an error, simply throw an exception with an error message.
throw new UpdateException('Something went wrong; here is what you should do.');
}
// To display a message to the user when the update is completed, return it.
// If you do not want to display a completion message, simply return nothing.
return t('The update did what it was supposed to do.');
// If you do not want to display a completion message, return nothing.
return t('All foo bars were updated with the new suffix');
}
/**
@ -597,6 +681,8 @@ function hook_update_N(&$sandbox) {
* you should always list the highest numbered one here (since updates within
* a given module always run in numerical order).
*
* @ingroup update_api
*
* @see update_resolve_dependencies()
* @see hook_update_N()
*/
@ -634,6 +720,8 @@ function hook_update_dependencies() {
* An integer, corresponding to hook_update_N() which has been removed from
* mymodule.install.
*
* @ingroup update_api
*
* @see hook_update_N()
*/
function hook_update_last_removed() {
@ -663,6 +751,8 @@ function hook_update_last_removed() {
* doesn't matter, but if you need to override an existing Updater, make
* sure your Updater has a lighter weight so that it comes first.
*
* @ingroup update_api
*
* @see drupal_get_updaters()
* @see hook_updater_info_alter()
*/
@ -692,6 +782,8 @@ function hook_updater_info() {
* Associative array of updaters as defined through hook_updater_info().
* Alter this array directly.
*
* @ingroup update_api
*
* @see drupal_get_updaters()
* @see hook_updater_info()
*/