2009-07-29 05:59:59 +00:00
< ? php
/**
* @ file
* Drupal database update API .
*
* This file contains functions to perform database updates for a Drupal
* installation . It is included and used extensively by update . php .
*/
2012-04-09 18:02:59 +00:00
use Drupal\Component\Graph\Graph ;
2013-03-22 09:36:55 +00:00
use Drupal\Component\Utility\Settings ;
2013-10-07 05:34:09 +00:00
use Drupal\Component\Utility\String ;
2012-06-02 19:45:56 +00:00
use Drupal\Core\Config\FileStorage ;
2012-06-29 17:35:06 +00:00
use Drupal\Core\Config\ConfigException ;
2013-01-21 19:21:34 +00:00
use Drupal\Core\DrupalKernel ;
2012-10-08 16:39:59 +00:00
use Drupal\Component\Uuid\Uuid ;
2012-11-14 10:15:58 +00:00
use Drupal\Component\Utility\NestedArray ;
2013-06-06 08:08:39 +00:00
use Symfony\Component\HttpFoundation\Request ;
2012-04-09 18:02:59 +00:00
2010-10-03 23:33:15 +00:00
/**
2011-03-21 00:28:12 +00:00
* Minimum schema version of Drupal 7 required for upgrade to Drupal 8.
2010-10-03 23:33:15 +00:00
*
2011-03-21 00:28:12 +00:00
* Upgrades from Drupal 7 to Drupal 8 require that Drupal 7 be running
2010-10-03 23:33:15 +00:00
* the most recent version , or the upgrade could fail . We can ' t easily
2011-03-21 00:28:12 +00:00
* check the Drupal 7 version once the update process has begun , so instead
2012-10-09 20:32:40 +00:00
* we check the schema version of system . module .
2010-10-03 23:33:15 +00:00
*/
2011-11-29 09:56:53 +00:00
const REQUIRED_D7_SCHEMA_VERSION = '7069' ;
2010-10-03 23:33:15 +00:00
2009-07-29 05:59:59 +00:00
/**
2012-10-09 20:32:40 +00:00
* Disables any extensions that are incompatible with the current core version .
2009-07-29 05:59:59 +00:00
*/
function update_fix_compatibility () {
2012-10-09 20:32:40 +00:00
foreach ( array ( 'module' , 'theme' ) as $type ) {
2013-09-16 03:58:06 +00:00
$config = \Drupal :: config ( " system. $type " );
2012-10-09 20:32:40 +00:00
$save = FALSE ;
foreach ( $config -> get ( 'enabled' ) as $name => $weight ) {
if ( update_check_incompatibility ( $name , $type )) {
$config -> clear ( " enabled. $name " );
$save = TRUE ;
}
}
if ( $save ) {
if ( $type == 'module' ) {
$config -> set ( 'enabled' , module_config_sort ( $config -> get ( 'enabled' )));
}
$config -> save ();
2009-07-29 05:59:59 +00:00
}
}
}
/**
2013-01-10 23:50:55 +00:00
* Tests the compatibility of a module or theme .
2009-07-29 05:59:59 +00:00
*/
function update_check_incompatibility ( $name , $type = 'module' ) {
static $themes , $modules ;
// Store values of expensive functions for future use.
if ( empty ( $themes ) || empty ( $modules )) {
2010-03-31 19:10:39 +00:00
// We need to do a full rebuild here to make sure the database reflects any
// code changes that were made in the filesystem before the update script
// was initiated.
$themes = system_rebuild_theme_data ();
2009-10-13 05:26:57 +00:00
$modules = system_rebuild_module_data ();
2009-07-29 05:59:59 +00:00
}
if ( $type == 'module' && isset ( $modules [ $name ])) {
$file = $modules [ $name ];
}
elseif ( $type == 'theme' && isset ( $themes [ $name ])) {
$file = $themes [ $name ];
}
if ( ! isset ( $file )
|| ! isset ( $file -> info [ 'core' ])
2013-09-16 03:58:06 +00:00
|| $file -> info [ 'core' ] != \Drupal :: CORE_COMPATIBILITY
2010-10-27 19:31:53 +00:00
|| version_compare ( phpversion (), $file -> info [ 'php' ]) < 0 ) {
2009-07-29 05:59:59 +00:00
return TRUE ;
}
return FALSE ;
}
/**
2011-04-01 14:08:42 +00:00
* Performs extra steps required to bootstrap when using a Drupal 7 database .
2009-12-31 13:43:36 +00:00
*
2011-04-01 14:08:42 +00:00
* Users who still have a Drupal 7 database ( and are in the process of
* updating to Drupal 8 ) need extra help before a full bootstrap can be
2009-07-29 05:59:59 +00:00
* achieved . This function does the necessary preliminary work that allows
* the bootstrap to be successful .
*
* No access check has been performed when this function is called , so no
2010-07-17 19:44:06 +00:00
* irreversible changes to the database are made here .
2009-07-29 05:59:59 +00:00
*/
2011-04-01 14:08:42 +00:00
function update_prepare_d8_bootstrap () {
2013-05-09 09:25:10 +00:00
include_once __DIR__ . '/install.inc' ;
include_once __DIR__ . '/schema.inc' ;
2012-10-30 10:08:53 +00:00
// Bootstrap to configuration.
2013-01-21 13:53:28 +00:00
drupal_bootstrap ( DRUPAL_BOOTSTRAP_CONFIGURATION );
2009-12-08 07:07:15 +00:00
2013-01-21 19:21:34 +00:00
// During the bootstrap to DRUPAL_BOOTSTRAP_PAGE_CACHE, code will try to read
// the cache but the cache tables are not compatible yet. Use the Null backend
// by default to avoid exceptions.
2013-03-22 09:36:55 +00:00
$settings = settings () -> getAll ();
$settings [ 'cache' ][ 'default' ] = 'cache.backend.memory' ;
new Settings ( $settings );
2013-01-21 19:21:34 +00:00
2013-06-28 16:11:57 +00:00
// Enable UpdateServiceProvider service overrides.
// @see update_flush_all_caches()
$GLOBALS [ 'conf' ][ 'container_service_providers' ][ 'UpdateServiceProvider' ] = 'Drupal\Core\DependencyInjection\UpdateServiceProvider' ;
2013-01-21 19:21:34 +00:00
2013-03-20 18:35:27 +00:00
// Check whether settings.php needs to be rewritten.
$settings_exist = ! empty ( $GLOBALS [ 'config_directories' ]);
if ( ! $settings_exist || ! is_dir ( config_get_config_directory ( 'active' )) || ! is_dir ( config_get_config_directory ( 'staging' ))) {
drupal_install_config_directories ();
}
2013-01-21 19:21:34 +00:00
// Bootstrap the kernel.
// Do not attempt to dump and write it.
2013-06-28 16:11:57 +00:00
$kernel = new DrupalKernel ( 'update' , drupal_classloader (), FALSE );
2013-01-21 19:21:34 +00:00
$kernel -> boot ();
2012-07-18 10:01:11 +00:00
// If any of the required settings needs to be written, then settings.php
// needs to be writable.
if ( ! $settings_exist ) {
$settings_file = conf_path () . '/settings.php' ;
$writable = drupal_verify_install_file ( $settings_file , FILE_EXIST | FILE_READABLE | FILE_WRITABLE );
$requirements [ 'settings file' ][ 'title' ] = 'Settings file' ;
if ( $writable ) {
$requirements [ 'settings file' ] += array (
'value' => 'settings.php is writable.' ,
);
}
else {
$requirements [ 'settings file' ] += array (
'value' => 'settings.php is not writable.' ,
'severity' => REQUIREMENT_ERROR ,
'description' => 'Drupal requires write permissions to <em>' . $settings_file . '</em> during the update process. If you are unsure how to grant file permissions, consult the <a href="http://drupal.org/server-permissions">online handbook</a>.' ,
);
}
update_extra_requirements ( $requirements );
}
2013-01-21 19:21:34 +00:00
// Bootstrap the database.
2014-01-03 19:40:40 +00:00
require_once __DIR__ . '/database.inc' ;
2012-10-30 10:08:53 +00:00
2013-08-12 06:05:34 +00:00
// module.inc is not yet loaded but there are calls to module_config_sort()
// below.
require_once __DIR__ . '/module.inc' ;
2011-04-01 14:08:42 +00:00
// If the site has not updated to Drupal 8 yet, check to make sure that it is
// running an up-to-date version of Drupal 7 before proceeding. Note this has
2010-10-03 23:33:15 +00:00
// to happen AFTER the database bootstraps because of
// drupal_get_installed_schema_version().
2012-10-09 20:32:40 +00:00
try {
$system_schema = drupal_get_installed_schema_version ( 'system' );
}
catch ( \Exception $e ) {
$system_schema = db_query ( 'SELECT schema_version FROM {system} WHERE name = :system' , array ( ':system' => 'system' )) -> fetchField ();
}
2011-04-01 14:08:42 +00:00
if ( $system_schema < 8000 ) {
2011-03-21 00:28:12 +00:00
$has_required_schema = $system_schema >= REQUIRED_D7_SCHEMA_VERSION ;
2010-10-03 23:33:15 +00:00
$requirements = array (
2011-04-01 14:08:42 +00:00
'drupal 7 version' => array (
'title' => 'Drupal 7 version' ,
'value' => $has_required_schema ? 'You are running a current version of Drupal 7.' : 'You are not running a current version of Drupal 7' ,
2012-07-18 10:01:11 +00:00
'severity' => $has_required_schema ? NULL : REQUIREMENT_ERROR ,
2011-04-01 14:08:42 +00:00
'description' => $has_required_schema ? '' : 'Please update your Drupal 7 installation to the most recent version before attempting to upgrade to Drupal 8' ,
2010-10-02 02:56:19 +00:00
),
);
2012-07-18 10:01:11 +00:00
update_extra_requirements ( $requirements );
2012-10-09 20:32:40 +00:00
// @todo update.php stages seem to be completely screwed up; the initial
// requirements check is not supposed to change the system. All of the
// following code seems to have been mistakenly/unknowingly added here and
// does not belong into update_prepare_d8_bootstrap().
2011-12-22 11:26:12 +00:00
if ( $has_required_schema ) {
2012-09-20 09:20:07 +00:00
if ( ! db_table_exists ( 'key_value' )) {
$specs = array (
2013-06-05 14:24:40 +00:00
'description' => 'Generic key-value storage table. See the state system for an example.' ,
2012-09-20 09:20:07 +00:00
'fields' => array (
'collection' => array (
'description' => 'A named collection of key and value pairs.' ,
'type' => 'varchar' ,
'length' => 128 ,
'not null' => TRUE ,
'default' => '' ,
),
'name' => array (
'description' => 'The key of the key-value pair. As KEY is a SQL reserved keyword, name was chosen instead.' ,
'type' => 'varchar' ,
'length' => 128 ,
'not null' => TRUE ,
'default' => '' ,
),
'value' => array (
'description' => 'The value.' ,
'type' => 'blob' ,
'not null' => TRUE ,
'size' => 'big' ,
'translatable' => TRUE ,
),
),
'primary key' => array ( 'collection' , 'name' ),
);
db_create_table ( 'key_value' , $specs );
}
2013-01-10 12:08:06 +00:00
if ( ! db_table_exists ( 'cache_tags' )) {
$table = 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' ),
);
db_create_table ( 'cache_tags' , $table );
}
if ( ! db_table_exists ( 'cache_config' )) {
$spec = array (
'description' => 'Cache table for configuration data.' ,
'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' ),
);
db_create_table ( 'cache_config' , $spec );
}
require_once DRUPAL_ROOT . '/core/modules/system/system.install' ;
$tables = array (
'cache' ,
'cache_bootstrap' ,
'cache_block' ,
'cache_field' ,
'cache_filter' ,
'cache_form' ,
'cache_image' ,
'cache_menu' ,
'cache_page' ,
'cache_path' ,
'cache_update' ,
);
foreach ( $tables as $table ) {
update_add_cache_columns ( $table );
}
2012-02-03 13:52:24 +00:00
// Bootstrap variables so we can update theme while preparing the update
// process.
drupal_bootstrap ( DRUPAL_BOOTSTRAP_VARIABLES );
2012-01-23 02:14:12 +00:00
2012-10-09 20:32:40 +00:00
// Update the 'language_default' system variable, if configured.
// Required to run before drupal_install_config_directories(), since that
// triggers a call into system_stream_wrappers(), which calls t(), which
// calls into language_default().
2013-12-20 02:24:31 +00:00
$language_default = update_variable_get ( 'language_default' );
2013-06-29 10:56:53 +00:00
if ( ! empty ( $language_default ) && ( isset ( $language_default -> id ) || isset ( $language_default -> language ))) {
if ( ! isset ( $language_default -> id )) {
$language_default -> id = $language_default -> language ;
2012-10-09 20:32:40 +00:00
}
unset ( $language_default -> language );
// In D8, the 'language_default' is not anymore an object, but an array,
// so make sure that the new value that is saved into this variable is an
// array.
2013-12-20 02:24:31 +00:00
update_variable_set ( 'language_default' , ( array ) $language_default );
2012-10-09 20:32:40 +00:00
}
2013-09-16 03:58:06 +00:00
$module_config = \Drupal :: config ( 'system.module' );
$theme_config = \Drupal :: config ( 'system.theme' );
$disabled_themes = \Drupal :: config ( 'system.theme.disabled' );
$schema_store = \Drupal :: keyValue ( 'system.schema' );
2012-10-09 20:32:40 +00:00
// Load system.module, because update_prepare_d8_bootstrap() is called in
// the initial minimal update.php bootstrap that performs the core
// requirements check.
require_once DRUPAL_ROOT . '/core/modules/system/system.module' ;
2012-10-30 10:08:53 +00:00
// Make sure that the bootstrap cache is cleared as that might contain
// incompatible data structures.
2012-11-28 21:36:29 +00:00
cache ( 'bootstrap' ) -> deleteAll ();
2012-10-30 10:08:53 +00:00
2012-10-09 20:32:40 +00:00
// Retrieve all installed extensions from the {system} table.
// Uninstalled extensions are ignored and not converted.
$result = db_query ( 'SELECT name, status, weight, schema_version, type FROM {system} WHERE type = :theme OR (type = :module AND schema_version <> :schema_uninstalled)' , array (
':theme' => 'theme' ,
':module' => 'module' ,
':schema_uninstalled' => SCHEMA_UNINSTALLED ,
));
2012-10-30 10:08:53 +00:00
2012-10-09 20:32:40 +00:00
$module_data = _system_rebuild_module_data ();
// Migrate each extension into configuration, varying by the extension's
// status, and record its schema version.
foreach ( $result as $record ) {
2013-06-19 09:14:55 +00:00
// Before migrating any extension into configuration, make sure the
// extensions name length is not higher than the limit.
if ( drupal_strlen ( $record -> name ) > 50 ) {
$requirements [ 'module name too long ' . $record -> name ] = array (
'title' => 'Module name too long' ,
'value' => format_string ( '@name is @count characters long.' , array ( '@name' => $record -> name , '@count' => drupal_strlen ( $record -> name ))),
'description' => 'Module names longer than 50 characters are <a href="https://drupal.org/node/2014073">no longer supported</a>.' ,
'severity' => REQUIREMENT_ERROR ,
);
update_extra_requirements ( $requirements );
}
2012-10-09 20:32:40 +00:00
if ( $record -> type == 'module' ) {
if ( $record -> status && isset ( $module_data [ $record -> name ])) {
$module_config -> set ( 'enabled.' . $record -> name , $record -> weight );
}
}
elseif ( $record -> type == 'theme' ) {
if ( $record -> status ) {
$theme_config -> set ( 'enabled.' . $record -> name , 0 );
}
else {
$disabled_themes -> set ( $record -> name , 0 );
}
}
$schema_store -> set ( $record -> name , $record -> schema_version );
}
2013-01-21 19:21:34 +00:00
$sorted_modules = module_config_sort ( $module_config -> get ( 'enabled' ));
$module_config -> set ( 'enabled' , $sorted_modules ) -> save ();
$sorted_with_filenames = array ();
foreach ( array_keys ( $sorted_modules ) as $m ) {
$sorted_with_filenames [ $m ] = drupal_get_filename ( 'module' , $m );
}
2013-09-16 03:58:06 +00:00
\Drupal :: moduleHandler () -> setModuleList ( $sorted_with_filenames );
2012-10-09 20:32:40 +00:00
$theme_config -> save ();
$disabled_themes -> save ();
2013-02-27 21:27:35 +00:00
// Migrate the private key to state. This is used to create the token for
// the upgrade batch so needs to be be done before the upgrade has begun.
update_variables_to_state ( array (
'drupal_private_key' => 'system.private_key' ,
));
2012-10-09 20:32:40 +00:00
// Update the dynamic include paths that might be used before running the
// proper update functions.
update_prepare_stored_includes ();
// Update the environment for the language bootstrap if needed.
update_prepare_d8_language ();
2013-06-10 11:33:55 +00:00
// Rebuild kernel after new language fields are added in the database
// because the translation service depends on them being there.
2013-09-16 03:58:06 +00:00
\Drupal :: service ( 'kernel' ) -> updateModules ( $sorted_with_filenames , $sorted_with_filenames );
2012-10-09 20:32:40 +00:00
2012-01-23 02:14:12 +00:00
// Change language column to langcode in url_alias.
if ( db_table_exists ( 'url_alias' ) && db_field_exists ( 'url_alias' , 'language' )) {
db_drop_index ( 'url_alias' , 'alias_language_pid' );
db_drop_index ( 'url_alias' , 'source_language_pid' );
$langcode_spec = array (
'description' => " The language code this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language. " ,
'type' => 'varchar' ,
'length' => 12 ,
'not null' => TRUE ,
2012-02-07 19:34:52 +00:00
'default' => '' ,
2012-01-23 02:14:12 +00:00
);
$langcode_indexes = array ( 'indexes' =>
array (
'alias_langcode_pid' => array ( 'alias' , 'langcode' , 'pid' ),
'source_langcode_pid' => array ( 'source' , 'langcode' , 'pid' ),
),
);
db_change_field ( 'url_alias' , 'language' , 'langcode' , $langcode_spec , $langcode_indexes );
}
2011-12-22 11:26:12 +00:00
}
}
2013-09-06 17:52:42 +00:00
// Moves install_profile from variable to settings. You can't do that in
// system.install because _system_rebuild_module_data() needs the profile
// directly. Check that it has not been set already. This is the case for
// Simpletest upgrade path tests.
if ( ! settings () -> get ( 'install_profile' )) {
2013-09-16 03:58:06 +00:00
$old_variable = unserialize ( \Drupal :: database () -> query ( 'SELECT value FROM {variable} WHERE name = :name' , array ( ':name' => 'install_profile' )) -> fetchField ());
2013-09-06 17:52:42 +00:00
$settings = array (
'settings' => array (
'install_profile' => ( object ) array (
'value' => $old_variable ,
'required' => TRUE ,
),
)
);
drupal_rewrite_settings ( $settings );
}
2013-01-10 12:08:06 +00:00
// Now remove the cache override.
2013-03-22 09:36:55 +00:00
$settings = settings () -> getAll ();
unset ( $settings [ 'cache' ][ 'default' ]);
new Settings ( $settings );
2013-06-28 16:11:57 +00:00
$kernel = new DrupalKernel ( 'update' , drupal_classloader (), FALSE );
2013-03-22 09:36:55 +00:00
$kernel -> boot ();
2013-11-16 21:30:33 +00:00
// Clear the D7 caches, to ensure that for example the theme_registry does not
// take part in the upgrade process.
Drupal :: cache ( 'cache' ) -> deleteAll ();
2011-12-22 11:26:12 +00:00
}
2012-02-03 13:52:24 +00:00
/**
2013-01-10 23:50:55 +00:00
* Fixes stored include paths to match the " /core " migration .
2012-02-03 13:52:24 +00:00
*/
function update_prepare_stored_includes () {
2013-07-03 12:48:55 +00:00
// Retrieve the currently stored language types. Default to the hardcoded D7
// values.
$default_language_types = array ( 'language' => TRUE , 'language_content' => FALSE , 'language_url' => FALSE );
$language_types = array_keys ( update_variable_get ( 'language_types' , $default_language_types ));
2012-02-03 13:52:24 +00:00
// Update language negotiation settings.
2013-07-03 12:48:55 +00:00
foreach ( $language_types as $language_type ) {
$negotiation = update_variable_get ( " language_negotiation_ $language_type " , array ());
2013-05-30 06:18:49 +00:00
foreach ( $negotiation as & $method ) {
2012-03-05 12:28:36 +00:00
if ( isset ( $method [ 'file' ]) && $method [ 'file' ] == 'includes/locale.inc' ) {
2012-04-11 14:47:06 +00:00
$method [ 'file' ] = 'core/modules/language/language.negotiation.inc' ;
2012-02-03 13:52:24 +00:00
}
}
2013-07-03 12:48:55 +00:00
update_variable_set ( " language_negotiation_ $language_type " , $negotiation );
2012-02-03 13:52:24 +00:00
}
}
2011-12-22 11:26:12 +00:00
/**
2013-01-10 23:50:55 +00:00
* Prepares Drupal 8 language changes for the bootstrap if needed .
2011-12-22 11:26:12 +00:00
*/
function update_prepare_d8_language () {
if ( db_table_exists ( 'languages' )) {
2013-09-21 15:23:51 +00:00
\Drupal :: moduleHandler () -> install ( array ( 'language' ));
2013-06-06 08:08:39 +00:00
2011-12-22 11:26:12 +00:00
$languages = db_select ( 'languages' , 'l' )
-> fields ( 'l' )
-> execute ();
$plurals = array ();
$javascript = array ();
$prefixes = array ();
$domains = array ();
foreach ( $languages as $language ) {
$plurals [ $language -> language ] = array (
2013-08-06 06:16:41 +00:00
'plurals' => $language -> plurals ,
'formula' => $language -> formula ,
2011-12-22 11:26:12 +00:00
);
$javascript [ $language -> language ] = $language -> javascript ;
$prefixes [ $language -> language ] = $language -> prefix ;
$domains [ $language -> language ] = $language -> domain ;
}
2013-09-16 03:58:06 +00:00
\Drupal :: state () -> set ( 'locale.translation.plurals' , $plurals );
\Drupal :: state () -> set ( 'locale.translation.javascript' , $javascript );
\Drupal :: config ( 'language.negotiation' )
2013-02-15 02:40:03 +00:00
-> set ( 'url.prefixes' , $prefixes )
-> set ( 'url.domains' , $domains )
-> save ();
2011-12-22 11:26:12 +00:00
// Drop now unneeded columns.
db_drop_field ( 'languages' , 'plurals' );
db_drop_field ( 'languages' , 'formula' );
db_drop_field ( 'languages' , 'javascript' );
db_drop_field ( 'languages' , 'prefix' );
db_drop_field ( 'languages' , 'domain' );
db_drop_field ( 'languages' , 'native' );
2012-04-25 23:44:20 +00:00
db_drop_field ( 'languages' , 'enabled' );
// Update language count.
2013-09-16 03:58:06 +00:00
\Drupal :: state () -> set ( 'language_count' , db_query ( 'SELECT COUNT(language) FROM {languages}' ) -> fetchField ());
2011-12-22 11:26:12 +00:00
// Rename the languages table to language.
db_rename_table ( 'languages' , 'language' );
2012-06-15 17:03:15 +00:00
// Rename language column to langcode and set it again as the primary key.
if ( db_field_exists ( 'language' , 'language' )) {
db_drop_primary_key ( 'language' );
$langcode_spec = array (
'type' => 'varchar' ,
'length' => 12 ,
'not null' => TRUE ,
'default' => '' ,
'description' => " Language code, e.g. 'de' or 'en-US'. " ,
);
db_change_field ( 'language' , 'language' , 'langcode' , $langcode_spec , array ( 'primary key' => array ( 'langcode' )));
}
// Adds the locked column and saves the special languages.
if ( ! db_field_exists ( 'language' , 'locked' )) {
$locked_spec = array (
'type' => 'int' ,
'size' => 'tiny' ,
'not null' => TRUE ,
'default' => 0 ,
'description' => 'A boolean indicating whether the administrator can edit or delete the language.' ,
);
db_add_field ( 'language' , 'locked' , $locked_spec );
2012-06-16 13:17:16 +00:00
$max_language_weight = db_query ( 'SELECT MAX(weight) FROM {language}' ) -> fetchField ();
2012-07-04 13:17:19 +00:00
$languages = language_default_locked_languages ( $max_language_weight );
2012-06-15 17:03:15 +00:00
foreach ( $languages as $language ) {
db_insert ( 'language' )
-> fields ( array (
2013-06-29 10:56:53 +00:00
'langcode' => $language -> id ,
2012-06-15 17:03:15 +00:00
'name' => $language -> name ,
'weight' => $language -> weight ,
// These languages are locked, default to enabled.
'locked' => 1 ,
))
-> execute ();
}
}
2012-04-25 23:44:20 +00:00
// Update the 'language_default' system variable with the langcode change.
2013-12-20 02:24:31 +00:00
$language_default = update_variable_get ( 'language_default' );
2012-04-25 23:44:20 +00:00
if ( ! empty ( $language_default )) {
if ( isset ( $language_default -> language )) {
2013-06-29 10:56:53 +00:00
$language_default -> id = $language_default -> language ;
2012-04-25 23:44:20 +00:00
unset ( $language_default -> language );
}
unset ( $language_default -> enabled );
2012-06-13 00:43:37 +00:00
// In D8, the 'language_default' is not anymore an object, but an array,
// so make sure that the new value that is saved into this variable is an
// array.
2013-06-29 10:56:53 +00:00
$language_default = ( array ) $language_default ;
$language_default [ 'langcode' ] = 'en' ;
2013-12-20 02:24:31 +00:00
update_variable_set ( 'language_default' , $language_default );
2013-06-29 10:56:53 +00:00
}
// Convert languages to config entities.
$result = db_query ( 'SELECT * FROM {language}' );
2013-09-24 22:16:05 +00:00
$uuid = \Drupal :: service ( 'uuid' );
2013-06-29 10:56:53 +00:00
foreach ( $result as $language ) {
2013-09-16 03:58:06 +00:00
\Drupal :: config ( 'language.entity.' . $language -> langcode )
2013-06-29 10:56:53 +00:00
-> set ( 'id' , $language -> langcode )
-> set ( 'uuid' , $uuid -> generate ())
-> set ( 'label' , $language -> name )
-> set ( 'direction' , $language -> direction )
-> set ( 'weight' , $language -> weight )
-> set ( 'locked' , $language -> locked )
-> set ( 'langcode' , 'en' )
-> save ();
2012-04-25 23:44:20 +00:00
}
2012-10-11 21:49:49 +00:00
// Add column to track customized string status to locales_target.
// When updating in a non-English language, the locale translation system is
// triggered, which attempts to query string translations already.
if ( db_table_exists ( 'locales_target' ) && ! db_field_exists ( 'locales_target' , 'customized' )) {
$spec = array (
'type' => 'int' ,
'not null' => TRUE ,
'default' => 0 , // LOCALE_NOT_CUSTOMIZED
'description' => 'Boolean indicating whether the translation is custom to this site.' ,
);
db_add_field ( 'locales_target' , 'customized' , $spec );
}
2012-10-23 10:25:45 +00:00
// Add locales_location table to track string locations.
// When updating in a non-English language, this table is used for
// refreshing JavaScript translations.
if ( db_table_exists ( 'locales_source' ) && ! db_table_exists ( 'locales_location' )) {
$table = array (
'description' => 'Location information for source strings.' ,
'fields' => array (
'lid' => array (
'type' => 'serial' ,
'not null' => TRUE ,
'description' => 'Unique identifier of this location.' ,
),
'sid' => array (
'type' => 'int' ,
'not null' => TRUE ,
'description' => 'Unique identifier of this string.' ,
),
'type' => array (
'type' => 'varchar' ,
'length' => 50 ,
'not null' => TRUE ,
'default' => '' ,
'description' => 'The location type (file, config, path, etc).' ,
),
'name' => array (
'type' => 'varchar' ,
'length' => 255 ,
'not null' => TRUE ,
'default' => '' ,
'description' => 'Type dependent location information (file name, path, etc).' ,
),
'version' => array (
'type' => 'varchar' ,
'length' => 20 ,
'not null' => TRUE ,
'default' => 'none' ,
'description' => 'Version of Drupal where the location was found.' ,
),
),
'primary key' => array ( 'lid' ),
'foreign keys' => array (
'locales_source' => array (
'table' => 'locales_source' ,
'columns' => array ( 'sid' => 'lid' ),
),
),
'indexes' => array (
'string_id' => array ( 'sid' ),
'string_type' => array ( 'sid' , 'type' ),
),
);
db_create_table ( 'locales_location' , $table );
}
2011-12-22 11:26:12 +00:00
}
}
2009-07-29 05:59:59 +00:00
/**
2013-01-10 23:50:55 +00:00
* Performs Drupal 7. x to 8. x required update . php updates .
2009-07-29 05:59:59 +00:00
*
2011-04-01 14:08:42 +00:00
* This function runs when update . php is run the first time for 8. x ,
2009-07-29 05:59:59 +00:00
* even before updates are selected or performed . It is important
* that if updates are not ultimately performed that no changes are
* made which make it impossible to continue using the prior version .
*/
2011-04-01 14:08:42 +00:00
function update_fix_d8_requirements () {
2012-08-07 19:11:13 +00:00
if ( drupal_get_installed_schema_version ( 'system' ) < 8000 && ! update_variable_get ( 'update_d8_requirements' , FALSE )) {
2012-11-26 10:38:45 +00:00
// Make sure that file.module is enabled as it is required for the user
// picture upgrade path.
2013-09-21 15:23:51 +00:00
\Drupal :: moduleHandler () -> install ( array ( 'file' ));
2012-11-26 10:38:45 +00:00
2013-03-20 11:51:03 +00:00
$schema = array (
'description' => 'Generic key/value storage table with an expiration.' ,
'fields' => array (
'collection' => array (
'description' => 'A named collection of key and value pairs.' ,
'type' => 'varchar' ,
'length' => 128 ,
'not null' => TRUE ,
'default' => '' ,
),
'name' => array (
// KEY is an SQL reserved word, so use 'name' as the key's field name.
'description' => 'The key of the key/value pair.' ,
'type' => 'varchar' ,
'length' => 128 ,
'not null' => TRUE ,
'default' => '' ,
),
'value' => array (
'description' => 'The value of the key/value pair.' ,
'type' => 'blob' ,
'not null' => TRUE ,
'size' => 'big' ,
),
'expire' => array (
'description' => 'The time since Unix epoch in seconds when this item expires. Defaults to the maximum possible time.' ,
'type' => 'int' ,
'not null' => TRUE ,
'default' => 2147483647 ,
),
),
'primary key' => array ( 'collection' , 'name' ),
'indexes' => array (
'all' => array ( 'name' , 'collection' , 'expire' ),
),
);
db_create_table ( 'key_value_expire' , $schema );
2013-06-06 08:08:39 +00:00
// Views module is required to convert all previously existing listings into
// views configurations.
// Like any other module APIs and services, Views' services are not available
// in update.php. Existing listings are migrated into configuration, using
2013-09-21 15:23:51 +00:00
// the limited standard tools of raw database queries and \Drupal::config().
2013-09-19 16:22:53 +00:00
\Drupal :: moduleHandler () -> install ( array ( 'views' ));
2013-06-06 08:08:39 +00:00
2012-08-07 19:11:13 +00:00
update_variable_set ( 'update_d8_requirements' , TRUE );
2009-07-29 05:59:59 +00:00
}
2010-12-18 06:36:00 +00:00
}
2011-09-21 10:09:49 +00:00
/**
2013-06-06 08:08:39 +00:00
* Forces a module to a given schema version .
2013-02-09 00:14:58 +00:00
*
2013-06-06 08:08:39 +00:00
* This function is rarely necessary .
2013-02-09 00:14:58 +00:00
*
2013-06-06 08:08:39 +00:00
* @ param string $module
* Name of the module .
* @ param string $schema_version
* The schema version the module should be set to .
2011-09-21 10:09:49 +00:00
*/
2013-06-06 08:08:39 +00:00
function update_set_schema ( $module , $schema_version ) {
2013-09-16 03:58:06 +00:00
\Drupal :: keyValue ( 'system.schema' ) -> set ( $module , $schema_version );
2013-06-06 08:08:39 +00:00
// system_list_reset() is in module.inc but that would only be available
// once the variable bootstrap is done.
require_once __DIR__ . '/module.inc' ;
system_list_reset ();
2011-09-21 10:09:49 +00:00
}
2010-12-18 06:36:00 +00:00
2009-07-29 05:59:59 +00:00
/**
2013-01-10 23:50:55 +00:00
* Performs one update and stores the results for display on the results page .
2009-07-29 05:59:59 +00:00
*
2009-09-07 15:43:55 +00:00
* If an update function completes successfully , it should return a message
* as a string indicating success , for example :
* @ code
* return t ( 'New index added successfully.' );
* @ endcode
*
* Alternatively , it may return nothing . In that case , no message
* will be displayed at all .
*
* If it fails for whatever reason , it should throw an instance of
2012-05-12 03:10:23 +00:00
* Drupal\Core\Utility\UpdateException with an appropriate error message , for
* example :
2009-09-07 15:43:55 +00:00
* @ code
2012-05-12 03:10:23 +00:00
* use Drupal\Core\Utility\UpdateException ;
* throw new UpdateException ( t ( 'Description of what went wrong' ));
2009-09-07 15:43:55 +00:00
* @ endcode
*
2010-02-03 18:16:23 +00:00
* If an exception is thrown , the current update and all updates that depend on
* it will be aborted . The schema version will not be updated in this case , and
* all the aborted updates will continue to appear on update . php as updates
* that have not yet been run .
2009-07-29 05:59:59 +00:00
*
2009-09-07 15:43:55 +00:00
* If an update function needs to be re - run as part of a batch process , it
* should accept the $sandbox array by reference as its first parameter
* and set the #finished property to the percentage completed that it is, as a
* fraction of 1.
*
2009-07-29 05:59:59 +00:00
* @ param $module
* The module whose update will be run .
* @ param $number
* The update number to run .
2010-02-03 18:16:23 +00:00
* @ param $dependency_map
* An array whose keys are the names of all update functions that will be
* performed during this batch process , and whose values are arrays of other
* update functions that each one depends on .
2009-07-29 05:59:59 +00:00
* @ param $context
2010-02-03 18:16:23 +00:00
* The batch context array .
*
* @ see update_resolve_dependencies ()
2009-07-29 05:59:59 +00:00
*/
2010-02-03 18:16:23 +00:00
function update_do_one ( $module , $number , $dependency_map , & $context ) {
$function = $module . '_update_' . $number ;
// If this update was aborted in a previous step, or has a dependency that
// was aborted in a previous step, go no further.
2010-02-17 20:45:36 +00:00
if ( ! empty ( $context [ 'results' ][ '#abort' ]) && array_intersect ( $context [ 'results' ][ '#abort' ], array_merge ( $dependency_map , array ( $function )))) {
2009-07-29 05:59:59 +00:00
return ;
}
2009-09-07 15:43:55 +00:00
$ret = array ();
2009-07-29 05:59:59 +00:00
if ( function_exists ( $function )) {
2009-09-07 15:43:55 +00:00
try {
$ret [ 'results' ][ 'query' ] = $function ( $context [ 'sandbox' ]);
$ret [ 'results' ][ 'success' ] = TRUE ;
}
2010-06-08 05:29:05 +00:00
// @TODO We may want to do different error handling for different
// exception types, but for now we'll just log the exception and
// return the message for printing.
2009-09-07 15:43:55 +00:00
catch ( Exception $e ) {
2010-06-08 05:29:05 +00:00
watchdog_exception ( 'update' , $e );
2013-05-09 09:25:10 +00:00
require_once __DIR__ . '/errors.inc' ;
2010-06-08 05:29:05 +00:00
$variables = _drupal_decode_exception ( $e );
2012-12-29 08:58:04 +00:00
unset ( $variables [ 'backtrace' ]);
2013-10-07 05:34:09 +00:00
// The exception message is run through
// \Drupal\Component\Utility\String::checkPlain() by
// _drupal_decode_exception().
2010-10-25 00:06:19 +00:00
$ret [ '#abort' ] = array ( 'success' => FALSE , 'query' => t ( '%type: !message in %function (line %line of %file).' , $variables ));
2009-09-07 15:43:55 +00:00
}
2009-07-29 05:59:59 +00:00
}
2009-09-07 15:43:55 +00:00
if ( isset ( $context [ 'sandbox' ][ '#finished' ])) {
$context [ 'finished' ] = $context [ 'sandbox' ][ '#finished' ];
unset ( $context [ 'sandbox' ][ '#finished' ]);
}
2009-07-29 05:59:59 +00:00
if ( ! isset ( $context [ 'results' ][ $module ])) {
$context [ 'results' ][ $module ] = array ();
}
if ( ! isset ( $context [ 'results' ][ $module ][ $number ])) {
$context [ 'results' ][ $module ][ $number ] = array ();
}
$context [ 'results' ][ $module ][ $number ] = array_merge ( $context [ 'results' ][ $module ][ $number ], $ret );
if ( ! empty ( $ret [ '#abort' ])) {
2010-02-03 18:16:23 +00:00
// Record this function in the list of updates that were aborted.
$context [ 'results' ][ '#abort' ][] = $function ;
2009-07-29 05:59:59 +00:00
}
2009-09-07 15:43:55 +00:00
2009-07-29 05:59:59 +00:00
// Record the schema update if it was completed successfully.
2010-02-03 18:16:23 +00:00
if ( $context [ 'finished' ] == 1 && empty ( $ret [ '#abort' ])) {
2009-07-29 05:59:59 +00:00
drupal_set_installed_schema_version ( $module , $number );
}
2013-10-07 05:34:09 +00:00
$context [ 'message' ] = 'Updating ' . String :: checkPlain ( $module ) . ' module' ;
2009-07-29 05:59:59 +00:00
}
2009-08-03 19:37:38 +00:00
/**
2013-01-10 23:50:55 +00:00
* Starts the database update batch process .
2009-08-03 19:37:38 +00:00
*
* @ param $start
2010-02-03 18:16:23 +00:00
* An array whose keys contain the names of modules to be updated during the
* current batch process , and whose values contain the number of the first
* requested update for that module . The actual updates that are run ( and the
* order they are run in ) will depend on the results of passing this data
* through the update dependency system .
2009-08-03 19:37:38 +00:00
* @ param $redirect
* Path to redirect to when the batch has finished processing .
* @ param $url
* URL of the batch processing page ( should only be used for separate
* scripts like update . php ) .
* @ param $batch
* Optional parameters to pass into the batch API .
2009-10-03 20:17:46 +00:00
* @ param $redirect_callback
* ( optional ) Specify a function to be called to redirect to the progressive
* processing page .
2010-02-03 18:16:23 +00:00
*
* @ see update_resolve_dependencies ()
2009-08-03 19:37:38 +00:00
*/
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
function update_batch ( $start , $redirect = NULL , $url = NULL , $batch = array (), $redirect_callback = NULL ) {
2009-08-03 19:37:38 +00:00
// During the update, bring the site offline so that schema changes do not
// affect visiting users.
2013-09-16 03:58:06 +00:00
$maintenance_mode = \Drupal :: config ( 'system.maintenance' ) -> get ( 'enabled' );
2012-07-28 13:39:43 +00:00
if ( isset ( $maintenance_mode )) {
$_SESSION [ 'maintenance_mode' ] = $maintenance_mode ;
}
if ( empty ( $_SESSION [ 'maintenance_mode' ])) {
2013-09-14 07:08:33 +00:00
if ( db_table_exists ( 'state' )) {
2013-09-16 03:58:06 +00:00
\Drupal :: state () -> set ( 'system.maintenance_mode' , TRUE );
2012-07-28 13:39:43 +00:00
}
2009-08-03 19:37:38 +00:00
}
2010-02-03 18:16:23 +00:00
// Resolve any update dependencies to determine the actual updates that will
// be run and the order they will be run in.
$updates = update_resolve_dependencies ( $start );
// Store the dependencies for each update function in an array which the
// batch API can pass in to the batch operation each time it is called. (We
// do not store the entire update dependency array here because it is
// potentially very large.)
$dependency_map = array ();
foreach ( $updates as $function => $update ) {
$dependency_map [ $function ] = ! empty ( $update [ 'reverse_paths' ]) ? array_keys ( $update [ 'reverse_paths' ]) : array ();
}
2009-08-03 19:37:38 +00:00
$operations = array ();
2010-02-03 18:16:23 +00:00
foreach ( $updates as $update ) {
if ( $update [ 'allowed' ]) {
// Set the installed version of each module so updates will start at the
// correct place. (The updates are already sorted, so we can simply base
// this on the first one we come across in the above foreach loop.)
if ( isset ( $start [ $update [ 'module' ]])) {
drupal_set_installed_schema_version ( $update [ 'module' ], $update [ 'number' ] - 1 );
unset ( $start [ $update [ 'module' ]]);
2009-08-03 19:37:38 +00:00
}
2010-02-03 18:16:23 +00:00
// Add this update function to the batch.
2010-02-17 20:45:36 +00:00
$function = $update [ 'module' ] . '_update_' . $update [ 'number' ];
$operations [] = array ( 'update_do_one' , array ( $update [ 'module' ], $update [ 'number' ], $dependency_map [ $function ]));
2009-08-03 19:37:38 +00:00
}
}
$batch [ 'operations' ] = $operations ;
$batch += array (
'title' => 'Updating' ,
'init_message' => 'Starting updates' ,
'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.' ,
'finished' => 'update_finished' ,
2011-10-31 04:05:57 +00:00
'file' => 'core/includes/update.inc' ,
2009-08-03 19:37:38 +00:00
);
batch_set ( $batch );
Issue #1668866 by ParisLiakos, aspilicious, tim.plunkett, pdrake, g.oechsler, dawehner, Berdir, corvus_ch, damiankloip, disasm, marcingy, neclimdul: Replace drupal_goto() with RedirectResponse.
2013-06-19 16:07:30 +00:00
return batch_process ( $redirect , $url , $redirect_callback );
2009-08-03 19:37:38 +00:00
}
/**
2013-01-10 23:50:55 +00:00
* Finishes the update process and stores the results for eventual display .
2009-08-03 19:37:38 +00:00
*
* After the updates run , all caches are flushed . The update results are
* stored into the session ( for example , to be displayed on the update results
* page in update . php ) . Additionally , if the site was off - line , now that the
* update process is completed , the site is set back online .
*
* @ param $success
* Indicate that the batch API tasks were all completed successfully .
* @ param $results
* An array of all the results that were updated in update_do_one () .
* @ param $operations
* A list of all the operations that had not been completed by the batch API .
*
* @ see update_batch ()
*/
function update_finished ( $success , $results , $operations ) {
// Clear the caches in case the data has been updated.
2013-06-06 08:08:39 +00:00
update_flush_all_caches ();
2009-08-03 19:37:38 +00:00
$_SESSION [ 'update_results' ] = $results ;
$_SESSION [ 'update_success' ] = $success ;
$_SESSION [ 'updates_remaining' ] = $operations ;
// Now that the update is done, we can put the site back online if it was
2009-08-22 18:24:14 +00:00
// previously in maintenance mode.
2012-07-28 13:39:43 +00:00
if ( isset ( $_SESSION [ 'maintenance_mode' ])) {
2013-09-16 03:58:06 +00:00
\Drupal :: state () -> set ( 'system.maintenance_mode' , FALSE );
2009-08-22 18:24:14 +00:00
unset ( $_SESSION [ 'maintenance_mode' ]);
2009-08-03 19:37:38 +00:00
}
}
2009-08-21 06:40:05 +00:00
/**
2013-01-10 23:50:55 +00:00
* Returns a list of all the pending database updates .
2009-08-21 06:40:05 +00:00
*
* @ return
* An associative array keyed by module name which contains all information
* about database updates that need to be run , and any updates that are not
* going to proceed due to missing requirements . The system module will
* always be listed first .
*
* The subarray for each module can contain the following keys :
* - start : The starting update that is to be processed . If this does not
* exist then do not process any updates for this module as there are
* other requirements that need to be resolved .
* - warning : Any warnings about why this module can not be updated .
* - pending : An array of all the pending updates for the module including
* the update number and the description from source code comment for
* each update function . This array is keyed by the update number .
*/
function update_get_update_list () {
// Make sure that the system module is first in the list of updates.
$ret = array ( 'system' => array ());
2009-09-07 15:43:55 +00:00
2009-08-21 06:40:05 +00:00
$modules = drupal_get_installed_schema_version ( NULL , FALSE , TRUE );
foreach ( $modules as $module => $schema_version ) {
2010-09-02 21:07:44 +00:00
// Skip uninstalled and incompatible modules.
if ( $schema_version == SCHEMA_UNINSTALLED || update_check_incompatibility ( $module )) {
continue ;
}
// Otherwise, get the list of updates defined by this module.
2009-08-21 06:40:05 +00:00
$updates = drupal_get_schema_versions ( $module );
2010-09-02 21:07:44 +00:00
if ( $updates !== FALSE ) {
2009-08-21 06:40:05 +00:00
// module_invoke returns NULL for nonexisting hooks, so if no updates
// are removed, it will == 0.
$last_removed = module_invoke ( $module , 'update_last_removed' );
if ( $schema_version < $last_removed ) {
$ret [ $module ][ 'warning' ] = '<em>' . $module . '</em> module can not be updated. Its schema version is ' . $schema_version . '. Updates up to and including ' . $last_removed . ' have been removed in this release. In order to update <em>' . $module . '</em> module, you will first <a href="http://drupal.org/upgrade">need to upgrade</a> to the last version in which these updates were available.' ;
continue ;
}
2009-09-07 15:43:55 +00:00
2009-08-21 06:40:05 +00:00
$updates = drupal_map_assoc ( $updates );
foreach ( array_keys ( $updates ) as $update ) {
if ( $update > $schema_version ) {
2010-09-25 18:10:53 +00:00
// The description for an update comes from its Doxygen.
2009-08-21 06:40:05 +00:00
$func = new ReflectionFunction ( $module . '_update_' . $update );
2010-09-25 18:10:53 +00:00
$description = str_replace ( array ( " \n " , '*' , '/' ), '' , $func -> getDocComment ());
$ret [ $module ][ 'pending' ][ $update ] = " $update - $description " ;
if ( ! isset ( $ret [ $module ][ 'start' ])) {
$ret [ $module ][ 'start' ] = $update ;
2009-08-21 06:40:05 +00:00
}
}
}
if ( ! isset ( $ret [ $module ][ 'start' ]) && isset ( $ret [ $module ][ 'pending' ])) {
$ret [ $module ][ 'start' ] = $schema_version ;
}
}
}
2009-09-07 15:43:55 +00:00
2009-08-21 06:40:05 +00:00
if ( empty ( $ret [ 'system' ])) {
unset ( $ret [ 'system' ]);
}
return $ret ;
}
2010-02-03 18:16:23 +00:00
/**
* Resolves dependencies in a set of module updates , and orders them correctly .
*
* This function receives a list of requested module updates and determines an
* appropriate order to run them in such that all update dependencies are met .
* Any updates whose dependencies cannot be met are included in the returned
* array but have the key 'allowed' set to FALSE ; the calling function should
* take responsibility for ensuring that these updates are ultimately not
* performed .
*
* In addition , the returned array also includes detailed information about the
* dependency chain for each update , as provided by the depth - first search
2012-04-09 18:02:59 +00:00
* algorithm in Drupal\Component\Graph\Graph :: searchAndSort () .
2010-02-03 18:16:23 +00:00
*
* @ param $starting_updates
* An array whose keys contain the names of modules with updates to be run
* and whose values contain the number of the first requested update for that
* module .
*
* @ return
* An array whose keys are the names of all update functions within the
* provided modules that would need to be run in order to fulfill the
* request , arranged in the order in which the update functions should be
* run . ( This includes the provided starting update for each module and all
* subsequent updates that are available . ) The values are themselves arrays
2012-04-09 18:02:59 +00:00
* containing all the keys provided by the
* Drupal\Component\Graph\Graph :: searchAndSort () algorithm , which encode
* detailed information about the dependency chain for this update function
2013-01-10 23:50:55 +00:00
* ( for example : 'paths' , 'reverse_paths' , 'weight' , and 'component' ), as
* well as the following additional keys :
2010-02-03 18:16:23 +00:00
* - 'allowed' : A boolean which is TRUE when the update function ' s
* dependencies are met , and FALSE otherwise . Calling functions should
* inspect this value before running the update .
* - 'missing_dependencies' : An array containing the names of any other
* update functions that are required by this one but that are unavailable
* to be run . This array will be empty when 'allowed' is TRUE .
* - 'module' : The name of the module that this update function belongs to .
* - 'number' : The number of this update function within that module .
*
2013-10-03 11:26:25 +00:00
* @ see \Drupal\Component\Graph\Graph :: searchAndSort ()
2010-02-03 18:16:23 +00:00
*/
function update_resolve_dependencies ( $starting_updates ) {
// Obtain a dependency graph for the requested update functions.
$update_functions = update_get_update_function_list ( $starting_updates );
$graph = update_build_dependency_graph ( $update_functions );
2012-04-09 18:02:59 +00:00
// Perform the depth-first search and sort on the results.
$graph_object = new Graph ( $graph );
$graph = $graph_object -> searchAndSort ();
2010-02-03 18:16:23 +00:00
uasort ( $graph , 'drupal_sort_weight' );
foreach ( $graph as $function => & $data ) {
$module = $data [ 'module' ];
$number = $data [ 'number' ];
// If the update function is missing and has not yet been performed, mark
// it and everything that ultimately depends on it as disallowed.
if ( update_is_missing ( $module , $number , $update_functions ) && ! update_already_performed ( $module , $number )) {
$data [ 'allowed' ] = FALSE ;
foreach ( array_keys ( $data [ 'paths' ]) as $dependent ) {
$graph [ $dependent ][ 'allowed' ] = FALSE ;
$graph [ $dependent ][ 'missing_dependencies' ][] = $function ;
}
}
elseif ( ! isset ( $data [ 'allowed' ])) {
$data [ 'allowed' ] = TRUE ;
$data [ 'missing_dependencies' ] = array ();
}
// Now that we have finished processing this function, remove it from the
// graph if it was not part of the original list. This ensures that we
// never try to run any updates that were not specifically requested.
if ( ! isset ( $update_functions [ $module ][ $number ])) {
unset ( $graph [ $function ]);
}
}
return $graph ;
}
/**
* Returns an organized list of update functions for a set of modules .
*
* @ param $starting_updates
* An array whose keys contain the names of modules and whose values contain
* the number of the first requested update for that module .
*
* @ return
* An array containing all the update functions that should be run for each
* module , including the provided starting update and all subsequent updates
* that are available . The keys of the array contain the module names , and
* each value is an ordered array of update functions , keyed by the update
* number .
*
* @ see update_resolve_dependencies ()
*/
function update_get_update_function_list ( $starting_updates ) {
// Go through each module and find all updates that we need (including the
// first update that was requested and any updates that run after it).
$update_functions = array ();
foreach ( $starting_updates as $module => $version ) {
$update_functions [ $module ] = array ();
$updates = drupal_get_schema_versions ( $module );
2010-03-07 06:53:26 +00:00
if ( $updates !== FALSE ) {
$max_version = max ( $updates );
if ( $version <= $max_version ) {
foreach ( $updates as $update ) {
if ( $update >= $version ) {
$update_functions [ $module ][ $update ] = $module . '_update_' . $update ;
}
2010-02-03 18:16:23 +00:00
}
}
}
}
return $update_functions ;
}
/**
* Constructs a graph which encodes the dependencies between module updates .
*
* This function returns an associative array which contains a " directed graph "
* representation of the dependencies between a provided list of update
* functions , as well as any outside update functions that they directly depend
* on but that were not in the provided list . The vertices of the graph
* represent the update functions themselves , and each edge represents a
* requirement that the first update function needs to run before the second .
* For example , consider this graph :
*
2011-04-01 14:08:42 +00:00
* system_update_8000 ---> system_update_8001 ---> system_update_8002
2010-02-03 18:16:23 +00:00
*
2011-04-01 14:08:42 +00:00
* Visually , this indicates that system_update_8000 () must run before
* system_update_8001 (), which in turn must run before system_update_8002 () .
2010-02-03 18:16:23 +00:00
*
* The function takes into account standard dependencies within each module , as
* shown above ( i . e . , the fact that each module ' s updates must run in numerical
* order ), but also finds any cross - module dependencies that are defined by
* modules which implement hook_update_dependencies (), and builds them into the
* graph as well .
*
* @ param $update_functions
* An organized array of update functions , in the format returned by
* update_get_update_function_list () .
*
* @ return
* A multidimensional array representing the dependency graph , suitable for
2012-04-09 18:02:59 +00:00
* passing in to Drupal\Component\Graph\Graph :: searchAndSort (), but with extra
* information about each update function also included . Each array key
* contains the name of an update function , including all update functions
* from the provided list as well as any outside update functions which they
* directly depend on . Each value is an associative array containing the
* following keys :
2010-02-03 18:16:23 +00:00
* - 'edges' : A representation of any other update functions that immediately
2012-04-09 18:02:59 +00:00
* depend on this one . See Drupal\Component\Graph\Graph :: searchAndSort () for
* more details on the format .
2010-02-03 18:16:23 +00:00
* - 'module' : The name of the module that this update function belongs to .
* - 'number' : The number of this update function within that module .
*
2013-10-03 11:26:25 +00:00
* @ see \Drupal\Component\Graph\Graph :: searchAndSort ()
2010-02-03 18:16:23 +00:00
* @ see update_resolve_dependencies ()
*/
function update_build_dependency_graph ( $update_functions ) {
// Initialize an array that will define a directed graph representing the
// dependencies between update functions.
$graph = array ();
// Go through each update function and build an initial list of dependencies.
foreach ( $update_functions as $module => $functions ) {
$previous_function = NULL ;
foreach ( $functions as $number => $function ) {
// Add an edge to the directed graph representing the fact that each
// update function in a given module must run after the update that
// numerically precedes it.
if ( $previous_function ) {
$graph [ $previous_function ][ 'edges' ][ $function ] = TRUE ;
}
$previous_function = $function ;
// Define the module and update number associated with this function.
$graph [ $function ][ 'module' ] = $module ;
$graph [ $function ][ 'number' ] = $number ;
}
}
// Now add any explicit update dependencies declared by modules.
2010-04-28 05:28:22 +00:00
$update_dependencies = update_retrieve_dependencies ();
2010-02-03 18:16:23 +00:00
foreach ( $graph as $function => $data ) {
if ( ! empty ( $update_dependencies [ $data [ 'module' ]][ $data [ 'number' ]])) {
foreach ( $update_dependencies [ $data [ 'module' ]][ $data [ 'number' ]] as $module => $number ) {
$dependency = $module . '_update_' . $number ;
$graph [ $dependency ][ 'edges' ][ $function ] = TRUE ;
$graph [ $dependency ][ 'module' ] = $module ;
$graph [ $dependency ][ 'number' ] = $number ;
}
}
}
return $graph ;
}
/**
* Determines if a module update is missing or unavailable .
*
* @ param $module
* The name of the module .
* @ param $number
* The number of the update within that module .
* @ param $update_functions
* An organized array of update functions , in the format returned by
* update_get_update_function_list () . This should represent all module
* updates that are requested to run at the time this function is called .
*
* @ return
* TRUE if the provided module update is not installed or is not in the
* provided list of updates to run ; FALSE otherwise .
*/
function update_is_missing ( $module , $number , $update_functions ) {
return ! isset ( $update_functions [ $module ][ $number ]) || ! function_exists ( $update_functions [ $module ][ $number ]);
}
/**
* Determines if a module update has already been performed .
*
* @ param $module
* The name of the module .
* @ param $number
* The number of the update within that module .
*
* @ return
* TRUE if the database schema indicates that the update has already been
* performed ; FALSE otherwise .
*/
function update_already_performed ( $module , $number ) {
return $number <= drupal_get_installed_schema_version ( $module );
}
/**
2013-01-10 23:50:55 +00:00
* Invokes hook_update_dependencies () in all installed modules .
2010-02-03 18:16:23 +00:00
*
2013-08-01 08:10:33 +00:00
* This function is similar to \Drupal :: moduleHandler () -> invokeAll (), with the
* main difference that it does not require that a module be enabled to invoke
* its hook , only that it be installed . This allows the update system to
* properly perform updates even on modules that are currently disabled .
2010-02-03 18:16:23 +00:00
*
* @ return
2010-04-28 05:28:22 +00:00
* An array of return values obtained by merging the results of the
* hook_update_dependencies () implementations in all installed modules .
2010-02-03 18:16:23 +00:00
*
2013-08-01 08:10:33 +00:00
* @ see \Drupal :: moduleHandler () -> invokeAll ()
2010-04-28 05:28:22 +00:00
* @ see hook_update_dependencies ()
2010-02-03 18:16:23 +00:00
*/
2010-04-28 05:28:22 +00:00
function update_retrieve_dependencies () {
2010-02-03 18:16:23 +00:00
$return = array ();
2010-04-28 05:28:22 +00:00
// Get a list of installed modules, arranged so that we invoke their hooks in
2013-08-01 08:10:33 +00:00
// the same order that \Drupal::moduleHandler()->invokeAll() does.
2013-09-16 03:58:06 +00:00
foreach ( \Drupal :: keyValue ( 'system.schema' ) -> getAll () as $module => $schema ) {
2013-01-15 22:07:29 +00:00
if ( $schema == SCHEMA_UNINSTALLED ) {
// Nothing to upgrade.
continue ;
}
2013-06-06 08:08:39 +00:00
$function = $module . '_update_dependencies' ;
2013-01-15 22:07:29 +00:00
// Ensure install file is loaded.
module_load_install ( $module );
2010-02-03 18:16:23 +00:00
if ( function_exists ( $function )) {
2010-04-28 05:28:22 +00:00
$result = $function ();
// Each implementation of hook_update_dependencies() returns a
// multidimensional, associative array containing some keys that
// represent module names (which are strings) and other keys that
// represent update function numbers (which are integers). We cannot use
// array_merge_recursive() to properly merge these results, since it
// treats strings and integers differently. Therefore, we have to
// explicitly loop through the expected array structure here and perform
// the merge manually.
2010-02-03 18:16:23 +00:00
if ( isset ( $result ) && is_array ( $result )) {
2010-04-28 05:28:22 +00:00
foreach ( $result as $module => $module_data ) {
foreach ( $module_data as $update => $update_data ) {
foreach ( $update_data as $module_dependency => $update_dependency ) {
// If there are redundant dependencies declared for the same
// update function (so that it is declared to depend on more than
// one update from a particular module), record the dependency on
// the highest numbered update here, since that automatically
// implies the previous ones. For example, if one module's
// implementation of hook_update_dependencies() required this
// ordering:
//
2011-04-01 14:08:42 +00:00
// system_update_8001 ---> user_update_8000
2010-04-28 05:28:22 +00:00
//
// but another module's implementation of the hook required this
// one:
//
2011-04-01 14:08:42 +00:00
// system_update_8002 ---> user_update_8000
2010-04-28 05:28:22 +00:00
//
2011-04-01 14:08:42 +00:00
// we record the second one, since system_update_8001() is always
// guaranteed to run before system_update_8002() anyway (within
2010-04-28 05:28:22 +00:00
// an individual module, updates are always run in numerical
// order).
if ( ! isset ( $return [ $module ][ $update ][ $module_dependency ]) || $update_dependency > $return [ $module ][ $update ][ $module_dependency ]) {
$return [ $module ][ $update ][ $module_dependency ] = $update_dependency ;
}
}
}
}
2010-02-03 18:16:23 +00:00
}
}
}
return $return ;
}
2012-08-07 19:11:13 +00:00
/**
2012-08-09 20:21:50 +00:00
* Gets the value of a variable from the database during 7. x - 8. x upgrades .
2012-08-07 19:11:13 +00:00
*
* Use this during the 7. x - 8. x upgrade path instead of variable_get () .
*
* @ param string $name
* The name of the variable .
* @ param mixed $default
* The default value of the variable .
*
* @ return mixed
* The value of the variable in the database unserialized , or NULL if not set .
*
* @ see update_variable_set ()
* @ see update_variable_del ()
* @ ingroup config_upgrade
*/
function update_variable_get ( $name , $default = NULL ) {
$result = db_query ( 'SELECT value FROM {variable} WHERE name = :name' , array ( ':name' => $name )) -> fetchField ();
if ( $result !== FALSE ) {
return unserialize ( $result );
}
return $default ;
}
/**
* Sets a persistent variable during the 7. x - 8. x upgrade path .
*
* Use this during the 7. x - 8. x upgrade path instead of variable_set () .
*
* @ param string $name
* The name of the variable to set .
* @ param mixed $value
* The value to set . This can be any PHP data type ; these functions take care
* of serialization as necessary .
*
* @ see update_variable_get ()
* @ see update_variable_del ()
* @ ingroup config_upgrade
*/
function update_variable_set ( $name , $value ) {
db_merge ( 'variable' )
-> key ( array (
'name' => $name ,
))
-> fields ( array (
'value' => serialize ( $value ),
))
-> execute ();
}
/**
* Delete a variable from the database during the 7. x - 8. x upgrade path .
*
* Use this during the 7. x - 8. x upgrade path instead of variable_del () .
*
* @ param string $name
* The name of the variable to delete .
*
* @ see update_variable_get ()
* @ see update_variable_set ()
* @ ingroup config_upgrade
*/
function update_variable_del ( $name ) {
db_delete ( 'variable' )
-> condition ( 'name' , $name )
-> execute ();
}
2012-03-30 03:43:32 +00:00
/**
2013-01-10 23:50:55 +00:00
* Updates config with values set on Drupal 7. x .
2012-04-07 02:28:53 +00:00
*
2013-01-10 23:50:55 +00:00
* Provides a generalised method to migrate variables from Drupal 7 to
* Drupal 8 ' s configuration management system .
2012-03-30 03:43:32 +00:00
*
2012-06-02 19:45:56 +00:00
* @ param string $config_name
* The configuration object name to retrieve .
* @ param array $variable_map
* An associative array that maps old variables names to new configuration
* object keys ; e . g .:
2012-03-30 03:43:32 +00:00
* @ code
2012-06-02 19:45:56 +00:00
* array ( 'old_variable' => 'new_config.sub_key' )
2012-04-07 02:28:53 +00:00
* @ endcode
2012-06-02 19:45:56 +00:00
* This would migrate the value contained in variable name 'old_variable' into
* the data key 'new_config.sub_key' of the configuration object $config_name .
2012-03-30 03:43:32 +00:00
*/
2012-06-02 19:45:56 +00:00
function update_variables_to_config ( $config_name , array $variable_map ) {
// Build the new configuration object.
// This potentially loads an existing configuration object, in case another
// update function migrated configuration values into $config_name already.
2013-09-16 03:58:06 +00:00
$config = \Drupal :: config ( $config_name );
2012-06-02 19:45:56 +00:00
$original_data = $config -> get ();
// Extract the module namespace/owner from the configuration object name.
$module = strtok ( $config_name , '.' );
// Load and set default configuration values.
2012-09-04 13:51:51 +00:00
$file = new FileStorage ( drupal_get_path ( 'module' , $module ) . '/config' );
2012-06-29 17:35:06 +00:00
if ( ! $file -> exists ( $config_name )) {
throw new ConfigException ( " Default configuration file $config_name for $module extension not found but is required to exist. " );
}
$default_data = $file -> read ( $config_name );
2012-06-02 19:45:56 +00:00
2012-11-14 10:15:58 +00:00
// Apply the default values.
$config -> setData ( $default_data );
2012-06-02 19:45:56 +00:00
// Merge any possibly existing original data into default values.
// Only relevant when being called repetitively on the same config object.
if ( ! empty ( $original_data )) {
2012-11-14 10:15:58 +00:00
$config -> merge ( $original_data );
2012-06-02 19:45:56 +00:00
}
2012-03-30 03:43:32 +00:00
2012-06-02 19:45:56 +00:00
// Fetch existing variables.
$variables = db_query ( 'SELECT name, value FROM {variable} WHERE name IN (:variables)' , array ( ':variables' => array_keys ( $variable_map ))) -> fetchAllKeyed ();
// Set configuration values according to the provided variable mapping.
foreach ( $variable_map as $variable_name => $config_key ) {
// This function migrates variables regardless of their value, including
// NULL values. Any possibly required customizations need to be performed
// manually, either via variable_set() before calling this function or via
2013-09-16 03:58:06 +00:00
// \Drupal::config() after calling this function.
2012-06-02 19:45:56 +00:00
if ( isset ( $variables [ $variable_name ])) {
$value = unserialize ( $variables [ $variable_name ]);
$config -> set ( $config_key , $value );
2012-03-30 03:43:32 +00:00
}
}
2012-06-02 19:45:56 +00:00
// Save the configuration object.
$config -> save ();
// Delete the migrated variables.
db_delete ( 'variable' ) -> condition ( 'name' , array_keys ( $variable_map ), 'IN' ) -> execute ();
2012-03-30 03:43:32 +00:00
}
2012-10-08 16:39:59 +00:00
2013-05-07 09:47:19 +00:00
/**
* Installs a default configuration file into the active store .
*
* Provide a generalised method to save a default configuration object for an
* already enabled module or theme as part of an update from Drupal 7 to Drupal
* 8 ' s configuration management system .
*
* @ param string $type
* The extension type ; e . g . , 'module' or 'theme' .
* @ param string $config_name
* The configuration object name to retrieve .
2013-07-21 07:44:33 +00:00
* @ param string $name
* ( optional ) The owner of the config . Defaults to NULL , in which case the
* name will be derived from the $config_name .
2013-05-07 09:47:19 +00:00
*
* @ return boolean
* True on success , false if config file does not exist .
*/
2013-07-21 07:44:33 +00:00
function update_install_default_config ( $type , $config_name , $name = NULL ) {
2013-05-07 09:47:19 +00:00
// Build the new configuration object.
2013-09-16 03:58:06 +00:00
$config = \Drupal :: config ( $config_name );
2013-05-07 09:47:19 +00:00
// Extract the extension namespace/owner from the configuration object name.
2013-07-21 07:44:33 +00:00
if ( ! $name ) {
$name = strtok ( $config_name , '.' );
}
2013-05-07 09:47:19 +00:00
// Load and set default configuration values.
$file = new FileStorage ( drupal_get_path ( $type , $name ) . '/config' );
if ( ! $file -> exists ( $config_name )) {
return FALSE ;
}
2013-05-30 06:18:49 +00:00
2013-05-07 09:47:19 +00:00
// Apply and save the default values.
$config -> setData ( $file -> read ( $config_name )) -> save ();
return TRUE ;
}
2012-10-27 21:15:15 +00:00
/**
* Updates 7. x variables to state records .
*
* Provides a generalized method to migrate variables from 7. x to 8. x ' s
2013-09-16 03:58:06 +00:00
* \Drupal :: state () system .
2012-10-27 21:15:15 +00:00
*
* @ param array $variable_map
* An associative array that maps old variables names to new state record
* names ; e . g .:
* @ code
* array ( 'old_variable' => 'extension.new_name' )
* @ endcode
* This would migrate the value contained in variable name 'old_variable' into
* the state item 'extension.new_name' .
* Non - existing variables and variables with NULL values are omitted .
*/
function update_variables_to_state ( array $variable_map ) {
foreach ( $variable_map as $variable_name => $state_name ) {
if ( NULL !== $value = update_variable_get ( $variable_name )) {
2013-09-16 03:58:06 +00:00
\Drupal :: state () -> set ( $state_name , $value );
2012-10-27 21:15:15 +00:00
}
}
// Delete the migrated variables.
db_delete ( 'variable' )
-> condition ( 'name' , array_keys ( $variable_map ))
-> execute ();
}
2012-10-08 16:39:59 +00:00
/**
* Helper function to update entities with uuid .
*
* @ param array $sandbox
* A sandbox where conversion happens .
* @ param string $table
* A table whose data should be updated .
* @ param string $primary_key
* A $table primary key column .
* @ param array $values
* A $primary_key values of rows to be updated .
*/
function update_add_uuids ( & $sandbox , $table , $primary_key , $values ) {
2013-09-24 22:16:05 +00:00
$uuid = \Drupal :: service ( 'uuid' );
2012-10-08 16:39:59 +00:00
foreach ( $values as $value ) {
db_update ( $table )
-> fields ( array (
'uuid' => $uuid -> generate (),
))
-> condition ( $primary_key , $value )
-> isNull ( 'uuid' )
-> execute ();
$sandbox [ 'progress' ] ++ ;
$sandbox [ 'last' ] = $value ;
}
}
2013-01-10 12:08:06 +00:00
/**
* Adds tags , checksum_invalidations , checksum_deletions to a cache table .
*
* @ param string $table
* Name of the cache table .
*/
function update_add_cache_columns ( $table ) {
if ( db_table_exists ( $table ) && ! db_field_exists ( $table , 'tags' )) {
db_add_field ( $table , 'tags' , array (
'description' => 'Space-separated list of cache tags for this entry.' ,
'type' => 'text' ,
'size' => 'big' ,
'not null' => FALSE ,
));
db_add_field ( $table , 'checksum_invalidations' , array (
'description' => 'The tag invalidation sum when this entry was saved.' ,
'type' => 'int' ,
'not null' => TRUE ,
'default' => 0 ,
));
db_add_field ( $table , 'checksum_deletions' , array (
'description' => 'The tag deletion sum when this entry was saved.' ,
'type' => 'int' ,
'not null' => TRUE ,
'default' => 0 ,
));
}
}
2013-06-24 16:38:32 +00:00
/**
* Replace permissions during update .
*
* This function can replace one permission to several or even delete an old
* one .
*
* @ param array $replace
* An associative array . The keys are the old permissions the values are lists
* of new permissions . If the list is an empty array , the old permission is
* removed .
*/
function update_replace_permissions ( $replace ) {
$prefix = 'user.role.' ;
$cut = strlen ( $prefix );
2013-09-16 03:58:06 +00:00
$role_names = \Drupal :: service ( 'config.storage' ) -> listAll ( $prefix );
2013-06-24 16:38:32 +00:00
foreach ( $role_names as $role_name ) {
$rid = substr ( $role_name , $cut );
2013-09-16 03:58:06 +00:00
$config = \Drupal :: config ( " user.role. $rid " );
2013-06-24 16:38:32 +00:00
$permissions = $config -> get ( 'permissions' ) ? : array ();
foreach ( $replace as $old_permission => $new_permissions ) {
if (( $index = array_search ( $old_permission , $permissions )) !== FALSE ) {
unset ( $permissions [ $index ]);
$permissions = array_unique ( array_merge ( $permissions , $new_permissions ));
}
}
$config
-> set ( 'permissions' , $permissions )
-> save ();
}
}
2013-06-29 10:56:53 +00:00
/**
* Returns a list of languages set up on the site during upgrades .
*
* @ param $flags
* ( optional ) Specifies the state of the languages that have to be returned .
* It can be : Language :: STATE_CONFIGURABLE , Language :: STATE_LOCKED ,
* Language :: STATE_ALL .
*
* @ return array
* An associative array of languages , keyed by the language code , ordered by
* weight ascending and name ascending .
*/
function update_language_list ( $flags = Language :: STATE_CONFIGURABLE ) {
$languages = & drupal_static ( __FUNCTION__ );
// Initialize master language list.
if ( ! isset ( $languages )) {
// Initialize local language list cache.
$languages = array ();
// Fill in master language list based on current configuration.
$default = language_default ();
2013-10-16 12:17:55 +00:00
if ( language_multilingual () || \Drupal :: moduleHandler () -> moduleExists ( 'language' )) {
2013-06-29 10:56:53 +00:00
// Use language module configuration if available. We can not use
// entity_load_multiple() because this breaks during updates.
2014-01-01 00:15:45 +00:00
$language_entities = config_get_storage_names_with_prefix ( 'language.entity.' );
2013-06-29 10:56:53 +00:00
// Initialize default property so callers have an easy reference and can
// save the same object without data loss.
foreach ( $language_entities as $langcode_config_name ) {
$langcode = substr ( $langcode_config_name , strlen ( 'language.entity.' ));
2013-09-16 03:58:06 +00:00
$info = \Drupal :: config ( $langcode_config_name ) -> get ();
2013-06-29 10:56:53 +00:00
$languages [ $langcode ] = new Language ( array (
'default' => ( $info [ 'id' ] == $default -> id ),
'name' => $info [ 'label' ],
'id' => $info [ 'id' ],
'direction' => $info [ 'direction' ],
'locked' => $info [ 'locked' ],
'weight' => $info [ 'weight' ],
));
}
Language :: sort ( $languages );
}
else {
// No language module, so use the default language only.
$languages = array ( $default -> id => $default );
// Add the special languages, they will be filtered later if needed.
$languages += language_default_locked_languages ( $default -> weight );
}
}
// Filter the full list of languages based on the value of the $all flag. By
// default we remove the locked languages, but the caller may request for
// those languages to be added as well.
$filtered_languages = array ();
// Add the site's default language if flagged as allowed value.
if ( $flags & Language :: STATE_SITE_DEFAULT ) {
$default = isset ( $default ) ? $default : language_default ();
// Rename the default language.
$default -> name = t ( " Site's default language (@lang_name) " , array ( '@lang_name' => $default -> name ));
$filtered_languages [ 'site_default' ] = $default ;
}
foreach ( $languages as $langcode => $language ) {
if (( $language -> locked && ! ( $flags & Language :: STATE_LOCKED )) || ( ! $language -> locked && ! ( $flags & Language :: STATE_CONFIGURABLE ))) {
continue ;
}
$filtered_languages [ $langcode ] = $language ;
}
return $filtered_languages ;
}