Commit 31513883 authored by catch's avatar catch

Issue #1712250 by alexpott, n3or, cam8001, vijaycs85, Albert Volkman, jibran,...

Issue #1712250 by alexpott, n3or, cam8001, vijaycs85, Albert Volkman, jibran, heyrocker, chx: Convert theme settings to configuration system.
parent cb4782ea
......@@ -9,8 +9,10 @@
*/
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Config\Config;
use Drupal\Core\Template\Attribute;
use Drupal\Core\Utility\ThemeRegistry;
use Drupal\Core\Theme\ThemeSettings;
/**
* @defgroup content_flags Content markers
......@@ -730,7 +732,7 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) {
* the themes' machine names, and the values are the themes' human-readable
* names. This element is not set if there are no themes on the system that
* declare this theme as their base theme.
*/
*/
function list_themes($refresh = FALSE) {
$list = &drupal_static(__FUNCTION__, array());
......@@ -1379,7 +1381,6 @@ function drupal_find_theme_templates($cache, $extension, $path) {
*
* The final setting is obtained from the last value found in the following
* sources:
* - the default global settings specified in this function
* - the default theme-specific settings defined in any base theme's .info.yml
* file
* - the default theme-specific settings defined in the theme's .info.yml file
......@@ -1405,22 +1406,8 @@ function theme_get_setting($setting_name, $theme = NULL) {
}
if (empty($cache[$theme])) {
// Set the default values for each global setting.
// To add new global settings, add their default values below, and then
// add form elements to system_theme_settings() in system.admin.inc.
$cache[$theme] = array(
'default_logo' => 1,
'logo_path' => '',
'default_favicon' => 1,
'favicon_path' => '',
// Use the IANA-registered MIME type for ICO files as default.
'favicon_mimetype' => 'image/vnd.microsoft.icon',
);
// Turn on all default features.
$features = _system_default_theme_features();
foreach ($features as $feature) {
$cache[$theme]['toggle_' . $feature] = 1;
}
// Create a theme settings object.
$cache[$theme] = new ThemeSettings($theme);
// Get the values for the theme-specific settings from the .info.yml files
// of the theme and all its base themes.
......@@ -1438,59 +1425,103 @@ function theme_get_setting($setting_name, $theme = NULL) {
}
foreach ($theme_keys as $theme_key) {
if (!empty($themes[$theme_key]->info['settings'])) {
$cache[$theme] = array_merge($cache[$theme], $themes[$theme_key]->info['settings']);
$cache[$theme]->mergeData($themes[$theme_key]->info['settings']);
}
}
}
// Get the saved global settings from the database.
$cache[$theme] = array_merge($cache[$theme], variable_get('theme_settings', array()));
// Get the global settings from configuration.
$cache[$theme]->mergeData(Drupal::config('system.theme.global')->get());
if ($theme) {
// Get the saved theme-specific settings from the database.
$cache[$theme] = array_merge($cache[$theme], variable_get('theme_' . $theme . '_settings', array()));
// Get the saved theme-specific settings from the configuration system.
$cache[$theme]->mergeData(Drupal::config($theme . '.settings')->get());
// If the theme does not support a particular feature, override the global
// setting and set the value to NULL.
//$supports = $cache[$theme]->get('supports');
if (!empty($theme_object->info['features'])) {
foreach ($features as $feature) {
foreach (_system_default_theme_features() as $feature) {
if (!in_array($feature, $theme_object->info['features'])) {
$cache[$theme]['toggle_' . $feature] = NULL;
$cache[$theme]->set('features.' . $feature, NULL);
}
}
}
// Generate the path to the logo image.
if ($cache[$theme]['toggle_logo']) {
if ($cache[$theme]['default_logo']) {
$cache[$theme]['logo'] = file_create_url(dirname($theme_object->filename) . '/logo.png');
if ($cache[$theme]->get('features.logo')) {
$logo_path = $cache[$theme]->get('logo.path');
if ($cache[$theme]->get('logo.use_default')) {
$cache[$theme]->set('logo.url', file_create_url(dirname($theme_object->filename) . '/logo.png'));
}
elseif ($cache[$theme]['logo_path']) {
$cache[$theme]['logo'] = file_create_url($cache[$theme]['logo_path']);
elseif ($logo_path) {
$cache[$theme]->set('logo.url', file_create_url($logo_path));
}
}
// Generate the path to the favicon.
if ($cache[$theme]['toggle_favicon']) {
if ($cache[$theme]['default_favicon']) {
if ($cache[$theme]->get('features.favicon')) {
$favicon_path = $cache[$theme]->get('favicon.path');
if ($cache[$theme]->get('favicon.use_default')) {
if (file_exists($favicon = dirname($theme_object->filename) . '/favicon.ico')) {
$cache[$theme]['favicon'] = file_create_url($favicon);
$cache[$theme]->set('favicon.url', file_create_url($favicon));
}
else {
$cache[$theme]['favicon'] = file_create_url('core/misc/favicon.ico');
$cache[$theme]->set('favicon.url', file_create_url('core/misc/favicon.ico'));
}
}
elseif ($cache[$theme]['favicon_path']) {
$cache[$theme]['favicon'] = file_create_url($cache[$theme]['favicon_path']);
elseif ($favicon_path) {
$cache[$theme]->set('favicon.url', file_create_url($favicon_path));
}
else {
$cache[$theme]['toggle_favicon'] = FALSE;
$cache[$theme]->set('features.favicon', FALSE);
}
}
}
}
return isset($cache[$theme][$setting_name]) ? $cache[$theme][$setting_name] : NULL;
return $cache[$theme]->get($setting_name);
}
/**
* Converts theme settings to configuration.
*
* @see system_theme_settings_submit()
* @see system_update_8054()
*
* @param array $theme_settings
* An array of theme settings from system setting form or a Drupal 7 variable.
* @param Config $config
* The configuration object to update.
*
* @return
* The Config object with updated data.
*/
function theme_settings_convert_to_config(array $theme_settings, Config $config) {
foreach ($theme_settings as $key => $value) {
if ($key == 'default_logo') {
$config->set('logo.use_default', $value);
}
else if ($key == 'logo_path') {
$config->set('logo.path', $value);
}
else if ($key == 'default_favicon') {
$config->set('favicon.use_default', $value);
}
else if ($key == 'favicon_path') {
$config->set('favicon.path', $value);
}
else if ($key == 'favicon_mimetype') {
$config->set('favicon.mimetype', $value);
}
else if (substr($key, 0, 7) == 'toggle_') {
$config->set('features.' . drupal_substr($key, 7), $value);
}
else if (!in_array($key, array('theme', 'logo_upload'))) {
$config->set($key, $value);
}
}
return $config;
}
/**
......@@ -2747,9 +2778,9 @@ function template_preprocess_html(&$variables) {
$variables['html_attributes']['dir'] = $language_interface->direction ? 'rtl' : 'ltr';
// Add favicon.
if (theme_get_setting('toggle_favicon')) {
$favicon = theme_get_setting('favicon');
$type = theme_get_setting('favicon_mimetype');
if (theme_get_setting('features.favicon')) {
$favicon = theme_get_setting('favicon.url');
$type = theme_get_setting('favicon.mimetype');
drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => drupal_strip_dangerous_protocols($favicon), 'type' => $type));
}
......@@ -2855,12 +2886,12 @@ function template_preprocess_page(&$variables) {
$variables['feed_icons'] = drupal_get_feeds();
$variables['language'] = $language_interface;
$variables['language']->dir = $language_interface->direction ? 'rtl' : 'ltr';
$variables['logo'] = theme_get_setting('logo');
$variables['main_menu'] = theme_get_setting('toggle_main_menu') ? menu_main_menu() : array();
$variables['secondary_menu'] = theme_get_setting('toggle_secondary_menu') ? menu_secondary_menu() : array();
$variables['logo'] = theme_get_setting('logo.url');
$variables['main_menu'] = theme_get_setting('features.main_menu') ? menu_main_menu() : array();
$variables['secondary_menu'] = theme_get_setting('features.secondary_menu') ? menu_secondary_menu() : array();
$variables['action_links'] = menu_local_actions();
$variables['site_name'] = (theme_get_setting('toggle_name') ? check_plain($site_config->get('name')) : '');
$variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? filter_xss_admin($site_config->get('slogan')) : '');
$variables['site_name'] = (theme_get_setting('features.name') ? check_plain($site_config->get('name')) : '');
$variables['site_slogan'] = (theme_get_setting('features.slogan') ? filter_xss_admin($site_config->get('slogan')) : '');
$variables['tabs'] = menu_local_tabs();
if ($node = menu_get_object()) {
......@@ -3019,9 +3050,9 @@ function template_preprocess_maintenance_page(&$variables) {
$regions = $theme_data[$theme]->info['regions'];
// Add favicon
if (theme_get_setting('toggle_favicon')) {
$favicon = theme_get_setting('favicon');
$type = theme_get_setting('favicon_mimetype');
if (theme_get_setting('features.favicon')) {
$favicon = theme_get_setting('favicon.url');
$type = theme_get_setting('favicon.mimetype');
drupal_add_html_head_link(array('rel' => 'shortcut icon', 'href' => drupal_strip_dangerous_protocols($favicon), 'type' => $type));
}
......@@ -3068,12 +3099,12 @@ function template_preprocess_maintenance_page(&$variables) {
$variables['help'] = '';
$variables['language'] = $language_interface;
$variables['language']->dir = $language_interface->direction ? 'rtl' : 'ltr';
$variables['logo'] = theme_get_setting('logo');
$variables['logo'] = theme_get_setting('logo.url');
$variables['messages'] = $variables['show_messages'] ? theme('status_messages') : '';
$variables['main_menu'] = array();
$variables['secondary_menu'] = array();
$variables['site_name'] = (theme_get_setting('toggle_name') ? check_plain($site_name) : '');
$variables['site_slogan'] = (theme_get_setting('toggle_slogan') ? filter_xss_admin($site_slogan) : '');
$variables['site_name'] = (theme_get_setting('features.name') ? check_plain($site_name) : '');
$variables['site_slogan'] = (theme_get_setting('features.slogan') ? filter_xss_admin($site_slogan) : '');
$variables['tabs'] = '';
$variables['title'] = drupal_get_title();
......
......@@ -1479,6 +1479,39 @@ function update_config_manifest_add($config_prefix, array $ids) {
$manifest->save();
}
/**
* Installs a default configuration file into the active store.
*
* Provide a generalised method to save a default configuration object for an
* already enabled module or theme as part of an update from Drupal 7 to Drupal
* 8's configuration management system.
*
* @param string $type
* The extension type; e.g., 'module' or 'theme'.
* @param string $config_name
* The configuration object name to retrieve.
*
* @return boolean
* True on success, false if config file does not exist.
*/
function update_7_to_8_install_default_config($type, $config_name) {
// Build the new configuration object.
$config = Drupal::config($config_name);
// Extract the extension namespace/owner from the configuration object name.
$name = strtok($config_name, '.');
// Load and set default configuration values.
$file = new FileStorage(drupal_get_path($type, $name) . '/config');
if (!$file->exists($config_name)) {
return FALSE;
}
$default_data = $file->read($config_name);
// Apply and save the default values.
$config->setData($file->read($config_name))->save();
return TRUE;
}
/**
* Updates 7.x variables to state records.
*
......
......@@ -112,7 +112,7 @@ protected function getAllFolders() {
if (!isset($this->folders)) {
$this->folders = $this->getComponentNames('profile', array(drupal_get_profile()));
$this->folders += $this->getComponentNames('module', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0)));
$this->folders += $this->getComponentNames('theme', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info$/', 'themes')));
$this->folders += $this->getComponentNames('theme', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes')));
}
return $this->folders;
}
......
<?php
/**
* @file
* Contains \Drupal\Core\Theme\ThemeSettings.
*/
namespace Drupal\Core\Theme;
use Drupal\Component\Utility\NestedArray;
/**
* Defines the default theme settings object.
*/
class ThemeSettings {
/**
* The theme of the theme settings object.
*
* @var string
*/
protected $theme;
/**
* The data of the theme settings object.
*
* @var array
*/
protected $data;
/**
* Constructs a theme settings object.
*
* @param string $name
* The name of the theme settings object being constructed.
*/
public function __construct($theme) {
$this->theme = $theme;
$this->data = array();
}
/**
* Returns the theme of this theme settings object.
*
* @return string
* The theme of this theme settings object.
*/
public function getTheme() {
return $this->theme;
}
/**
* Gets data from this theme settings object.
*
* @param string $key
* A string that maps to a key within the theme settings data.
* For instance in the following theme settings array:
* @code
* array(
* 'foo' => array(
* 'bar' => '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.
*
*
* @return mixed
* 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 {
$value = NestedArray::getValue($this->data, $parts, $key_exists);
return $key_exists ? $value : NULL;
}
}
}
/**
* Replaces the data of this theme settings object.
*
* @param array $data
* The new theme settings data.
*
* @return \Drupal\Core\Theme\ThemeSettings
* The theme settings object.
*/
public function setData(array $data) {
$this->data = $data;
return $this;
}
/**
* Sets value in this theme settings object.
*
* @param string $key
* Identifier to store value in theme settings.
* @param string $value
* Value to associate with identifier.
*
* @return \Drupal\Core\Theme\ThemeSettings
* The theme settings object.
*/
public function set($key, $value) {
// The dot/period is a reserved character; it may appear between keys, but
// not within keys.
$parts = explode('.', $key);
if (count($parts) == 1) {
$this->data[$key] = $value;
}
else {
NestedArray::setValue($this->data, $parts, $value);
}
return $this;
}
/**
* Unsets value in this theme settings object.
*
* @param string $key
* Name of the key whose value should be unset.
*
* @return \Drupal\Core\Theme\ThemeSettings
* The theme settings object.
*/
public function clear($key) {
$parts = explode('.', $key);
if (count($parts) == 1) {
unset($this->data[$key]);
}
else {
NestedArray::unsetValue($this->data, $parts);
}
return $this;
}
/**
* Merges the data into this theme settings object.
*
* @param array $data
* Theme settings data to merge.
*
* @return \Drupal\Core\Theme\ThemeSettings
* The theme settings object.
*/
public function mergeData ($data) {
$this->data = NestedArray::mergeDeep($this->data, $data);
return $this;
}
}
......@@ -1576,7 +1576,7 @@ function template_preprocess_comment(&$variables) {
$variables['changed'] = format_date($comment->changed->value);
}
if (theme_get_setting('toggle_comment_user_picture')) {
if (theme_get_setting('features.comment_user_picture')) {
// To change user picture settings (e.g., image style), edit the 'compact'
// view mode on the User entity.
$variables['user_picture'] = user_view($account, 'compact');
......
......@@ -1166,7 +1166,7 @@ function template_preprocess_node(&$variables) {
if (variable_get('node_submitted_' . $node->type, TRUE)) {
$variables['display_submitted'] = TRUE;
$variables['submitted'] = t('Submitted by !username on !datetime', array('!username' => $variables['name'], '!datetime' => $variables['date']));
if (theme_get_setting('toggle_node_user_picture')) {
if (theme_get_setting('features.node_user_picture')) {
// To change user picture settings (e.g. image style), edit the 'compact'
// view mode on the User entity. Note that the 'compact' view mode might
// not be configured, so remember to always check the theme setting first.
......
favicon:
mimetype: image/vnd.microsoft.icon
path: ''
url: ''
use_default: '1'
features:
comment_user_picture: '1'
comment_user_verification: '1'
favicon: '1'
logo: '1'
name: '1'
node_user_picture: '1'
main_menu: '1'
secondary_menu: '1'
slogan: '1'
logo:
path: ''
url: ''
use_default: '1'
......@@ -390,20 +390,24 @@ function system_theme_default() {
* @return
* The form structure.
* @ingroup forms
* @see system_theme_settings_validate()
* @see system_theme_settings_submit()
*/
function system_theme_settings($form, &$form_state, $key = '') {
// Default settings are defined in theme_get_setting() in includes/theme.inc
if ($key) {
$var = 'theme_' . $key . '_settings';
$config_key = $key . '.settings';
$themes = list_themes();
$features = $themes[$key]->info['features'];
}
else {
$var = 'theme_settings';
$config_key = 'system.theme.global';
}
$form['var'] = array('#type' => 'hidden', '#value' => $var);
$form['config_key'] = array('#type' => 'hidden', '#value' => $config_key);
// Toggle settings
$toggles = array(
......@@ -436,7 +440,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
);
foreach ($toggles as $name => $title) {
if ((!$key) || in_array($name, $features)) {
$form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => theme_get_setting('toggle_' . $name, $key));
$form['theme_settings']['toggle_' . $name] = array('#type' => 'checkbox', '#title' => $title, '#default_value' => theme_get_setting('features.' . $name, $key));
// Disable checkboxes for features not supported in the current configuration.
if (isset($disabled['toggle_' . $name])) {
$form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
......@@ -466,7 +470,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
$form['logo']['default_logo'] = array(
'#type' => 'checkbox',
'#title' => t('Use the default logo supplied by the theme'),
'#default_value' => theme_get_setting('default_logo', $key),
'#default_value' => theme_get_setting('logo.use_default', $key),
'#tree' => FALSE,
);
$form['logo']['settings'] = array(
......@@ -481,7 +485,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
$form['logo']['settings']['logo_path'] = array(
'#type' => 'textfield',
'#title' => t('Path to custom logo'),
'#default_value' => theme_get_setting('logo_path', $key),
'#default_value' => theme_get_setting('logo.path', $key),
);
$form['logo']['settings']['logo_upload'] = array(
'#type' => 'file',
......@@ -507,7 +511,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
$form['favicon']['default_favicon'] = array(
'#type' => 'checkbox',
'#title' => t('Use the default shortcut icon supplied by the theme'),
'#default_value' => theme_get_setting('default_favicon', $key),
'#default_value' => theme_get_setting('favicon.use_default', $key),
);
$form['favicon']['settings'] = array(
'#type' => 'container',
......@@ -521,7 +525,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
$form['favicon']['settings']['favicon_path'] = array(
'#type' => 'textfield',
'#title' => t('Path to custom icon'),
'#default_value' => theme_get_setting('favicon_path', $key),
'#default_value' => theme_get_setting('favicon.path', $key),
);
$form['favicon']['settings']['favicon_upload'] = array(
'#type' => 'file',
......@@ -615,12 +619,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
}
}
$form = system_settings_form($form);
// We don't want to call system_settings_form_submit(), so change #submit.
array_pop($form['#submit']);
$form['#submit'][] = 'system_theme_settings_submit';
$form['#validate'][] = 'system_theme_settings_validate';
return $form;
return system_config_form($form, $form_state);
}
/**
......@@ -715,14 +714,15 @@ function _system_theme_settings_validate_path($path) {
* Process system_theme_settings form submissions.
*/
function system_theme_settings_submit($form, &$form_state) {
$config = Drupal::config($form_state['values']['config_key']);
// Exclude unnecessary elements before saving.
form_state_values_clean($form_state);
$values = $form_state['values'];
$key = $form_state['values']['var'];
unset($form_state['values']['var']);
unset($form_state['values']['config_key']);
// Extract the name of the theme from the submitted form values, then
// remove it from the array so that it is not saved as part of the variable.
$key = $values['var'];
unset($values['var']);
$values = $form_state['values'];
// If the user uploaded a new logo or favicon, save it to a permanent location
// and use it in place of the default theme-provided file.
......@@ -756,8 +756,7 @@ function system_theme_settings_submit($form, &$form_state) {
}
}
variable_set($key, $values);
drupal_set_message(t('The configuration options have been saved.'));
theme_settings_convert_to_config($values, $config)->save();
cache_invalidate_tags(array('content' => TRUE));
}
......
......@@ -2101,6 +2101,46 @@ function system_update_8054() {
db_add_field('menu_router', 'route_name', $spec);
}
/**
* Moves system theme settings from variables to config.
*/
function system_update_8054() {
// Install the global theme settings from the system module.
update_7_to_8_install_default_config('module', 'system.theme.global');
// Add the global settings to a map for variable to configuration conversion.
$theme_settings_to_config_map = array('theme_settings' => 'system.theme.global');
// Can only upgrade core themes since if you follow the instructions in
// UPGRADE.txt list_themes() would only return core themes at this point.
// Therefore limit to a hard coded list to ensure that this update only
// applies to core provided themes.
$core_themes = array(
'bartik',
'seven',
'stark',
);
// Add the core theme to the variable to configuration conversion map and