diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 027e070..30e3c3b 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -239,6 +239,8 @@ const REGISTRY_WRITE_LOOKUP_CACHE = 2;
*/
const DRUPAL_PHP_FUNCTION_PATTERN = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*';
+require_once DRUPAL_ROOT . '/core/includes/config.inc';
+
/**
* Provides a caching wrapper to be used in place of large array structures.
*
@@ -766,7 +768,7 @@ function drupal_settings_initialize() {
global $base_url, $base_path, $base_root;
// Export the following settings.php variables to the global namespace
- global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url;
+ global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url, $config_directory_name, $config_signature_key;
$conf = array();
if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
@@ -1342,8 +1344,10 @@ function drupal_page_header() {
* response is sent.
*/
function drupal_serve_page_from_cache(stdClass $cache) {
+ $config = config('system.performance');
+
// Negotiate whether to use compression.
- $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
+ $page_compression = $config->get('page_compression') && extension_loaded('zlib');
$return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
// Get headers set in hook_boot(). Keys are lower-case.
@@ -1369,7 +1373,7 @@ function drupal_serve_page_from_cache(stdClass $cache) {
// max-age > 0, allowing the page to be cached by external proxies, when a
// session cookie is present unless the Vary header has been replaced or
// unset in hook_boot().
- $max_age = !isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary']) ? variable_get('page_cache_maximum_age', 0) : 0;
+ $max_age = !isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary']) ? $config->get('page_cache_maximum_age') : 0;
$default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
// Entity tag should change if the output changes.
@@ -2335,7 +2339,8 @@ function _drupal_bootstrap_page_cache() {
}
else {
drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE);
- $cache_enabled = variable_get('cache');
+ $config = config('system.performance');
+ $cache_enabled = $config->get('cache');
}
drupal_block_denied(ip_address());
// If there is no session cookie and cache is enabled (or forced), try
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 757a0c6..6d1c44b 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -2597,7 +2597,8 @@ function drupal_page_footer() {
// Commit the user session, if needed.
drupal_session_commit();
- if (variable_get('cache', 0) && ($cache = drupal_page_set_cache())) {
+ $config = config('system.performance');
+ if ($config->get('cache') && ($cache = drupal_page_set_cache())) {
drupal_serve_page_from_cache($cache);
}
else {
@@ -3175,7 +3176,14 @@ function drupal_group_css($css) {
* @see system_element_info()
*/
function drupal_aggregate_css(&$css_groups) {
- $preprocess_css = (variable_get('preprocess_css', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update'));
+ // Only aggregate during normal site operation.
+ if (defined('MAINTENANCE_MODE')) {
+ $preprocess_css = FALSE;
+ }
+ else {
+ $config = config('system.performance');
+ $preprocess_css = $config->get('preprocess_css');
+ }
// For each group that needs aggregation, aggregate its items.
foreach ($css_groups as $key => $group) {
@@ -4408,9 +4416,16 @@ function drupal_group_js($javascript) {
* @see drupal_pre_render_scripts()
*/
function drupal_aggregate_js(&$js_groups) {
- // Only aggregate when the site is configured to do so, and not during an
- // update.
- if (variable_get('preprocess_js', FALSE) && (!defined('MAINTENANCE_MODE') || MAINTENANCE_MODE != 'update')) {
+ // Only aggregate during normal site operation.
+ if (defined('MAINTENANCE_MODE')) {
+ $preprocess_js = FALSE;
+ }
+ else {
+ $config = config('system.performance');
+ $preprocess_js = $config->get('preprocess_js');
+ }
+
+ if ($preprocess_js) {
foreach ($js_groups as $key => $group) {
if ($group['type'] == 'file' && $group['preprocess']) {
$js_groups[$key]['data'] = drupal_build_js_cache($group['items']);
@@ -5185,7 +5200,7 @@ function drupal_page_set_cache() {
}
if ($cache->data['body']) {
- if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
+ if (config('system.performance')->get('page_compression') && extension_loaded('zlib')) {
$cache->data['body'] = gzencode($cache->data['body'], 9, FORCE_GZIP);
}
cache('page')->set($cache->cid, $cache->data, $cache->expire);
diff --git a/core/includes/config.inc b/core/includes/config.inc
new file mode 100644
index 0000000..8c2772b
--- /dev/null
+++ b/core/includes/config.inc
@@ -0,0 +1,242 @@
+ $file) {
+ // Load config data into the active store and write it out to the
+ // file system in the drupal config directory. Note the config name
+ // needs to be the same as the file name WITHOUT the extension.
+ $parts = explode('/', $file);
+ $file = array_pop($parts);
+ $config_name = str_replace('.xml', '', $file);
+
+ $verified_storage = new DrupalVerifiedStorageSQL($config_name);
+ $verified_storage->write(file_get_contents($module_config_dir . '/' . $file));
+ }
+ }
+}
+
+/**
+ * Retrieves an iterable array which lists the children under a config 'branch'.
+ *
+ * Given the following configuration files:
+ * - core.entity.node_type.article.xml
+ * - core.entity.node_type.page.xml
+ *
+ * You can pass a prefix 'core.entity.node_type' and get back an array of the
+ * filenames that match. This allows you to iterate through all files in a
+ * branch.
+ *
+ * @param $prefix
+ * The prefix of the files we are searching for.
+ *
+ * @return
+ * An array of file names under a branch.
+ */
+function config_get_signed_file_storage_names_with_prefix($prefix = '') {
+ $files = glob(config_get_config_directory() . '/' . $prefix . '*.xml');
+ $clean_name = function ($value) {
+ return basename($value, '.xml');
+ };
+ return array_map($clean_name, $files);
+}
+
+/**
+ * Generates a hash of a config file's contents using our encryption key.
+ *
+ * @param $data
+ * The contents of a configuration file.
+ *
+ * @return
+ * A hash of the data.
+ */
+function config_sign_data($data) {
+ // The configuration key is loaded from settings.php and imported into the global namespace
+ global $config_signature_key;
+
+ // SHA-512 is both secure and very fast on 64 bit CPUs.
+ return hash_hmac('sha512', $data, $config_signature_key);
+}
+
+/**
+ * @todo
+ *
+ * @param $prefix
+ * @todo
+ *
+ * @return
+ * @todo
+ */
+function config_get_verified_storage_names_with_prefix($prefix = '') {
+ return DrupalVerifiedStorageSQL::getNamesWithPrefix($prefix);
+}
+
+/**
+ * Retrieves a configuration object.
+ *
+ * This is the main entry point to the configuration API. Calling
+ * @code config(book.admin) @endcode will return a configuration object in which
+ * the book module can store its administrative settings.
+ *
+ * @param $name
+ * The name of the configuration object to retrieve. The name corresponds to
+ * an XML configuration file. For @code config(book.admin) @endcode, the
+ * config object returned will contain the contents of book.admin.xml.
+ * @param $class
+ * The class name of the config object to be returned. Defaults to
+ * DrupalConfig.
+ *
+ * @return
+ * An instance of the class specified in the $class parameter.
+ *
+ * @todo Replace this with an appropriate factory / ability to inject in
+ * alternate storage engines..
+ */
+function config($name, $class = 'Drupal\Core\Config\DrupalConfig') {
+ return new $class(new DrupalVerifiedStorageSQL($name));
+}
+
+/**
+ * Decodes configuration data from its native format to an associative array.
+ *
+ * @param $data
+ * Configuration data.
+ *
+ * @return
+ * An associative array representation of the data.
+ */
+function config_decode($data) {
+ if (empty($data)) {
+ return array();
+ }
+
+ // This is the fastest and easiest way to get from a string of XML to a PHP
+ // array since SimpleXML and json_decode()/encode() are native to PHP. Our
+ // only other choice would be a custom userspace implementation which would
+ // be a lot less performant and more complex.
+ $xml = new SimpleXMLElement($data);
+ $json = json_encode($xml);
+ return json_decode($json, TRUE);
+}
+
+/**
+ * Standardizes SimpleXML object output into simple arrays for easier use.
+ *
+ * @param $xmlObject
+ * A valid XML string.
+ *
+ * @return
+ * An array representation of A SimpleXML object.
+ */
+function config_xml_to_array($data) {
+ $out = array();
+ $xmlObject = simplexml_load_string($data);
+
+ if (is_object($xmlObject)) {
+ $attributes = (array) $xmlObject->attributes();
+ if (isset($attributes['@attributes'])) {
+ $out['#attributes'] = $attributes['@attributes'];
+ }
+ }
+ if (trim((string) $xmlObject)) {
+ return trim((string) $xmlObject);
+ }
+ foreach ($xmlObject as $index => $content) {
+ if (is_object($content)) {
+ $out[$index] = config_xml_to_array($content);
+ }
+ }
+
+ return $out;
+}
+
+/**
+ * Encodes an array into the native configuration format.
+ *
+ * @param $data
+ * An associative array or an object
+ *
+ * @return
+ * A representation of this array or object in the native configuration
+ * format.
+ *
+ * @todo The loaded XML can be invalid; throwing plenty of PHP warnings but no
+ * catchable error.
+ */
+function config_encode($data) {
+ // Convert the supplied array into a SimpleXMLElement.
+ $xml_object = new SimpleXMLElement("");
+ config_array_to_xml($data, $xml_object);
+
+ // Pretty print the result.
+ $dom = new DOMDocument('1.0');
+ $dom->preserveWhiteSpace = false;
+ $dom->formatOutput = true;
+ $dom->loadXML($xml_object->asXML());
+
+ return $dom->saveXML();
+}
+
+/**
+ * Encodes an array into XML
+ *
+ * @param $data
+ * An associative array or an object
+ *
+ * @return
+ * A representation of this array or object in the native configuration
+ * format.
+ */
+function config_array_to_xml($array, &$xml_object) {
+ foreach ($array as $key => $value) {
+ if (is_array($value)) {
+ if (!is_numeric($key)){
+ $subnode = $xml_object->addChild("$key");
+ config_array_to_xml($value, $subnode);
+ }
+ else {
+ config_array_to_xml($value, $xml_object);
+ }
+ }
+ else {
+ $xml_object->addChild($key, $value);
+ }
+ }
+}
diff --git a/core/includes/file.inc b/core/includes/file.inc
index 0e69bfe..6ac7b92 100644
--- a/core/includes/file.inc
+++ b/core/includes/file.inc
@@ -464,6 +464,7 @@ function file_ensure_htaccess() {
file_save_htaccess('private://', TRUE);
}
file_save_htaccess('temporary://', TRUE);
+ file_save_htaccess(config_get_config_directory(), TRUE);
}
/**
diff --git a/core/includes/form.inc b/core/includes/form.inc
index 853990b..a8b53b3 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -865,7 +865,8 @@ function drupal_process_form($form_id, &$form, &$form_state) {
// We'll clear out the cached copies of the form and its stored data
// here, as we've finished with them. The in-memory copies are still
// here, though.
- if (!variable_get('cache', 0) && !empty($form_state['values']['form_build_id'])) {
+ $config = config('system.performance');
+ if (!$config->get('cache') && !empty($form_state['values']['form_build_id'])) {
cache('form')->delete('form_' . $form_state['values']['form_build_id']);
cache('form')->delete('form_state_' . $form_state['values']['form_build_id']);
}
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index ef7eafe..d451488 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -986,7 +986,36 @@ function install_settings_form_submit($form, &$form_state) {
'value' => drupal_hash_base64(drupal_random_bytes(55)),
'required' => TRUE,
);
+
+ $settings['config_signature_key'] = array(
+ 'value' => drupal_hash_base64(drupal_random_bytes(55)),
+ 'required' => TRUE,
+ );
+
+ // This duplicates drupal_get_token() because that function can't work yet.
+ // Wondering if it makes sense to move this later in the process, but its
+ // nice having all the settings stuff here.
+ //
+ // @todo This is actually causing a bug right now, because you can install
+ // without hitting install_settings_form_submit() if your settings.php
+ // already has the db stuff in it, and right now in that case your
+ // config directory never gets created. So this needs to be moved elsewhere.
+ $settings['config_directory_name'] = array(
+ 'value' => 'config_' . drupal_hmac_base64('', session_id() . $settings['config_signature_key']['value'] . $settings['drupal_hash_salt']['value']),
+ 'required' => TRUE,
+ );
+
drupal_rewrite_settings($settings);
+ // Actually create the config directory named above.
+ $config_path = conf_path() . '/files/' . $settings['config_directory_name']['value'];
+ if (!file_prepare_directory($config_path, FILE_CREATE_DIRECTORY)) {
+ // How best to handle errors here?
+ };
+
+ // Write out a .htaccess file that will protect the config directory from
+ // prying eyes.
+ file_save_htaccess($config_path, TRUE);
+
// Indicate that the settings file has been verified, and check the database
// for the last completed task, now that we have a valid connection. This
// last step is important since we want to trigger an error if the new
diff --git a/core/includes/install.inc b/core/includes/install.inc
index 533678f..a7b4dc7 100644
--- a/core/includes/install.inc
+++ b/core/includes/install.inc
@@ -730,6 +730,7 @@ function drupal_install_system() {
))
->execute();
system_rebuild_module_data();
+ config_install_default_config('system');
}
/**
diff --git a/core/includes/language.inc b/core/includes/language.inc
index f12f34a..f9b5c3b 100644
--- a/core/includes/language.inc
+++ b/core/includes/language.inc
@@ -375,7 +375,8 @@ function language_provider_invoke($provider_id, $provider = NULL) {
// If the language provider has no cache preference or this is satisfied
// we can execute the callback.
- $cache = !isset($provider['cache']) || $user->uid || $provider['cache'] == variable_get('cache', 0);
+ $config = config('system.performance');
+ $cache = !isset($provider['cache']) || $user->uid || $provider['cache'] == $config->get('cache');
$callback = isset($provider['callbacks']['language']) ? $provider['callbacks']['language'] : FALSE;
$langcode = $cache && function_exists($callback) ? $callback($languages) : FALSE;
$results[$provider_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE;
diff --git a/core/includes/module.inc b/core/includes/module.inc
index d61aba9..aa9eaf5 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -461,6 +461,9 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
$versions = drupal_get_schema_versions($module);
$version = $versions ? max($versions) : SCHEMA_INSTALLED;
+ // Copy any default configuration data to the system config directory/
+ config_install_default_config($module);
+
// If the module has no current updates, but has some that were
// previously removed, set the version to the value of
// hook_update_last_removed().
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 37af13c..7afd698 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -101,7 +101,8 @@ class DatabaseBackend implements CacheBackendInterface {
// timer. The cache variable is loaded into the $user object by
// _drupal_session_read() in session.inc. If the data is permanent or we're
// not enforcing a minimum cache lifetime always return the cached data.
- if ($cache->expire != CACHE_PERMANENT && variable_get('cache_lifetime', 0) && $user->cache > $cache->created) {
+ $config = config('system.performance');
+ if ($cache->expire != CACHE_PERMANENT && $config->get('cache_lifetime') && $user->cache > $cache->created) {
// This cache data is too old and thus not valid for us, ignore it.
return FALSE;
}
diff --git a/core/lib/Drupal/Core/Config/ConfigException.php b/core/lib/Drupal/Core/Config/ConfigException.php
new file mode 100644
index 0000000..c60a449
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/ConfigException.php
@@ -0,0 +1,8 @@
+_verifiedStorage = $verified_storage;
+ $this->read();
+ }
+
+ /**
+ * Reads config data from the active store into our object.
+ */
+ public function read() {
+ $active = (array) config_decode($this->_verifiedStorage->read());
+ foreach ($active as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * Checks whether a particular value is overridden.
+ *
+ * @param $key
+ * @todo
+ *
+ * @return
+ * @todo
+ */
+ public function isOverridden($key) {
+ return isset($this->_overrides[$key]);
+ }
+
+ /**
+ * Gets data from this config object.
+ *
+ * @param $key
+ * A string that maps to a key within the configuration data.
+ * For instance in the following XML:
+ * @code
+ *
+ * baz
+ *
+ * @endcode
+ * A key of 'foo.bar' would return the string 'baz'. However, a key of 'foo'
+ * would return array('bar' => 'baz').
+ * If no key is specified, then the entire data array is returned.
+ *
+ * The configuration system does not retain data types. Every saved value is
+ * casted to a string. In most cases this is not an issue; however, it can
+ * cause issues with Booleans, which are casted to "1" (TRUE) or "0" (FALSE).
+ * In particular, code relying on === or !== will no longer function properly.
+ *
+ * @see http://php.net/manual/en/language.operators.comparison.php.
+ *
+ * @return
+ * The data that was requested.
+ */
+ public function get($key = '') {
+ if (empty($key)) {
+ return $this->data;
+ }
+ else {
+ $parts = explode('.', $key);
+ if (count($parts) == 1) {
+ return isset($this->data[$key]) ? $this->data[$key] : NULL;
+ }
+ else {
+ $key_exists = NULL;
+ $value = drupal_array_get_nested_value($this->data, $parts, $key_exists);
+ return $key_exists ? $value : NULL;
+ }
+ }
+ }
+
+ /**
+ * Sets value in this config object.
+ *
+ * @param $key
+ * @todo
+ * @param $value
+ * @todo
+ */
+ public function set($key, $value) {
+ // Remove all non-alphanumeric characters from the key.
+ // @todo Reverse this and throw an exception when encountering a key with
+ // invalid name. The identical validation also needs to happen in get().
+ // Furthermore, the dot/period is a reserved character; it may appear
+ // between keys, but not within keys.
+ $key = preg_replace('@[^a-zA-Z0-9_.-]@', '', $key);
+
+ // Type-cast value into a string.
+ $value = $this->castValue($value);
+
+ $parts = explode('.', $key);
+ if (count($parts) == 1) {
+ $this->data[$key] = $value;
+ }
+ else {
+ drupal_array_set_nested_value($this->data, $parts, $value);
+ }
+ return $this;
+ }
+
+ /**
+ * Casts a saved value to a string.
+ *
+ * The configuration system only saves strings or arrays. Any scalar
+ * non-string value is cast to a string. The one exception is boolean FALSE
+ * which would normally become '' when cast to a string, but is manually
+ * cast to '0' here for convenience and consistency.
+ *
+ * Any non-scalar value that is not an array (aka objects) gets cast
+ * to an array.
+ *
+ * @param $value
+ * A value being saved into the configuration system.
+ * @param $value
+ * The value cast to a string or array.
+ */
+ public function castValue($value) {
+ if (is_scalar($value)) {
+ // Handle special case of FALSE, which should be '0' instead of ''.
+ if ($value === FALSE) {
+ $value = '0';
+ }
+ else {
+ $value = (string) $value;
+ }
+ }
+ else {
+ // Any non-scalar value must be an array.
+ if (!is_array($value)) {
+ $value = (array) $value;
+ }
+ // Recurse into any nested keys.
+ foreach ($value as $key => $nested_value) {
+ $value[$key] = $this->castValue($nested_value);
+ }
+ }
+ return $value;
+ }
+
+ /**
+ * Unsets value in this config object.
+ *
+ * @param $key
+ * Name of the key whose value should be unset.
+ */
+ public function clear($key) {
+ $parts = explode('.', $key);
+ if (count($parts) == 1) {
+ unset($this->data[$key]);
+ }
+ else {
+ drupal_array_unset_nested_value($this->data, $parts);
+ }
+ }
+
+ /**
+ * Saves the configuration object to disk as XML.
+ */
+ public function save() {
+ $this->_verifiedStorage->write(config_encode($this->data));
+ }
+
+ /**
+ * Deletes the configuration object on disk.
+ */
+ public function delete() {
+ $this->data = array();
+ $this->_verifiedStorage->delete();
+ }
+}
diff --git a/core/lib/Drupal/Core/Config/DrupalConfigVerifiedStorage.php b/core/lib/Drupal/Core/Config/DrupalConfigVerifiedStorage.php
new file mode 100644
index 0000000..ca37cdd
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/DrupalConfigVerifiedStorage.php
@@ -0,0 +1,102 @@
+name = $name;
+ }
+
+ /**
+ * Instantiates a new signed file object or returns the existing one.
+ *
+ * @return SignedFileStorage
+ * The signed file object for this configuration object.
+ */
+ protected function signedFileStorage() {
+ if (!isset($this->signedFile)) {
+ $this->signedFile = new SignedFileStorage($this->name);
+ }
+ return $this->signedFile;
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::copyToFile().
+ */
+ public function copyToFile() {
+ return $this->writeToFile($this->read());
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::deleteFile().
+ */
+ public function deleteFile() {
+ return $this->signedFileStorage()->delete();
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::copyFromFile().
+ */
+ public function copyFromFile() {
+ return $this->writeToActive($this->readFromFile());
+ }
+
+ /**
+ * @todo
+ *
+ * @return
+ * @todo
+ */
+ public function readFromFile() {
+ return $this->signedFileStorage()->read($this->name);
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::isOutOfSync().
+ */
+ public function isOutOfSync() {
+ return $this->read() !== $this->readFromFile();
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::write().
+ */
+ public function write($data) {
+ $this->writeToActive($data);
+ $this->writeToFile($data);
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::writeToFile().
+ */
+ public function writeToFile($data) {
+ return $this->signedFileStorage()->write($data);
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::delete().
+ */
+ public function delete() {
+ $this->deleteFromActive();
+ $this->deleteFile();
+ }
+}
diff --git a/core/lib/Drupal/Core/Config/DrupalConfigVerifiedStorageInterface.php b/core/lib/Drupal/Core/Config/DrupalConfigVerifiedStorageInterface.php
new file mode 100644
index 0000000..2fdce76
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/DrupalConfigVerifiedStorageInterface.php
@@ -0,0 +1,84 @@
+ $this->name))->fetchField();
+ } catch (Exception $e) {
+ return array();
+ }
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::writeToActive().
+ */
+ public function writeToActive($data) {
+ return db_merge('config')
+ ->key(array('name' => $this->name))
+ ->fields(array('data' => $data))
+ ->execute();
+ }
+
+ /**
+ * @todo
+ */
+ public function deleteFromActive() {
+ db_delete('config')
+ ->condition('name', $this->name)
+ ->execute();
+ }
+
+ /**
+ * Implements DrupalConfigVerifiedStorageInterface::getNamesWithPrefix().
+ */
+ static public function getNamesWithPrefix($prefix = '') {
+ return db_query('SELECT name FROM {config} WHERE name LIKE :name', array(':name' => db_like($prefix) . '%'))->fetchCol();
+ }
+}
diff --git a/core/lib/Drupal/Core/Config/SignedFileStorage.php b/core/lib/Drupal/Core/Config/SignedFileStorage.php
new file mode 100644
index 0000000..c669029
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/SignedFileStorage.php
@@ -0,0 +1,143 @@
+name = $name;
+ }
+
+ /**
+ * Reads and returns a signed file and its signature.
+ *
+ * @return
+ * An array with "signature" and "data" keys.
+ *
+ * @throws
+ * Exception
+ */
+ protected function readWithSignature() {
+ $content = file_get_contents($this->getFilePath());
+ if ($content === FALSE) {
+ throw new \Exception('Read file is invalid.');
+ }
+ $signature = file_get_contents($this->getFilePath() . '.sig');
+ if ($signature === FALSE) {
+ throw new \Exception('Signature file is invalid.');
+ }
+ return array('data' => $content, 'signature' => $signature);
+ }
+
+ /**
+ * Checks whether the XML configuration file already exists on disk.
+ *
+ * @return
+ * @todo
+ */
+ protected function exists() {
+ return file_exists($this->getFilePath());
+ }
+
+ /**
+ * Returns the path to the XML configuration file.
+ *
+ * @return
+ * @todo
+ */
+ public function getFilePath() {
+ return config_get_config_directory() . '/' . $this->name . '.xml';
+ }
+
+ /**
+ * Recreates the signature for the file.
+ */
+ public function resign() {
+ if ($this->exists()) {
+ $parts = $this->readWithSignature();
+ $this->write($parts['data']);
+ }
+ }
+
+ /**
+ * Cryptographically verifies the integrity of the configuration file.
+ *
+ * @param $contentOnSuccess
+ * Whether or not to return the contents of the verified configuration file.
+ *
+ * @return mixed
+ * If $contentOnSuccess was TRUE, returns the contents of the verified
+ * configuration file; otherwise returns TRUE on success. Always returns
+ * FALSE if the configuration file was not successfully verified.
+ */
+ public function verify($contentOnSuccess = FALSE) {
+ if ($this->exists()) {
+ $split = $this->readWithSignature();
+ $expected_signature = config_sign_data($split['data']);
+ if ($expected_signature === $split['signature']) {
+ if ($contentOnSuccess) {
+ return $split['data'];
+ }
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ /**
+ * Writes the contents of the configuration file to disk.
+ *
+ * @param $data
+ * The data to be written to the file.
+ *
+ * @throws
+ * Exception
+ */
+ public function write($data) {
+ $signature = config_sign_data($data);
+ if (!file_put_contents($this->getFilePath(), $data)) {
+ throw new \Exception('Failed to write configuration file: ' . $this->getFilePath());
+ }
+ if (!file_put_contents($this->getFilePath() . '.sig', $signature)) {
+ throw new \Exception('Failed to write signature file: ' . $this->getFilePath());
+ }
+ }
+
+ /**
+ * Returns the contents of the configuration file.
+ *
+ * @return
+ * @todo
+ */
+ public function read() {
+ if ($this->exists()) {
+ $verification = $this->verify(TRUE);
+ if ($verification === FALSE) {
+ throw new \Exception('Invalid signature in file header.');
+ }
+ return $verification;
+ }
+ }
+
+ /**
+ * Deletes a configuration file.
+ */
+ public function delete() {
+ // Needs error handling and etc.
+ @drupal_unlink($this->getFilePath());
+ @drupal_unlink($this->getFilePath() . '.sig');
+ }
+}
diff --git a/core/modules/block/config/block.performance.xml b/core/modules/block/config/block.performance.xml
new file mode 100644
index 0000000..ff3f7e1
--- /dev/null
+++ b/core/modules/block/config/block.performance.xml
@@ -0,0 +1,4 @@
+
+
+ 0
+
diff --git a/core/modules/color/color.test b/core/modules/color/color.test
index 06e4cb9..616d19a 100644
--- a/core/modules/color/color.test
+++ b/core/modules/color/color.test
@@ -92,7 +92,9 @@ class ColorTestCase extends DrupalWebTestCase {
$this->assertTrue(strpos($stylesheet_content, 'color: ' . $test_values['scheme_color']) !== FALSE, 'Make sure the color we changed is in the color stylesheet. (' . $theme . ')');
// Test with aggregated CSS turned on.
- variable_set('preprocess_css', 1);
+ $config = config('system.performance');
+ $config->set('preprocess_css', 1);
+ $config->save();
$this->drupalGet('');
$stylesheets = variable_get('drupal_css_cache_files', array());
$stylesheet_content = '';
@@ -100,7 +102,8 @@ class ColorTestCase extends DrupalWebTestCase {
$stylesheet_content .= join("\n", file(drupal_realpath($uri)));
}
$this->assertTrue(strpos($stylesheet_content, 'public://') === FALSE, 'Make sure the color paths have been translated to local paths. (' . $theme . ')');
- variable_set('preprocess_css', 0);
+ $config->set('preprocess_css', 0);
+ $config->save();
}
/**
diff --git a/core/modules/config/config.info b/core/modules/config/config.info
new file mode 100644
index 0000000..643a3fc
--- /dev/null
+++ b/core/modules/config/config.info
@@ -0,0 +1,5 @@
+name = Configuration manager
+package = Core
+version = VERSION
+core = 8.x
+files[] = config.test
diff --git a/core/modules/config/config.module b/core/modules/config/config.module
new file mode 100644
index 0000000..b3d9bbc
--- /dev/null
+++ b/core/modules/config/config.module
@@ -0,0 +1 @@
+ 'File security',
+ 'description' => 'Tests security of saved configuration files.',
+ 'group' => 'Configuration',
+ );
+ }
+
+ /**
+ * Tests that a file written by this system has a valid signature.
+ */
+ function testFileVerify() {
+ $file = new SignedFileStorage($this->filename);
+ $file->write($this->testContent);
+
+ $this->assertTrue($file->verify(), 'A file verifies after being written.');
+
+ unset($file);
+
+ // Load the file again, so that there is no stale data from the old object.
+ $file = new SignedFileStorage($this->filename);
+ $this->assertTrue($file->verify(), 'A file verifies after being written and reloaded.');
+ }
+
+ /**
+ * Tests that a file written by this system can be successfully read back.
+ */
+ function testFilePersist() {
+ $file = new SignedFileStorage($this->filename);
+ $file->write($this->testContent);
+
+ unset($file);
+
+ // Reading should throw an exception in case of bad validation.
+ // Note that if any other exception is thrown, we let the test system
+ // handle catching and reporting it.
+ try {
+ $file = new SignedFileStorage($this->filename);
+ $saved_content = $file->read();
+
+ $this->assertEqual($saved_content, $this->testContent, 'A file can be read back successfully.');
+ }
+ catch (Exception $e) {
+ $this->fail('File failed verification when being read.');
+ }
+ }
+
+ /**
+ * Tests that a file fails validation if it's been monkeyed with.
+ */
+ function testFileNotVerify() {
+ $file = new SignedFileStorage($this->filename);
+ $file->write($this->testContent);
+
+ // Manually overwrite the body of the secure file. Note that we skip the
+ // first line, which is reserved for the signature and such, to overwrite
+ // just the payload.
+ $raw_file = new SplFileObject($file->getFilePath(), 'a+');
+ $raw_file->fwrite('Good morning, Detroit!');
+ $raw_file->fflush();
+ unset($raw_file);
+
+ unset($file);
+
+ $file = new SignedFileStorage($this->filename);
+ $this->assertFalse($file->verify(), 'Corrupted file does not verify.');
+ }
+}
+
+/**
+ * Tests reading and writing file contents.
+ */
+class ConfigFileContentTestCase extends DrupalWebTestCase {
+ protected $profile = 'testing';
+
+ protected $fileExtension = 'xml';
+
+ public static function getInfo() {
+ return array(
+ 'name' => 'File content',
+ 'description' => 'Tests reading and writing of configuration files.',
+ 'group' => 'Configuration',
+ );
+ }
+
+ /**
+ * Tests setting, writing, and reading of a configuration setting.
+ */
+ function testReadWriteConfig() {
+ $config_dir = config_get_config_directory();
+ $name = 'foo.bar';
+ $key = 'foo';
+ $value = 'bar';
+ $nested_key = 'biff.bang';
+ $nested_value = 'pow';
+ $array_key = 'array';
+ $array_value = array(
+ 'foo' => 'bar',
+ 'biff' => array(
+ 'bang' => 'pow',
+ )
+ );
+ $casting_array_key = 'casting_array';
+ $casting_array_false_value_key = 'casting_array.cast.false';
+ $casting_array_value = array(
+ 'cast' => array(
+ 'false' => FALSE,
+ )
+ );
+ $nested_array_key = 'nested.array';
+ $true_key = 'true';
+ $false_key = 'false';
+
+ // Attempt to read non-existing configuration.
+ $config = config($name);
+
+ // Verify an configuration object is returned.
+// $this->assertEqual($config->name, $name);
+ $this->assertTrue($config, t('Config object created.'));
+
+ // Verify the configuration object is empty.
+ $this->assertEqual($config->get(), array(), t('New config object is empty.'));
+
+ // Verify nothing was saved.
+ $db_config = db_query('SELECT * FROM {config} WHERE name = :name', array(':name' => $name))->fetch();
+ $this->assertIdentical($db_config, FALSE, t('Active store does not have a record for %name', array('%name' => $name)));
+ $this->assertFalse(file_exists($config_dir . '/' . $name . '.' . $this->fileExtension), 'Configuration file does not exist.');
+
+ // Add a top level value
+ $config = config($name);
+ $config->set($key, $value);
+
+ // Add a nested value
+ $config->set($nested_key, $nested_value);
+
+ // Add an array
+ $config->set($array_key, $array_value);
+
+ // Add a nested array
+ $config->set($nested_array_key, $array_value);
+
+ // Add a boolean false value. Should get cast to 0
+ $config->set($false_key, FALSE);
+
+ // Add a boolean true value. Should get cast to 1
+ $config->set($true_key, TRUE);
+
+ // Add an array with a nested boolean false that should get cast to 0.
+ $config->set($casting_array_key, $casting_array_value);
+ $config->save();
+
+ // Verify the database entry exists.
+ $db_config = db_query('SELECT * FROM {config} WHERE name = :name', array(':name' => $name))->fetch();
+ $this->assertEqual($db_config->name, $name, t('After saving configuration, active store has a record for %name', array('%name' => $name)));
+
+ // Verify the file exists.
+ $this->assertTrue(file_exists($config_dir . '/' . $name . '.' . $this->fileExtension), t('After saving configuration, config file exists.'));
+
+ // Read top level value
+ $config = config($name);
+// $this->assertEqual($config->name, $name);
+ $this->assertTrue($config, 'Config object created.');
+ $this->assertEqual($config->get($key), 'bar', t('Top level configuration value found.'));
+
+ // Read nested value
+ $this->assertEqual($config->get($nested_key), $nested_value, t('Nested configuration value found.'));
+
+ // Read array
+ $this->assertEqual($config->get($array_key), $array_value, t('Top level array configuration value found.'));
+
+ // Read nested array
+ $this->assertEqual($config->get($nested_array_key), $array_value, t('Nested array configuration value found.'));
+
+ // Read a top level value that doesn't exist
+ $this->assertNull($config->get('i_dont_exist'), t('Non-existent top level value returned NULL.'));
+
+ // Read a nested value that doesn't exist
+ $this->assertNull($config->get('i.dont.exist'), t('Non-existent nested value returned NULL.'));
+
+ // Read false value
+ $this->assertEqual($config->get($false_key), '0', t('Boolean FALSE value returned the string \'0\'.'));
+
+ // Read true value
+ $this->assertEqual($config->get($true_key), '1', t('Boolean TRUE value returned the string \'1\'.'));
+
+ // Read false that had been nested in an array value
+ $this->assertEqual($config->get($casting_array_false_value_key), '0', t('Nested boolean FALSE value returned the string \'0\'.'));
+
+ // Unset a top level value
+ $config->clear($key);
+
+ // Unset a nested value
+ $config->clear($nested_key);
+ $config->save();
+ $config = config($name);
+
+ // Read unset top level value
+ $this->assertNull($config->get($key), t('Top level value unset.'));
+
+ // Read unset nested value
+ $this->assertNull($config->get($nested_key), t('Nested value unset.'));
+
+ // Create two new configuration files to test listing
+ $config = config('foo.baz');
+ $config->set($key, $value);
+ $config->save();
+
+ // Test chained set()->save()
+ $chained_name = 'biff.bang';
+ $config = config($chained_name);
+ $config->set($key, $value)->save();
+
+ // Verify the database entry exists from a chained save.
+ $db_config = db_query('SELECT * FROM {config} WHERE name = :name', array(':name' => $chained_name))->fetch();
+ $this->assertEqual($db_config->name, $chained_name, t('After saving configuration by chaining through set(), active store has a record for %name', array('%name' => $chained_name)));
+
+ // Verify the file exists from a chained save.
+ $this->assertTrue(file_exists($config_dir . '/' . $chained_name . '.' . $this->fileExtension), t('After saving configuration by chaining through set(), config file exists.'));
+
+ // Get file listing for all files starting with 'foo'. Should return
+ // two elements.
+ $files = config_get_signed_file_storage_names_with_prefix('foo');
+ $this->assertEqual(count($files), 2, 'Two files listed with the prefix \'foo\'.');
+
+ // Get file listing for all files starting with 'biff'. Should return
+ // one element.
+ $files = config_get_signed_file_storage_names_with_prefix('biff');
+ $this->assertEqual(count($files), 1, 'One file listed with the prefix \'biff\'.');
+
+ // Get file listing for all files starting with 'foo.bar'. Should return
+ // one element.
+ $files = config_get_signed_file_storage_names_with_prefix('foo.bar');
+ $this->assertEqual(count($files), 1, 'One file listed with the prefix \'foo.bar\'.');
+
+ // Get file listing for all files starting with 'bar'. Should return
+ // an empty array.
+ $files = config_get_signed_file_storage_names_with_prefix('bar');
+ $this->assertEqual($files, array(), 'No files listed with the prefix \'bar\'.');
+
+ // Delete the configuration.
+ $config = config($name);
+ $config->delete();
+
+ // Verify the database entry no longer exists.
+ $db_config = db_query('SELECT * FROM {config} WHERE name = :name', array(':name' => $name))->fetch();
+ $this->assertIdentical($db_config, FALSE);
+ $this->assertFalse(file_exists($config_dir . '/' . $name . '.' . $this->fileExtension));
+
+ // Attempt to delete non-existing configuration.
+ }
+}
diff --git a/core/modules/image/config/image.styles.large.xml b/core/modules/image/config/image.styles.large.xml
new file mode 100644
index 0000000..62448c1
--- /dev/null
+++ b/core/modules/image/config/image.styles.large.xml
@@ -0,0 +1,16 @@
+
+
+ large
+
+
+ image_scale
+ image_scale_480_480_1
+
+ 480
+ 480
+ 1
+
+ 0
+
+
+
diff --git a/core/modules/image/config/image.styles.medium.xml b/core/modules/image/config/image.styles.medium.xml
new file mode 100644
index 0000000..d301877
--- /dev/null
+++ b/core/modules/image/config/image.styles.medium.xml
@@ -0,0 +1,16 @@
+
+
+ medium
+
+
+ image_scale
+ image_scale_220_220_1
+
+ 220
+ 220
+ 1
+
+ 0
+
+
+
diff --git a/core/modules/image/config/image.styles.thumbnail.xml b/core/modules/image/config/image.styles.thumbnail.xml
new file mode 100644
index 0000000..385abe6
--- /dev/null
+++ b/core/modules/image/config/image.styles.thumbnail.xml
@@ -0,0 +1,16 @@
+
+
+ thumbnail
+
+
+ image_scale
+ image_scale_100_100_1
+
+ 100
+ 100
+ 1
+
+ 0
+
+
+
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 9643841..539e756 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -38,13 +38,6 @@ function image_style_form($form, &$form_state, $style) {
$title = t('Edit %name style', array('%name' => $style['name']));
drupal_set_title($title, PASS_THROUGH);
- // Adjust this form for styles that must be overridden to edit.
- $editable = (bool) ($style['storage'] & IMAGE_STORAGE_EDITABLE);
-
- if (!$editable && empty($form_state['input'])) {
- drupal_set_message(t('This image style is currently being provided by a module. Click the "Override defaults" button to change its settings.'), 'warning');
- }
-
$form_state['image_style'] = $style;
$form['#tree'] = TRUE;
$form['#attached']['css'][drupal_get_path('module', 'image') . '/image.admin.css'] = array();
@@ -56,27 +49,15 @@ function image_style_form($form, &$form_state, $style) {
'#markup' => theme('image_style_preview', array('style' => $style)),
);
- // Allow the name of the style to be changed, unless this style is
- // provided by a module's hook_default_image_styles().
- if ($style['storage'] & IMAGE_STORAGE_MODULE) {
- $form['name'] = array(
- '#type' => 'item',
- '#title' => t('Image style name'),
- '#markup' => $style['name'],
- '#description' => t('This image style is being provided by %module module and may not be renamed.', array('%module' => $style['module'])),
- );
- }
- else {
- $form['name'] = array(
- '#type' => 'textfield',
- '#size' => '64',
- '#title' => t('Image style name'),
- '#default_value' => $style['name'],
- '#description' => t('The name is used in URLs for generated images. Use only lowercase alphanumeric characters, underscores (_), and hyphens (-).'),
- '#element_validate' => array('image_style_name_validate'),
- '#required' => TRUE,
- );
- }
+ $form['name'] = array(
+ '#type' => 'textfield',
+ '#size' => '64',
+ '#title' => t('Image style name'),
+ '#default_value' => $style['name'],
+ '#description' => t('The name is used in URLs for generated images. Use only lowercase alphanumeric characters, underscores (_), and hyphens (-).'),
+ '#element_validate' => array('image_style_name_validate'),
+ '#required' => TRUE,
+ );
// Build the list of existing image effects for this image style.
$form['effects'] = array(
@@ -95,25 +76,19 @@ function image_style_form($form, &$form_state, $style) {
'#title' => t('Weight for @title', array('@title' => $effect['label'])),
'#title_display' => 'invisible',
'#default_value' => $effect['weight'],
- '#access' => $editable,
);
- // Only attempt to display these fields for editable styles as the 'ieid'
- // key is not set for styles defined in code.
- if ($editable) {
- $form['effects'][$key]['configure'] = array(
- '#type' => 'link',
- '#title' => t('edit'),
- '#href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $effect['ieid'],
- '#access' => $editable && isset($effect['form callback']),
- );
- $form['effects'][$key]['remove'] = array(
- '#type' => 'link',
- '#title' => t('delete'),
- '#href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $effect['ieid'] . '/delete',
- '#access' => $editable,
- );
- }
+ $form['effects'][$key]['configure'] = array(
+ '#type' => 'link',
+ '#title' => t('edit'),
+ '#href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $key,
+ '#access' => isset($effect['form callback']),
+ );
+ $form['effects'][$key]['remove'] = array(
+ '#type' => 'link',
+ '#title' => t('delete'),
+ '#href' => 'admin/config/media/image-styles/edit/' . $style['name'] . '/effects/' . $key . '/delete',
+ );
}
// Build the new image effect addition form and add it to the effect list.
@@ -124,7 +99,6 @@ function image_style_form($form, &$form_state, $style) {
$form['effects']['new'] = array(
'#tree' => FALSE,
'#weight' => isset($form_state['input']['weight']) ? $form_state['input']['weight'] : NULL,
- '#access' => $editable,
);
$form['effects']['new']['new'] = array(
'#type' => 'select',
@@ -148,17 +122,9 @@ function image_style_form($form, &$form_state, $style) {
// Show the Override or Submit button for this style.
$form['actions'] = array('#type' => 'actions');
- $form['actions']['override'] = array(
- '#type' => 'submit',
- '#value' => t('Override defaults'),
- '#validate' => array(),
- '#submit' => array('image_style_form_override_submit'),
- '#access' => !$editable,
- );
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Update style'),
- '#access' => $editable,
);
return $form;
@@ -188,43 +154,54 @@ function image_style_form_add_submit($form, &$form_state) {
}
// If there's no form, immediately add the image effect.
else {
- $effect['isid'] = $style['isid'];
- $effect['weight'] = $form_state['values']['weight'];
- image_effect_save($effect);
+ $effect = array(
+ 'name' => $effect['name'],
+ 'data' => array(),
+ 'weight' => $form_state['values']['weight'],
+ );
+ image_effect_save($style['name'], $effect);
drupal_set_message(t('The image effect was successfully applied.'));
}
}
/**
- * Submit handler for overriding a module-defined style.
- */
-function image_style_form_override_submit($form, &$form_state) {
- drupal_set_message(t('The %style style has been overridden, allowing you to change its settings.', array('%style' => $form_state['image_style']['name'])));
- image_default_style_save($form_state['image_style']);
-}
-
-/**
* Submit handler for saving an image style.
*/
function image_style_form_submit($form, &$form_state) {
- // Update the image style name if it has changed.
$style = $form_state['image_style'];
- if (isset($form_state['values']['name']) && $style['name'] != $form_state['values']['name']) {
- $style['name'] = $form_state['values']['name'];
- }
// Update image effect weights.
if (!empty($form_state['values']['effects'])) {
foreach ($form_state['values']['effects'] as $ieid => $effect_data) {
if (isset($style['effects'][$ieid])) {
- $effect = $style['effects'][$ieid];
- $effect['weight'] = $effect_data['weight'];
- image_effect_save($effect);
+ $effect = array(
+ 'name' => $style['effects'][$ieid]['name'],
+ 'data' => $style['effects'][$ieid]['data'],
+ 'weight' => $effect_data['weight'],
+ 'ieid' => $ieid,
+ );
+ $style['effects'][$ieid] = $effect;
}
}
}
+ // Update the image style name if it has changed. We also need to delete the
+ // old style, because there is no concept of rename at the moment, just
+ // create and delete.
+ // @todo The config API does not yet support the concept of a rename, but
+ // hooks need to be able to change old style name references to the new
+ // name, so first save the new style, then delete the old, so that the
+ // delete function can receive the name of a fully saved style to update
+ // references to.
+ if (isset($form_state['values']['name']) && $style['name'] != $form_state['values']['name']) {
+ $old_style = $style;
+ $style['name'] = $form_state['values']['name'];
+ }
image_style_save($style);
+ if (isset($old_style)) {
+ image_style_delete($old_style, $style['name']);
+ }
+
if ($form_state['values']['op'] == t('Update style')) {
drupal_set_message(t('Changes to the style have been saved.'));
}
@@ -273,7 +250,7 @@ function image_style_add_form_submit($form, &$form_state) {
function image_style_name_validate($element, $form_state) {
// Check for duplicates.
$styles = image_styles();
- if (isset($styles[$element['#value']]) && (!isset($form_state['image_style']['isid']) || $styles[$element['#value']]['isid'] != $form_state['image_style']['isid'])) {
+ if (isset($styles[$element['#value']]) && (!isset($form_state['image_style']['name']) || $styles[$element['#value']]['name'] != $form_state['image_style']['name'])) {
form_set_error($element['#name'], t('The image style name %name is already in use.', array('%name' => $element['#value'])));
}
@@ -324,30 +301,6 @@ function image_style_delete_form_submit($form, &$form_state) {
}
/**
- * Confirmation form to revert a database style to its default.
- */
-function image_style_revert_form($form, $form_state, $style) {
- $form_state['image_style'] = $style;
-
- return confirm_form(
- $form,
- t('Revert the %style style?', array('%style' => $style['name'])),
- 'admin/config/media/image-styles',
- t('Reverting this style will delete the customized settings and restore the defaults provided by the @module module.', array('@module' => $style['module'])),
- t('Revert'), t('Cancel')
- );
-}
-
-/**
- * Submit handler to convert an overridden style to its default.
- */
-function image_style_revert_form_submit($form, &$form_state) {
- drupal_set_message(t('The %style style has been reverted to its defaults.', array('%style' => $form_state['image_style']['name'])));
- image_default_style_revert($form_state['image_style']);
- $form_state['redirect'] = 'admin/config/media/image-styles';
-}
-
-/**
* Form builder; Form for adding and editing image effects.
*
* This form is used universally for editing all image effects. Each effect adds
@@ -416,12 +369,14 @@ function image_effect_form($form, &$form_state, $style, $effect) {
* Submit handler for updating an image effect.
*/
function image_effect_form_submit($form, &$form_state) {
- $style = $form_state['image_style'];
- $effect = array_merge($form_state['image_effect'], $form_state['values']);
- $effect['isid'] = $style['isid'];
- image_effect_save($effect);
+ $effect = array(
+ 'name' => $form_state['image_effect']['name'],
+ 'data' => $form_state['values']['data'],
+ 'weight' => $form_state['values']['weight'],
+ );
+ image_effect_save($form_state['image_style']['name'], $effect);
drupal_set_message(t('The image effect was successfully applied.'));
- $form_state['redirect'] = 'admin/config/media/image-styles/edit/' . $style['name'];
+ $form_state['redirect'] = 'admin/config/media/image-styles/edit/' . $form_state['image_style']['name'];
}
/**
@@ -449,7 +404,7 @@ function image_effect_delete_form_submit($form, &$form_state) {
$style = $form_state['image_style'];
$effect = $form_state['image_effect'];
- image_effect_delete($effect);
+ image_effect_delete($style['name'], $effect);
drupal_set_message(t('The image effect %name has been deleted.', array('%name' => $effect['label'])));
$form_state['redirect'] = 'admin/config/media/image-styles/edit/' . $style['name'];
}
@@ -645,31 +600,19 @@ function image_rotate_form($data) {
function theme_image_style_list($variables) {
$styles = $variables['styles'];
- $header = array(t('Style name'), t('Settings'), array('data' => t('Operations'), 'colspan' => 3));
+ $header = array(t('Style name'), array('data' => t('Operations'), 'colspan' => 3));
$rows = array();
+ $link_attributes = array(
+ 'attributes' => array(
+ 'class' => array('image-style-link'),
+ ),
+ );
+
foreach ($styles as $style) {
$row = array();
$row[] = l($style['name'], 'admin/config/media/image-styles/edit/' . $style['name']);
- $link_attributes = array(
- 'attributes' => array(
- 'class' => array('image-style-link'),
- ),
- );
- if ($style['storage'] == IMAGE_STORAGE_NORMAL) {
- $row[] = t('Custom');
- $row[] = l(t('edit'), 'admin/config/media/image-styles/edit/' . $style['name'], $link_attributes);
- $row[] = l(t('delete'), 'admin/config/media/image-styles/delete/' . $style['name'], $link_attributes);
- }
- elseif ($style['storage'] == IMAGE_STORAGE_OVERRIDE) {
- $row[] = t('Overridden');
- $row[] = l(t('edit'), 'admin/config/media/image-styles/edit/' . $style['name'], $link_attributes);
- $row[] = l(t('revert'), 'admin/config/media/image-styles/revert/' . $style['name'], $link_attributes);
- }
- else {
- $row[] = t('Default');
- $row[] = l(t('edit'), 'admin/config/media/image-styles/edit/' . $style['name'], $link_attributes);
- $row[] = '';
- }
+ $row[] = l(t('edit'), 'admin/config/media/image-styles/edit/' . $style['name'], $link_attributes);
+ $row[] = l(t('delete'), 'admin/config/media/image-styles/delete/' . $style['name'], $link_attributes);
$rows[] = $row;
}
@@ -694,13 +637,12 @@ function theme_image_style_list($variables) {
*/
function theme_image_style_effects($variables) {
$form = $variables['form'];
-
$rows = array();
foreach (element_children($form) as $key) {
$row = array();
$form[$key]['weight']['#attributes']['class'] = array('image-effect-order-weight');
- if (is_numeric($key)) {
+ if ($key != 'new') {
$summary = drupal_render($form[$key]['summary']);
$row[] = drupal_render($form[$key]['label']) . (empty($summary) ? '' : ' ' . $summary);
$row[] = drupal_render($form[$key]['weight']);
@@ -728,7 +670,7 @@ function theme_image_style_effects($variables) {
array('data' => t('Operations'), 'colspan' => 2),
);
- if (count($rows) == 1 && $form['new']['#access']) {
+ if (count($rows) == 1 && (!isset($form['new']['#access']) || $form['new']['#access'])) {
array_unshift($rows, array(array(
'data' => t('There are currently no effects in this style. Add one by selecting an option below.'),
'colspan' => 4,
diff --git a/core/modules/image/image.install b/core/modules/image/image.install
index 02be57c..91349a9 100644
--- a/core/modules/image/image.install
+++ b/core/modules/image/image.install
@@ -23,87 +23,6 @@ function image_uninstall() {
}
/**
- * Implements hook_schema().
- */
-function image_schema() {
- $schema = array();
-
- $schema['image_styles'] = array(
- 'description' => 'Stores configuration options for image styles.',
- 'fields' => array(
- 'isid' => array(
- 'description' => 'The primary identifier for an image style.',
- 'type' => 'serial',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- ),
- 'name' => array(
- 'description' => 'The style name.',
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- ),
- ),
- 'primary key' => array('isid'),
- 'unique keys' => array(
- 'name' => array('name'),
- ),
- );
-
- $schema['image_effects'] = array(
- 'description' => 'Stores configuration options for image effects.',
- 'fields' => array(
- 'ieid' => array(
- 'description' => 'The primary identifier for an image effect.',
- 'type' => 'serial',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- ),
- 'isid' => array(
- 'description' => 'The {image_styles}.isid for an image style.',
- 'type' => 'int',
- 'unsigned' => TRUE,
- 'not null' => TRUE,
- 'default' => 0,
- ),
- 'weight' => array(
- 'description' => 'The weight of the effect in the style.',
- 'type' => 'int',
- 'unsigned' => FALSE,
- 'not null' => TRUE,
- 'default' => 0,
- ),
- 'name' => array(
- 'description' => 'The unique name of the effect to be executed.',
- 'type' => 'varchar',
- 'length' => 255,
- 'not null' => TRUE,
- ),
- 'data' => array(
- 'description' => 'The configuration data for the effect.',
- 'type' => 'blob',
- 'not null' => TRUE,
- 'size' => 'big',
- 'serialize' => TRUE,
- ),
- ),
- 'primary key' => array('ieid'),
- 'indexes' => array(
- 'isid' => array('isid'),
- 'weight' => array('weight'),
- ),
- 'foreign keys' => array(
- 'image_style' => array(
- 'table' => 'image_styles',
- 'columns' => array('isid' => 'isid'),
- ),
- ),
- );
-
- return $schema;
-}
-
-/**
* Implements hook_field_schema().
*/
function image_field_schema($field) {
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 0e48d15..e6a9bc8 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -135,15 +135,6 @@ function image_menu() {
'access arguments' => array('administer image styles'),
'file' => 'image.admin.inc',
);
- $items['admin/config/media/image-styles/revert/%image_style'] = array(
- 'title' => 'Revert style',
- 'description' => 'Revert an image style.',
- 'load arguments' => array(NULL, (string) IMAGE_STORAGE_OVERRIDE),
- 'page callback' => 'drupal_get_form',
- 'page arguments' => array('image_style_revert_form', 5),
- 'access arguments' => array('administer image styles'),
- 'file' => 'image.admin.inc',
- );
$items['admin/config/media/image-styles/edit/%image_style/effects/%image_effect'] = array(
'title' => 'Edit image effect',
'description' => 'Edit an existing effect within a style.',
@@ -331,45 +322,6 @@ function image_file_predelete($file) {
}
/**
- * Implements hook_image_default_styles().
- */
-function image_image_default_styles() {
- $styles = array();
-
- $styles['thumbnail'] = array(
- 'effects' => array(
- array(
- 'name' => 'image_scale',
- 'data' => array('width' => 100, 'height' => 100, 'upscale' => 1),
- 'weight' => 0,
- ),
- )
- );
-
- $styles['medium'] = array(
- 'effects' => array(
- array(
- 'name' => 'image_scale',
- 'data' => array('width' => 220, 'height' => 220, 'upscale' => 1),
- 'weight' => 0,
- ),
- )
- );
-
- $styles['large'] = array(
- 'effects' => array(
- array(
- 'name' => 'image_scale',
- 'data' => array('width' => 480, 'height' => 480, 'upscale' => 0),
- 'weight' => 0,
- ),
- )
- );
-
- return $styles;
-}
-
-/**
* Implements hook_image_style_save().
*/
function image_image_style_save($style) {
@@ -483,55 +435,30 @@ function image_path_flush($path) {
* @see image_style_load()
*/
function image_styles() {
- $styles = &drupal_static(__FUNCTION__);
-
- // Grab from cache or build the array.
- if (!isset($styles)) {
- if ($cache = cache()->get('image_styles')) {
- $styles = $cache->data;
- }
- else {
+ // @todo Configuration must not be statically cached nor cache-system cached.
+ // However, there's a drupal_alter() involved here.
+
+// $styles = &drupal_static(__FUNCTION__);
+//
+// // Grab from cache or build the array.
+// if (!isset($styles)) {
+// if ($cache = cache()->get('image_styles')) {
+// $styles = $cache->data;
+// }
+// else {
$styles = array();
- // Select the module-defined styles.
- foreach (module_implements('image_default_styles') as $module) {
- $module_styles = module_invoke($module, 'image_default_styles');
- foreach ($module_styles as $style_name => $style) {
- $style['name'] = $style_name;
- $style['module'] = $module;
- $style['storage'] = IMAGE_STORAGE_DEFAULT;
- foreach ($style['effects'] as $key => $effect) {
- $definition = image_effect_definition_load($effect['name']);
- $effect = array_merge($definition, $effect);
- $style['effects'][$key] = $effect;
- }
- $styles[$style_name] = $style;
- }
- }
-
- // Select all the user-defined styles.
- $user_styles = db_select('image_styles', NULL, array('fetch' => PDO::FETCH_ASSOC))
- ->fields('image_styles')
- ->orderBy('name')
- ->execute()
- ->fetchAllAssoc('name', PDO::FETCH_ASSOC);
-
- // Allow the user styles to override the module styles.
- foreach ($user_styles as $style_name => $style) {
- $style['module'] = NULL;
- $style['storage'] = IMAGE_STORAGE_NORMAL;
- $style['effects'] = image_style_effects($style);
- if (isset($styles[$style_name]['module'])) {
- $style['module'] = $styles[$style_name]['module'];
- $style['storage'] = IMAGE_STORAGE_OVERRIDE;
- }
- $styles[$style_name] = $style;
+ // Select the styles we have configured.
+ $configured_styles = config_get_verified_storage_names_with_prefix('image.styles');
+ foreach ($configured_styles as $config_name) {
+ // @todo Allow to retrieve the name without prefix only.
+ $style = image_style_load(str_replace('image.styles.', '', $config_name));
+ $styles[$style['name']] = $style;
}
-
drupal_alter('image_styles', $styles);
- cache()->set('image_styles', $styles);
- }
- }
+// cache()->set('image_styles', $styles);
+// }
+// }
return $styles;
}
@@ -541,45 +468,31 @@ function image_styles() {
*
* @param $name
* The name of the style.
- * @param $isid
- * Optional. The numeric id of a style if the name is not known.
- * @param $include
- * If set, this loader will restrict to a specific type of image style, may be
- * one of the defined Image style storage constants.
* @return
* An image style array containing the following keys:
- * - "isid": The unique image style ID.
* - "name": The unique image style name.
* - "effects": An array of image effects within this image style.
- * If the image style name or ID is not valid, an empty array is returned.
+ * If the image style name is not valid, an empty array is returned.
* @see image_effect_load()
*/
-function image_style_load($name = NULL, $isid = NULL, $include = NULL) {
- $styles = image_styles();
-
- // If retrieving by name.
- if (isset($name) && isset($styles[$name])) {
- $style = $styles[$name];
- }
+function image_style_load($name) {
+ $style = config('image.styles.' . $name)->get();
- // If retrieving by image style id.
- if (!isset($name) && isset($isid)) {
- foreach ($styles as $name => $database_style) {
- if (isset($database_style['isid']) && $database_style['isid'] == $isid) {
- $style = $database_style;
- break;
- }
- }
+ // @todo Requires a more reliable + generic method to check for whether the
+ // configuration object exists.
+ if (!isset($style['name'])) {
+ return FALSE;
}
- // Restrict to the specific type of flag. This bitwise operation basically
- // states "if the storage is X, then allow".
- if (isset($style) && (!isset($include) || ($style['storage'] & (int) $include))) {
- return $style;
+ foreach ($style['effects'] as $ieid => $effect) {
+ $definition = image_effect_definition_load($effect['name']);
+ $effect = array_merge($definition, $effect);
+ $style['effects'][$ieid] = $effect;
}
+ // Sort effects by weight.
+ uasort($style['effects'], 'drupal_sort_weight');
- // Otherwise the style was not found.
- return FALSE;
+ return $style;
}
/**
@@ -588,22 +501,21 @@ function image_style_load($name = NULL, $isid = NULL, $include = NULL) {
* @param style
* An image style array.
* @return
- * An image style array. In the case of a new style, 'isid' will be populated.
+ * An image style array.
*/
function image_style_save($style) {
- if (isset($style['isid']) && is_numeric($style['isid'])) {
- // Load the existing style to make sure we account for renamed styles.
- $old_style = image_style_load(NULL, $style['isid']);
- image_style_flush($old_style);
- drupal_write_record('image_styles', $style, 'isid');
- if ($old_style['name'] != $style['name']) {
- $style['old_name'] = $old_style['name'];
- }
+ $config = config('image.styles.' . $style['name']);
+ $config->set('name', $style['name']);
+ if (isset($style['effects'])) {
+ $config->set('effects', $style['effects']);
}
else {
- drupal_write_record('image_styles', $style);
- $style['is_new'] = TRUE;
+ $config->set('effects', array());
}
+ $config->save();
+ // @todo is_new must only be set when the configuration object did not exist
+ // yet.
+ $style['is_new'] = TRUE;
// Let other modules update as necessary on save.
module_invoke_all('image_style_save', $style);
@@ -628,8 +540,8 @@ function image_style_save($style) {
function image_style_delete($style, $replacement_style_name = '') {
image_style_flush($style);
- db_delete('image_effects')->condition('isid', $style['isid'])->execute();
- db_delete('image_styles')->condition('isid', $style['isid'])->execute();
+ $config = config('image.styles.' . $style['name']);
+ $config->delete();
// Let other modules update as necessary on save.
$style['old_name'] = $style['name'];
@@ -648,17 +560,11 @@ function image_style_delete($style, $replacement_style_name = '') {
* An array of image effects associated with specified image style in the
* format array('isid' => array()), or an empty array if the specified style
* has no effects.
+ *
+ * @todo Remove this function; it's entirely obsolete.
*/
function image_style_effects($style) {
- $effects = image_effects();
- $style_effects = array();
- foreach ($effects as $effect) {
- if ($style['isid'] == $effect['isid']) {
- $style_effects[$effect['ieid']] = $effect;
- }
- }
-
- return $style_effects;
+ return $style['effects'];
}
/**
@@ -854,11 +760,11 @@ function image_style_flush($style) {
// Let other modules update as necessary on flush.
module_invoke_all('image_style_flush', $style);
- // Clear image style and effect caches.
- cache()->delete('image_styles');
- cache()->deletePrefix('image_effects:');
- drupal_static_reset('image_styles');
- drupal_static_reset('image_effects');
+// // Clear image style and effect caches.
+// cache()->delete('image_styles');
+// cache()->deletePrefix('image_effects:');
+// drupal_static_reset('image_styles');
+// drupal_static_reset('image_effects');
// Clear field caches so that formatters may be added for this style.
field_info_cache_clear();
@@ -876,15 +782,15 @@ function image_style_flush($style) {
*
* @param $style_name
* The name of the style to be used with this image.
- * @param $uri
+ * @param $path
* The path to the image.
* @return
* The absolute URL where a style image can be downloaded, suitable for use
* in an tag. Requesting the URL will cause the image to be created.
* @see image_style_deliver()
*/
-function image_style_url($style_name, $uri) {
- $uri = image_style_path($style_name, $uri);
+function image_style_url($style_name, $path) {
+ $uri = image_style_path($style_name, $path);
// If not using clean URLs, the image derivative callback is only available
// with the query string. If the file does not exist, use url() to ensure
@@ -925,44 +831,6 @@ function image_style_path($style_name, $uri) {
}
/**
- * Save a default image style to the database.
- *
- * @param style
- * An image style array provided by a module.
- * @return
- * An image style array. The returned style array will include the new 'isid'
- * assigned to the style.
- */
-function image_default_style_save($style) {
- $style = image_style_save($style);
- $effects = array();
- foreach ($style['effects'] as $effect) {
- $effect['isid'] = $style['isid'];
- $effect = image_effect_save($effect);
- $effects[$effect['ieid']] = $effect;
- }
- $style['effects'] = $effects;
- return $style;
-}
-
-/**
- * Revert the changes made by users to a default image style.
- *
- * @param style
- * An image style array.
- * @return
- * Boolean TRUE if the operation succeeded.
- */
-function image_default_style_revert($style) {
- image_style_flush($style);
-
- db_delete('image_effects')->condition('isid', $style['isid'])->execute();
- db_delete('image_styles')->condition('isid', $style['isid'])->execute();
-
- return TRUE;
-}
-
-/**
* Pull in image effects exposed by modules implementing hook_image_effect_info().
*
* @return
@@ -1015,8 +883,6 @@ function image_effect_definitions() {
*
* @param $effect
* The name of the effect definition to load.
- * @param $style
- * An image style array to which this effect will be added.
* @return
* An array containing the image effect definition with the following keys:
* - "effect": The unique name for the effect being performed. Usually prefixed
@@ -1028,18 +894,8 @@ function image_effect_definitions() {
* - "summary": (optional) The name of a theme function that will display a
* one-line summary of the effect. Does not include the "theme_" prefix.
*/
-function image_effect_definition_load($effect, $style_name = NULL) {
+function image_effect_definition_load($effect) {
$definitions = image_effect_definitions();
-
- // If a style is specified, do not allow loading of default style
- // effects.
- if (isset($style_name)) {
- $style = image_style_load($style_name, NULL);
- if ($style['storage'] == IMAGE_STORAGE_DEFAULT) {
- return FALSE;
- }
- }
-
return isset($definitions[$effect]) ? $definitions[$effect] : FALSE;
}
@@ -1049,6 +905,8 @@ function image_effect_definition_load($effect, $style_name = NULL) {
* @return
* An array of all image effects.
* @see image_effect_load()
+ *
+ * @todo Remove after moving/resolving the todo.
*/
function image_effects() {
$effects = &drupal_static(__FUNCTION__);
@@ -1057,6 +915,9 @@ function image_effects() {
$effects = array();
// Add database image effects.
+ // @todo Strictly speaking, this is obsolete. However, it demonstrates a
+ // use-case for retrieving/listing configuration objects using a wildcard
+ // within the name (instead of only the suffix).
$result = db_select('image_effects', NULL, array('fetch' => PDO::FETCH_ASSOC))
->fields('image_effects')
->orderBy('image_effects.weight', 'ASC')
@@ -1082,13 +943,10 @@ function image_effects() {
* The image effect ID.
* @param $style_name
* The image style name.
- * @param $include
- * If set, this loader will restrict to a specific type of image style, may be
- * one of the defined Image style storage constants.
+ *
* @return
* An image effect array, consisting of the following keys:
* - "ieid": The unique image effect ID.
- * - "isid": The unique image style ID that contains this image effect.
* - "weight": The weight of this image effect within the image style.
* - "name": The name of the effect definition that powers this image effect.
* - "data": An array of configuration options for this image effect.
@@ -1098,9 +956,20 @@ function image_effects() {
* @see image_style_load()
* @see image_effect_definition_load()
*/
-function image_effect_load($ieid, $style_name, $include = NULL) {
- if (($style = image_style_load($style_name, NULL, $include)) && isset($style['effects'][$ieid])) {
- return $style['effects'][$ieid];
+function image_effect_load($ieid, $style_name) {
+ if (($style = image_style_load($style_name)) && isset($style['effects'][$ieid])) {
+ $effect = $style['effects'][$ieid];
+ $definition = image_effect_definition_load($effect['name']);
+ $effect = array_merge($definition, $effect);
+ // @todo The effect's key name within the style is unknown. It *should* be
+ // identical to the ieid, but that is in no way guaranteed. And of course,
+ // the ieid key *within* the effect is senseless duplication in the first
+ // place. This problem can be eliminated in many places, but especially
+ // for loaded menu arguments like %image_effect, the actual router
+ // callbacks don't have access to 'ieid' anymore (unless resorting to
+ // dirty %index and %map tricks).
+ $effect['ieid'] = $ieid;
+ return $effect;
}
return FALSE;
}
@@ -1108,19 +977,32 @@ function image_effect_load($ieid, $style_name, $include = NULL) {
/**
* Save an image effect.
*
+ * @param $style_name
+ * The image style this effect belongs to.
* @param $effect
* An image effect array.
* @return
* An image effect array. In the case of a new effect, 'ieid' will be set.
*/
-function image_effect_save($effect) {
- if (!empty($effect['ieid'])) {
- drupal_write_record('image_effects', $effect, 'ieid');
- }
- else {
- drupal_write_record('image_effects', $effect);
+function image_effect_save($style_name, $effect) {
+ $config = config('image.styles.' . $style_name);
+
+ if (!isset($effect['ieid']) || empty($effect['ieid'])) {
+ // We need to generate the ieid and save the new effect.
+ // The machine name is all the elements of the data array concatenated
+ // together, delimited by underscores.
+ $effect['ieid'] = $effect['name'];
+ foreach ($effect['data'] as $key => $value) {
+ $effect['ieid'] .= '_' . $value;
+ }
+ // @todo The machine name must not use any special non-alphanumeric
+ // characters, and may also not contain dots/periods, as that is the
+ // config system's nested key syntax.
+ $effect['ieid'] = preg_replace('@[^a-zA-Z0-9_-]@', '', $effect['ieid']);
}
- $style = image_style_load(NULL, $effect['isid']);
+ $config->set('effects.' . $effect['ieid'], $effect);
+ $config->save();
+ $style = image_style_load($style_name);
image_style_flush($style);
return $effect;
}
@@ -1128,12 +1010,16 @@ function image_effect_save($effect) {
/**
* Delete an image effect.
*
+ * @param $style_name
+ * The image style this effect belongs to.
* @param $effect
* An image effect array.
*/
-function image_effect_delete($effect) {
- db_delete('image_effects')->condition('ieid', $effect['ieid'])->execute();
- $style = image_style_load(NULL, $effect['isid']);
+function image_effect_delete($style_name, $effect) {
+ $config = config('image.styles.' . $style_name);
+ $config->clear('effects.' . $effect['ieid']);
+ $config->save();
+ $style = image_style_load($style_name);
image_style_flush($style);
}
diff --git a/core/modules/image/image.test b/core/modules/image/image.test
index 4d4532c..7a4683b 100644
--- a/core/modules/image/image.test
+++ b/core/modules/image/image.test
@@ -468,7 +468,7 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
// Load the style by the new name with the new weights.
drupal_static_reset('image_styles');
- $style = image_style_load($style_name, NULL);
+ $style = image_style_load($style_name);
// Confirm the new style order was saved.
$effect_edits_order = array_reverse($effect_edits_order);
@@ -507,76 +507,6 @@ class ImageAdminStylesUnitTest extends ImageFieldTestCase {
}
/**
- * Test to override, edit, then revert a style.
- */
- function testDefaultStyle() {
- // Setup a style to be created and effects to add to it.
- $style_name = 'thumbnail';
- $edit_path = 'admin/config/media/image-styles/edit/' . $style_name;
- $delete_path = 'admin/config/media/image-styles/delete/' . $style_name;
- $revert_path = 'admin/config/media/image-styles/revert/' . $style_name;
-
- // Ensure deleting a default is not possible.
- $this->drupalGet($delete_path);
- $this->assertText(t('Page not found'), t('Default styles may not be deleted.'));
-
- // Ensure that editing a default is not possible (without overriding).
- $this->drupalGet($edit_path);
- $this->assertNoField('edit-name', t('Default styles may not be renamed.'));
- $this->assertNoField('edit-submit', t('Default styles may not be edited.'));
- $this->assertNoField('edit-add', t('Default styles may not have new effects added.'));
-
- // Create an image to make sure the default works before overriding.
- drupal_static_reset('image_styles');
- $style = image_style_load($style_name);
- $image_path = $this->createSampleImage($style);
- $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
-
- // Verify that effects attached to a default style do not have an ieid key.
- foreach ($style['effects'] as $effect) {
- $this->assertFalse(isset($effect['ieid']), t('The %effect effect does not have an ieid.', array('%effect' => $effect['name'])));
- }
-
- // Override the default.
- $this->drupalPost($edit_path, array(), t('Override defaults'));
- $this->assertRaw(t('The %style style has been overridden, allowing you to change its settings.', array('%style' => $style_name)), t('Default image style may be overridden.'));
-
- // Add sample effect to the overridden style.
- $this->drupalPost($edit_path, array('new' => 'image_desaturate'), t('Add'));
- drupal_static_reset('image_styles');
- $style = image_style_load($style_name);
-
- // Verify that effects attached to the style have an ieid now.
- foreach ($style['effects'] as $effect) {
- $this->assertTrue(isset($effect['ieid']), t('The %effect effect has an ieid.', array('%effect' => $effect['name'])));
- }
-
- // The style should now have 2 effect, the original scale provided by core
- // and the desaturate effect we added in the override.
- $effects = array_values($style['effects']);
- $this->assertEqual($effects[0]['name'], 'image_scale', t('The default effect still exists in the overridden style.'));
- $this->assertEqual($effects[1]['name'], 'image_desaturate', t('The added effect exists in the overridden style.'));
-
- // Check that we are unable to rename an overridden style.
- $this->drupalGet($edit_path);
- $this->assertNoField('edit-name', t('Overridden styles may not be renamed.'));
-
- // Create an image to ensure the override works properly.
- $image_path = $this->createSampleImage($style);
- $this->assertEqual($this->getImageCount($style), 1, t('Image style %style image %file successfully generated.', array('%style' => $style['name'], '%file' => $image_path)));
-
- // Revert the image style.
- $this->drupalPost($revert_path, array(), t('Revert'));
- drupal_static_reset('image_styles');
- $style = image_style_load($style_name);
-
- // The style should now have the single effect for scale.
- $effects = array_values($style['effects']);
- $this->assertEqual($effects[0]['name'], 'image_scale', t('The default effect still exists in the reverted style.'));
- $this->assertFalse(array_key_exists(1, $effects), t('The added effect has been removed in the reverted style.'));
- }
-
- /**
* Test deleting a style and choosing a replacement style.
*/
function testStyleReplacement() {
@@ -935,6 +865,7 @@ class ImageFieldValidateTestCase extends ImageFieldTestCase {
* Tests that images have correct dimensions when styled.
*/
class ImageDimensionsUnitTest extends DrupalWebTestCase {
+ protected $profile = 'testing';
public static function getInfo() {
return array(
@@ -945,7 +876,7 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
}
function setUp() {
- parent::setUp('image_module_test');
+ parent::setUp('rdf', 'image', 'image_module_test');
}
/**
@@ -968,6 +899,10 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'width' => 40,
'height' => 20,
);
+ // Verify that the original image matches the hard-coded values.
+ $image_info = image_get_info($original_uri);
+ $this->assertEqual($image_info['width'], $variables['width']);
+ $this->assertEqual($image_info['height'], $variables['height']);
// Scale an image that is wider than it is high.
$effect = array(
@@ -977,19 +912,19 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'height' => 90,
'upscale' => TRUE,
),
- 'isid' => $style['isid'],
+ 'weight' => 0,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
$this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 120, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 60, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 120);
+ $this->assertEqual($image_info['height'], 60);
// Rotate 90 degrees anticlockwise.
$effect = array(
@@ -998,19 +933,19 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'degrees' => -90,
'random' => FALSE,
),
- 'isid' => $style['isid'],
+ 'weight' => 1,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
$this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 60, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 120, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 60);
+ $this->assertEqual($image_info['height'], 120);
// Scale an image that is higher than it is wide (rotated by previous effect).
$effect = array(
@@ -1020,19 +955,19 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'height' => 90,
'upscale' => TRUE,
),
- 'isid' => $style['isid'],
+ 'weight' => 2,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
$this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 45, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 90, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 45);
+ $this->assertEqual($image_info['height'], 90);
// Test upscale disabled.
$effect = array(
@@ -1042,37 +977,37 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'height' => 200,
'upscale' => FALSE,
),
- 'isid' => $style['isid'],
+ 'weight' => 3,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
$this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 45, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 90, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 45);
+ $this->assertEqual($image_info['height'], 90);
// Add a desaturate effect.
$effect = array(
'name' => 'image_desaturate',
'data' => array(),
- 'isid' => $style['isid'],
+ 'weight' => 4,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
$this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 45, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 90, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 45);
+ $this->assertEqual($image_info['height'], 90);
// Add a random rotate effect.
$effect = array(
@@ -1081,12 +1016,12 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'degrees' => 180,
'random' => TRUE,
),
- 'isid' => $style['isid'],
+ 'weight' => 5,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
@@ -1101,19 +1036,19 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'height' => 30,
'anchor' => 'center-center',
),
- 'isid' => $style['isid'],
+ 'weight' => 6,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
$this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
$image_info = image_get_info($generated_uri);
- $this->assertEqual($image_info['width'], 30, t('Expected width was found.'));
- $this->assertEqual($image_info['height'], 30, t('Expected height was found.'));
+ $this->assertEqual($image_info['width'], 30);
+ $this->assertEqual($image_info['height'], 30);
// Rotate to a non-multiple of 90 degrees.
$effect = array(
@@ -1122,30 +1057,30 @@ class ImageDimensionsUnitTest extends DrupalWebTestCase {
'degrees' => 57,
'random' => FALSE,
),
- 'isid' => $style['isid'],
+ 'weight' => 7,
);
- $effect = image_effect_save($effect);
+ $effect = image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
$this->assertFalse(file_exists($generated_uri), t('Generated file does not exist.'));
$this->drupalGet($url);
$this->assertResponse(200, t('Image was generated at the URL.'));
$this->assertTrue(file_exists($generated_uri), t('Generated file does exist after we accessed it.'));
- image_effect_delete($effect);
+ image_effect_delete('test', $effect);
// Ensure that an effect with no dimensions callback unsets the dimensions.
// This ensures compatibility with 7.0 contrib modules.
$effect = array(
'name' => 'image_module_test_null',
'data' => array(),
- 'isid' => $style['isid'],
+ 'weight' => 8,
);
- image_effect_save($effect);
+ image_effect_save('test', $effect);
$img_tag = theme_image_style($variables);
- $this->assertEqual($img_tag, '', t('Expected img tag was found.'));
+ $this->assertEqual($img_tag, '');
}
}
diff --git a/core/modules/poll/poll.test b/core/modules/poll/poll.test
index ad63d0f..9275af8 100644
--- a/core/modules/poll/poll.test
+++ b/core/modules/poll/poll.test
@@ -496,7 +496,9 @@ class PollVoteCheckHostname extends PollTestCase {
// Enable page cache to verify that the result page is not saved in the
// cache when anonymous voting is allowed.
- variable_set('cache', 1);
+ $config = config('system.performance');
+ $config->set('cache', 1);
+ $config->save();
// Create poll.
$title = $this->randomName();
diff --git a/core/modules/simpletest/drupal_web_test_case.php b/core/modules/simpletest/drupal_web_test_case.php
index 2852fca..61e54df 100644
--- a/core/modules/simpletest/drupal_web_test_case.php
+++ b/core/modules/simpletest/drupal_web_test_case.php
@@ -1310,6 +1310,8 @@ class DrupalWebTestCase extends DrupalTestCase {
// Store necessary current values before switching to prefixed database.
$this->originalLanguage = $language_interface;
$this->originalLanguageDefault = variable_get('language_default');
+ $this->originalConfigDirectory = $GLOBALS['config_directory_name'];
+ $this->originalConfigSignatureKey = $GLOBALS['config_signature_key'];
$this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files');
$this->originalProfile = drupal_get_profile();
$clean_url_original = variable_get('clean_url', 0);
@@ -1346,6 +1348,14 @@ class DrupalWebTestCase extends DrupalTestCase {
file_prepare_directory($temp_files_directory, FILE_CREATE_DIRECTORY);
$this->generatedTestFiles = FALSE;
+ // Create and set a new configuration directory and signature key.
+ // The child site automatically adjusts the global $config_directory_name to
+ // a test-prefix-specific directory within the public files directory.
+ $GLOBALS['config_directory_name'] = 'simpletest/config_' . $this->databasePrefix;
+ $this->configFileDirectory = $this->originalFileDirectory . '/' . $GLOBALS['config_directory_name'];
+ file_prepare_directory($this->configFileDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
+ $GLOBALS['config_signature_key'] = drupal_hash_base64(drupal_random_bytes(55));
+
// Log fatal errors.
ini_set('log_errors', 1);
ini_set('error_log', $public_files_directory . '/error.log');
@@ -1568,6 +1578,10 @@ class DrupalWebTestCase extends DrupalTestCase {
// Rebuild caches.
$this->refreshVariables();
+ // Reset configuration globals.
+ $GLOBALS['config_directory_name'] = $this->originalConfigDirectory;
+ $GLOBALS['config_signature_key'] = $this->originalConfigSignatureKey;
+
// Reset language.
$language_interface = $this->originalLanguage;
if ($this->originalLanguageDefault) {
diff --git a/core/modules/simpletest/tests/bootstrap.test b/core/modules/simpletest/tests/bootstrap.test
index 13fdf07..fee8fcc 100644
--- a/core/modules/simpletest/tests/bootstrap.test
+++ b/core/modules/simpletest/tests/bootstrap.test
@@ -116,7 +116,9 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase {
* Test support for requests containing If-Modified-Since and If-None-Match headers.
*/
function testConditionalRequests() {
- variable_set('cache', 1);
+ $config = config('system.performance');
+ $config->set('cache', 1);
+ $config->save();
// Fill the cache.
$this->drupalGet('');
@@ -154,7 +156,9 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase {
* Test cache headers.
*/
function testPageCache() {
- variable_set('cache', 1);
+ $config = config('system.performance');
+ $config->set('cache', 1);
+ $config->save();
// Fill the cache.
$this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
@@ -198,7 +202,9 @@ class BootstrapPageCacheTestCase extends DrupalWebTestCase {
* mod_deflate Apache module.
*/
function testPageCompression() {
- variable_set('cache', 1);
+ $config = config('system.performance');
+ $config->set('cache', 1);
+ $config->save();
// Fill the cache and verify that output is compressed.
$this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
@@ -291,14 +297,18 @@ class HookBootExitTestCase extends DrupalWebTestCase {
*/
function testHookBootExit() {
// Test with cache disabled. Boot and exit should always fire.
- variable_set('cache', 0);
+ $config = config('system.performance');
+ $config->set('cache', 0);
+ $config->save();
+
$this->drupalGet('');
$calls = 1;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with disabled cache.'));
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_exit'))->fetchField(), $calls, t('hook_exit called with disabled cache.'));
// Test with normal cache. Boot and exit should be called.
- variable_set('cache', 1);
+ $config->set('cache', 1);
+ $config->save();
$this->drupalGet('');
$calls++;
$this->assertEqual(db_query('SELECT COUNT(*) FROM {watchdog} WHERE type = :type AND message = :message', array(':type' => 'system_test', ':message' => 'hook_boot'))->fetchField(), $calls, t('hook_boot called with normal cache.'));
diff --git a/core/modules/simpletest/tests/cache.test b/core/modules/simpletest/tests/cache.test
index bca4e25..81f2377 100644
--- a/core/modules/simpletest/tests/cache.test
+++ b/core/modules/simpletest/tests/cache.test
@@ -95,7 +95,9 @@ class CacheTestCase extends DrupalWebTestCase {
* The time in seconds the cache should minimal live.
*/
protected function setupLifetime($time) {
- variable_set('cache_lifetime', $time);
+ $config = config('system.performance');
+ $config->set('cache_lifetime', $time);
+ $config->save();
variable_set('cache_flush', 0);
}
}
diff --git a/core/modules/simpletest/tests/common.test b/core/modules/simpletest/tests/common.test
index fee3831..1c9a7e7 100644
--- a/core/modules/simpletest/tests/common.test
+++ b/core/modules/simpletest/tests/common.test
@@ -1191,8 +1191,10 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
parent::setUp('locale', 'simpletest', 'common_test');
// Disable preprocessing
- $this->preprocess_js = variable_get('preprocess_js', 0);
- variable_set('preprocess_js', 0);
+ $config = config('system.performance');
+ $this->preprocess_js = $config->get('preprocess_js');
+ $config->set('preprocess_js', 0);
+ $config->save();
// Reset drupal_add_js() and drupal_add_library() statics before each test.
drupal_static_reset('drupal_add_js');
@@ -1201,7 +1203,9 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
function tearDown() {
// Restore configured value for JavaScript preprocessing.
- variable_set('preprocess_js', $this->preprocess_js);
+ $config = config('system.performance');
+ $config->set('preprocess_js', $this->preprocess_js);
+ $config->save();
parent::tearDown();
}
@@ -1399,7 +1403,9 @@ class CommonJavaScriptTestCase extends DrupalWebTestCase {
// Now ensure that with aggregation on, one file is made for the
// 'every_page' files, and one file is made for the others.
drupal_static_reset('drupal_add_js');
- variable_set('preprocess_js', 1);
+ $config = config('system.performance');
+ $config->set('preprocess_js', 1);
+ $config->save();
drupal_add_js('core/misc/ajax.js');
drupal_add_js('core/misc/authorize.js', array('every_page' => TRUE));
drupal_add_js('core/misc/autocomplete.js');
diff --git a/core/modules/simpletest/tests/session.test b/core/modules/simpletest/tests/session.test
index 746f632..6303ca5 100644
--- a/core/modules/simpletest/tests/session.test
+++ b/core/modules/simpletest/tests/session.test
@@ -139,7 +139,9 @@ class SessionTestCase extends DrupalWebTestCase {
$this->assertSessionEmpty(TRUE);
// The same behavior is expected when caching is enabled.
- variable_set('cache', 1);
+ $config = config('system.performance');
+ $config->set('cache', 1);
+ $config->save();
$this->drupalGet('');
$this->assertSessionCookie(FALSE);
$this->assertSessionEmpty(TRUE);
diff --git a/core/modules/simpletest/tests/theme.test b/core/modules/simpletest/tests/theme.test
index a3b5c3d..a795054 100644
--- a/core/modules/simpletest/tests/theme.test
+++ b/core/modules/simpletest/tests/theme.test
@@ -100,7 +100,9 @@ class ThemeUnitTest extends DrupalWebTestCase {
// what is output to the HTML HEAD based on what is in a theme's .info file,
// so it doesn't matter what page we get, as long as it is themed with the
// test theme. First we test with CSS aggregation disabled.
- variable_set('preprocess_css', 0);
+ $config = config('system.performance');
+ $config->set('preprocess_css', 0);
+ $config->save();
$this->drupalGet('theme-test/suggestion');
$this->assertNoText('system.base.css', t('The theme\'s .info file is able to override a module CSS file from being added to the page.'));
@@ -108,9 +110,11 @@ class ThemeUnitTest extends DrupalWebTestCase {
// triggered during drupal_build_css_cache() when a source file doesn't
// exist. Then allow remaining tests to continue with aggregation disabled
// by default.
- variable_set('preprocess_css', 1);
+ $config->set('preprocess_css', 1);
+ $config->save();
$this->drupalGet('theme-test/suggestion');
- variable_set('preprocess_css', 0);
+ $config->set('preprocess_css', 0);
+ $config->save();
}
/**
diff --git a/core/modules/statistics/statistics.test b/core/modules/statistics/statistics.test
index 125ae6e..d710af2 100644
--- a/core/modules/statistics/statistics.test
+++ b/core/modules/statistics/statistics.test
@@ -69,7 +69,9 @@ class StatisticsLoggingTestCase extends DrupalWebTestCase {
$this->node = $this->drupalCreateNode(array('title' => $this->randomName(255), 'uid' => $this->auth_user->uid));
// Enable page caching.
- variable_set('cache', TRUE);
+ $config = config('system.performance');
+ $config->set('cache', 1);
+ $config->save();
// Enable access logging.
variable_set('statistics_enable_access_log', 1);
diff --git a/core/modules/system/config/system.performance.xml b/core/modules/system/config/system.performance.xml
new file mode 100644
index 0000000..8edf1eb
--- /dev/null
+++ b/core/modules/system/config/system.performance.xml
@@ -0,0 +1,9 @@
+
+
+ 0
+ 0
+ 0
+ 0
+ 0
+ 0
+
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index d28ecfb..259fd0e 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -1641,10 +1641,11 @@ function system_logging_settings() {
* Form builder; Configure site performance settings.
*
* @ingroup forms
- * @see system_settings_form()
+ * @see system_performance_settings_submit().
*/
-function system_performance_settings() {
+function system_performance_settings($form, &$form_state) {
drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
+ $config = config('system.performance');
$form['clear_cache'] = array(
'#type' => 'fieldset',
@@ -1662,11 +1663,10 @@ function system_performance_settings() {
'#title' => t('Caching'),
);
- $cache = variable_get('cache', 0);
$form['caching']['cache'] = array(
'#type' => 'checkbox',
'#title' => t('Cache pages for anonymous users'),
- '#default_value' => $cache,
+ '#default_value' => $config->get('cache'),
'#weight' => -2,
);
$period = drupal_map_assoc(array(0, 60, 180, 300, 600, 900, 1800, 2700, 3600, 10800, 21600, 32400, 43200, 86400), 'format_interval');
@@ -1674,16 +1674,16 @@ function system_performance_settings() {
$form['caching']['cache_lifetime'] = array(
'#type' => 'select',
'#title' => t('Minimum cache lifetime'),
- '#default_value' => variable_get('cache_lifetime', 0),
+ '#default_value' => $config->get('cache_lifetime'),
'#options' => $period,
- '#description' => t('Cached pages will not be re-created until at least this much time has elapsed.')
+ '#description' => t('Cached pages will not be re-created until at least this much time has elapsed.'),
);
$form['caching']['page_cache_maximum_age'] = array(
'#type' => 'select',
'#title' => t('Expiration of cached pages'),
- '#default_value' => variable_get('page_cache_maximum_age', 0),
+ '#default_value' => $config->get('page_cache_maximum_age'),
'#options' => $period,
- '#description' => t('The maximum time an external cache can use an old version of a page.')
+ '#description' => t('The maximum time an external cache can use an old version of a page.'),
);
$directory = 'public://';
@@ -1700,34 +1700,57 @@ function system_performance_settings() {
'#description' => t('External resources can be optimized automatically, which can reduce both the size and number of requests made to your website.') . $disabled_message,
);
- $js_hide = $cache ? '' : ' class="js-hide"';
+ $js_hide = $config->get('cache') ? '' : ' class="js-hide"';
$form['bandwidth_optimization']['page_compression'] = array(
'#type' => 'checkbox',
'#title' => t('Compress cached pages.'),
- '#default_value' => variable_get('page_compression', TRUE),
+ '#default_value' => $config->get('page_compression'),
'#prefix' => '',
'#suffix' => '
',
);
$form['bandwidth_optimization']['preprocess_css'] = array(
'#type' => 'checkbox',
'#title' => t('Aggregate and compress CSS files.'),
- '#default_value' => intval(variable_get('preprocess_css', 0) && $is_writable),
+ '#default_value' => $config->get('preprocess_css'),
'#disabled' => $disabled,
);
$form['bandwidth_optimization']['preprocess_js'] = array(
'#type' => 'checkbox',
'#title' => t('Aggregate JavaScript files.'),
- '#default_value' => intval(variable_get('preprocess_js', 0) && $is_writable),
+ '#default_value' => $config->get('preprocess_js'),
'#disabled' => $disabled,
);
+ $form['actions'] = array('#type' => 'actions');
+ $form['actions']['submit'] = array(
+ '#type' => 'submit',
+ '#value' => t('Save configuration'),
+ );
+
$form['#submit'][] = 'drupal_clear_css_cache';
$form['#submit'][] = 'drupal_clear_js_cache';
// This form allows page compression settings to be changed, which can
// invalidate the page cache, so it needs to be cleared on form submit.
$form['#submit'][] = 'system_clear_page_cache_submit';
+ $form['#submit'][] = 'system_performance_settings_submit';
- return system_settings_form($form);
+ return $form;
+}
+
+/**
+ * Form submission handler for system_performance_settings().
+ *
+ * @ingroup forms
+ */
+function system_performance_settings_submit($form, &$form_state) {
+ $config = config('system.performance');
+ $config->set('cache', $form_state['values']['cache']);
+ $config->set('cache_lifetime', $form_state['values']['cache_lifetime']);
+ $config->set('page_cache_maximum_age', $form_state['values']['page_cache_maximum_age']);
+ $config->set('page_compression', $form_state['values']['page_compression']);
+ $config->set('preprocess_css', $form_state['values']['preprocess_css']);
+ $config->set('preprocess_js', $form_state['values']['preprocess_js']);
+ $config->save();
}
/**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index d8112e6..45e369f 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -695,6 +695,28 @@ function system_schema() {
$schema['cache_path'] = $schema['cache'];
$schema['cache_path']['description'] = 'Cache table for path alias lookup.';
+ $schema['config'] = array(
+ 'description' => 'Default active store for the configuration system.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'The identifier for the configuration entry, such as module.example (the name of the file, minus the file extension).',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'data' => array(
+ 'description' => 'The raw data for this configuration entry.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ 'size' => 'big',
+ 'translatable' => TRUE,
+ ),
+ ),
+ 'primary key' => array('name'),
+ );
+
+
$schema['date_format_type'] = array(
'description' => 'Stores configured date format types.',
'fields' => array(
@@ -1666,6 +1688,36 @@ function system_update_8002() {
}
/**
+ * Adds {config} table for new Configuration system.
+ */
+function system_update_8003() {
+ // @todo Temporary.
+ if (db_table_exists('config')) {
+ db_drop_table('config');
+ }
+ db_create_table('config', array(
+ 'description' => 'Default active store for the configuration system.',
+ 'fields' => array(
+ 'name' => array(
+ 'description' => 'The identifier for the configuration entry, such as module.example (the name of the file, minus the file extension).',
+ 'type' => 'varchar',
+ 'length' => 255,
+ 'not null' => TRUE,
+ 'default' => '',
+ ),
+ 'data' => array(
+ 'description' => 'The raw data for this configuration entry.',
+ 'type' => 'blob',
+ 'not null' => TRUE,
+ 'size' => 'big',
+ 'translatable' => TRUE,
+ ),
+ ),
+ 'primary key' => array('name'),
+ ));
+}
+
+/**
* @} End of "defgroup updates-7.x-to-8.x"
* The next series of updates should start at 9000.
*/
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 38c3f55..449cf7e 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -2771,12 +2771,27 @@ function system_settings_form_submit($form, &$form_state) {
// Exclude unnecessary elements.
form_state_values_clean($form_state);
+ $config_objects = array();
foreach ($form_state['values'] as $key => $value) {
+ if (isset($form_state['config'][$key])) {
+ $config_name = $form_state['config'][$key]['name'];
+ $config_key = $form_state['config'][$key]['path'];
+ if (empty($config_objects[$config_name])) {
+ $config_objects[$config_name] = config($config_name);
+ }
+ if (!empty($config_objects[$config_name])) {
+ $config_objects[$config_name]->set($config_key, $value);
+ continue;
+ }
+ }
if (is_array($value) && isset($form_state['values']['array_filter'])) {
$value = array_keys(array_filter($value));
}
variable_set($key, $value);
}
+ foreach ($config_objects as $config) {
+ $config->save();
+ }
drupal_set_message(t('The configuration options have been saved.'));
}
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index 83548f4..b24ff5d 100755
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -237,6 +237,28 @@ $update_free_access = FALSE;
$drupal_hash_salt = '';
/**
+ * Location of the site configuration files.
+ *
+ * By default, Drupal configuration files are stored in a randomly named
+ * directory under the default public files path. On install the
+ * named directory is created in the default files directory. For enhanced
+ * security, you may set this variable to a location outside your docroot.
+ *
+ * @todo Flesh this out, provide more details, etc.
+ *
+ * Example:
+ * $config_directory_name = '/some/directory/outside/webroot';
+ */
+$config_directory_name = '';
+
+/**
+ * Configuration signature key.
+ *
+ * Drupal configuration files are signed using this key.
+ */
+$config_signature_key = '';
+
+/**
* Base URL (optional).
*
* If Drupal is generating incorrect URLs on your site, which could