class ThemeSettingsForm in Drupal 9
Same name and namespace in other branches
- 8 core/modules/system/src/Form/ThemeSettingsForm.php \Drupal\system\Form\ThemeSettingsForm
Displays theme configuration for entire site and individual themes.
@internal
Hierarchy
- class \Drupal\Core\Form\FormBase implements ContainerInjectionInterface, FormInterface uses DependencySerializationTrait, LoggerChannelTrait, MessengerTrait, RedirectDestinationTrait, StringTranslationTrait
- class \Drupal\Core\Form\ConfigFormBase uses ConfigFormBaseTrait
- class \Drupal\system\Form\ThemeSettingsForm
- class \Drupal\Core\Form\ConfigFormBase uses ConfigFormBaseTrait
Expanded class hierarchy of ThemeSettingsForm
1 string reference to 'ThemeSettingsForm'
- system.routing.yml in core/
modules/ system/ system.routing.yml - core/modules/system/system.routing.yml
File
- core/
modules/ system/ src/ Form/ ThemeSettingsForm.php, line 25
Namespace
Drupal\system\FormView source
class ThemeSettingsForm extends ConfigFormBase {
/**
* The module handler.
*
* @var \Drupal\Core\Extension\ModuleHandlerInterface
*/
protected $moduleHandler;
/**
* The theme handler.
*
* @var \Drupal\Core\Extension\ThemeHandlerInterface
*/
protected $themeHandler;
/**
* The MIME type guesser.
*
* @var \Symfony\Component\Mime\MimeTypeGuesserInterface
*/
protected $mimeTypeGuesser;
/**
* An array of configuration names that should be editable.
*
* @var array
*/
protected $editableConfig = [];
/**
* The theme manager.
*
* @var \Drupal\Core\Theme\ThemeManagerInterface
*/
protected $themeManager;
/**
* The file system.
*
* @var \Drupal\Core\File\FileSystemInterface
*/
protected $fileSystem;
/**
* Constructs a ThemeSettingsForm object.
*
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
* The factory for configuration objects.
* @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
* The module handler instance to use.
* @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
* The theme handler.
* @param \Symfony\Component\Mime\MimeTypeGuesserInterface $mime_type_guesser
* The MIME type guesser instance to use.
* @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
* The theme manager.
* @param \Drupal\Core\File\FileSystemInterface $file_system
* The file system.
*/
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, $mime_type_guesser, ThemeManagerInterface $theme_manager, FileSystemInterface $file_system) {
parent::__construct($config_factory);
$this->moduleHandler = $module_handler;
$this->themeHandler = $theme_handler;
$this->mimeTypeGuesser = $mime_type_guesser;
$this->themeManager = $theme_manager;
$this->fileSystem = $file_system;
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static($container
->get('config.factory'), $container
->get('module_handler'), $container
->get('theme_handler'), $container
->get('file.mime_type.guesser'), $container
->get('theme.manager'), $container
->get('file_system'));
}
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'system_theme_settings';
}
/**
* {@inheritdoc}
*/
protected function getEditableConfigNames() {
return $this->editableConfig;
}
/**
* {@inheritdoc}
*
* @param array $form
* An associative array containing the structure of the form.
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current state of the form.
* @param string $theme
* The theme name.
*/
public function buildForm(array $form, FormStateInterface $form_state, $theme = '') {
$form = parent::buildForm($form, $form_state);
$themes = $this->themeHandler
->listInfo();
// Default settings are defined in theme_get_setting() in includes/theme.inc
if ($theme) {
if (!$this->themeHandler
->hasUi($theme)) {
throw new NotFoundHttpException();
}
$var = 'theme_' . $theme . '_settings';
$config_key = $theme . '.settings';
$themes = $this->themeHandler
->listInfo();
$features = $themes[$theme]->info['features'];
}
else {
$var = 'theme_settings';
$config_key = 'system.theme.global';
}
// @todo this is pretty meaningless since we're using theme_get_settings
// which means overrides can bleed into active config here. Will be fixed
// by https://www.drupal.org/node/2402467.
$this->editableConfig = [
$config_key,
];
$form['var'] = [
'#type' => 'hidden',
'#value' => $var,
];
$form['config_key'] = [
'#type' => 'hidden',
'#value' => $config_key,
];
// Toggle settings
$toggles = [
'node_user_picture' => $this
->t('User pictures in posts'),
'comment_user_picture' => $this
->t('User pictures in comments'),
'comment_user_verification' => $this
->t('User verification status in comments'),
'favicon' => $this
->t('Shortcut icon'),
];
// Some features are not always available
$disabled = [];
if (!user_picture_enabled()) {
$disabled['toggle_node_user_picture'] = TRUE;
$disabled['toggle_comment_user_picture'] = TRUE;
}
if (!$this->moduleHandler
->moduleExists('comment')) {
$disabled['toggle_comment_user_picture'] = TRUE;
$disabled['toggle_comment_user_verification'] = TRUE;
}
$form['theme_settings'] = [
'#type' => 'details',
'#title' => $this
->t('Page element display'),
'#open' => TRUE,
];
foreach ($toggles as $name => $title) {
if (!$theme || in_array($name, $features)) {
$form['theme_settings']['toggle_' . $name] = [
'#type' => 'checkbox',
'#title' => $title,
'#default_value' => theme_get_setting('features.' . $name, $theme),
];
// Disable checkboxes for features not supported in the current configuration.
if (isset($disabled['toggle_' . $name])) {
$form['theme_settings']['toggle_' . $name]['#disabled'] = TRUE;
}
}
}
if (!Element::children($form['theme_settings'])) {
// If there is no element in the theme settings details then do not show
// it -- but keep it in the form if another module wants to alter.
$form['theme_settings']['#access'] = FALSE;
}
// Logo settings, only available when file.module is enabled.
if ((!$theme || in_array('logo', $features)) && $this->moduleHandler
->moduleExists('file')) {
$form['logo'] = [
'#type' => 'details',
'#title' => $this
->t('Logo image'),
'#open' => TRUE,
];
$form['logo']['default_logo'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Use the logo supplied by the theme'),
'#default_value' => theme_get_setting('logo.use_default', $theme),
'#tree' => FALSE,
];
$form['logo']['settings'] = [
'#type' => 'container',
'#states' => [
// Hide the logo settings when using the default logo.
'invisible' => [
'input[name="default_logo"]' => [
'checked' => TRUE,
],
],
],
];
$form['logo']['settings']['logo_path'] = [
'#type' => 'textfield',
'#title' => $this
->t('Path to custom logo'),
'#default_value' => theme_get_setting('logo.path', $theme),
];
$form['logo']['settings']['logo_upload'] = [
'#type' => 'file',
'#title' => $this
->t('Upload logo image'),
'#maxlength' => 40,
'#description' => $this
->t("If you don't have direct file access to the server, use this field to upload your logo."),
'#upload_validators' => [
'file_validate_is_image' => [],
],
];
}
if ((!$theme || in_array('favicon', $features)) && $this->moduleHandler
->moduleExists('file')) {
$form['favicon'] = [
'#type' => 'details',
'#title' => $this
->t('Favicon'),
'#open' => TRUE,
'#description' => $this
->t("Your shortcut icon, or favicon, is displayed in the address bar and bookmarks of most browsers."),
'#states' => [
// Hide the shortcut icon settings fieldset when shortcut icon display
// is disabled.
'invisible' => [
'input[name="toggle_favicon"]' => [
'checked' => FALSE,
],
],
],
];
$form['favicon']['default_favicon'] = [
'#type' => 'checkbox',
'#title' => $this
->t('Use the favicon supplied by the theme'),
'#default_value' => theme_get_setting('favicon.use_default', $theme),
];
$form['favicon']['settings'] = [
'#type' => 'container',
'#states' => [
// Hide the favicon settings when using the default favicon.
'invisible' => [
'input[name="default_favicon"]' => [
'checked' => TRUE,
],
],
],
];
$form['favicon']['settings']['favicon_path'] = [
'#type' => 'textfield',
'#title' => $this
->t('Path to custom icon'),
'#default_value' => theme_get_setting('favicon.path', $theme),
];
$form['favicon']['settings']['favicon_upload'] = [
'#type' => 'file',
'#title' => $this
->t('Upload favicon image'),
'#description' => $this
->t("If you don't have direct file access to the server, use this field to upload your shortcut icon."),
'#upload_validators' => [
'file_validate_extensions' => [
'ico png gif jpg jpeg apng svg',
],
],
];
}
// Inject human-friendly values and form element descriptions for logo and
// favicon.
foreach ([
'logo' => 'logo.svg',
'favicon' => 'favicon.ico',
] as $type => $default) {
if (isset($form[$type]['settings'][$type . '_path'])) {
$element =& $form[$type]['settings'][$type . '_path'];
// If path is a public:// URI, display the path relative to the files
// directory; stream wrappers are not end-user friendly.
$original_path = $element['#default_value'];
$friendly_path = NULL;
if (StreamWrapperManager::getScheme($original_path) == 'public') {
$friendly_path = StreamWrapperManager::getTarget($original_path);
$element['#default_value'] = $friendly_path;
}
// Prepare local file path for description.
if ($original_path && isset($friendly_path)) {
$local_file = strtr($original_path, [
'public:/' => PublicStream::basePath(),
]);
}
elseif ($theme) {
$local_file = $this->themeHandler
->getTheme($theme)
->getPath() . '/' . $default;
}
else {
$local_file = $this->themeManager
->getActiveTheme()
->getPath() . '/' . $default;
}
$element['#description'] = $this
->t('Examples: <code>@implicit-public-file</code> (for a file in the public filesystem), <code>@explicit-file</code>, or <code>@local-file</code>.', [
'@implicit-public-file' => isset($friendly_path) ? $friendly_path : $default,
'@explicit-file' => StreamWrapperManager::getScheme($original_path) !== FALSE ? $original_path : 'public://' . $default,
'@local-file' => $local_file,
]);
}
}
if ($theme) {
// Call engine-specific settings.
$function = $themes[$theme]->prefix . '_engine_settings';
if (function_exists($function)) {
$form['engine_specific'] = [
'#type' => 'details',
'#title' => $this
->t('Theme-engine-specific settings'),
'#open' => TRUE,
'#description' => $this
->t('These settings only exist for the themes based on the %engine theme engine.', [
'%engine' => $themes[$theme]->prefix,
]),
];
$function($form, $form_state);
}
// Create a list which includes the current theme and all its base themes.
if (isset($themes[$theme]->base_themes)) {
$theme_keys = array_keys($themes[$theme]->base_themes);
$theme_keys[] = $theme;
}
else {
$theme_keys = [
$theme,
];
}
// Save the name of the current theme (if any), so that we can temporarily
// override the current theme and allow theme_get_setting() to work
// without having to pass the theme name to it.
$default_active_theme = $this->themeManager
->getActiveTheme();
$default_theme = $default_active_theme
->getName();
/** @var \Drupal\Core\Theme\ThemeInitialization $theme_initialization */
$theme_initialization = \Drupal::service('theme.initialization');
$this->themeManager
->setActiveTheme($theme_initialization
->getActiveThemeByName($theme));
// Process the theme and all its base themes.
foreach ($theme_keys as $theme) {
// Include the theme-settings.php file.
$theme_path = $this->themeHandler
->getTheme($theme)
->getPath();
$theme_settings_file = $theme_path . '/theme-settings.php';
$theme_file = $theme_path . '/' . $theme . '.theme';
$filenames = [
$theme_settings_file,
$theme_file,
];
foreach ($filenames as $filename) {
if (file_exists($filename)) {
require_once $filename;
// The file must be required for the cached form too.
$files = $form_state
->getBuildInfo()['files'];
if (!in_array($filename, $files)) {
$files[] = $filename;
}
$form_state
->addBuildInfo('files', $files);
}
}
// Call theme-specific settings.
$function = $theme . '_form_system_theme_settings_alter';
if (function_exists($function)) {
$function($form, $form_state);
}
}
// Restore the original current theme.
if (isset($default_theme)) {
$this->themeManager
->setActiveTheme($default_active_theme);
}
else {
$this->themeManager
->resetActiveTheme();
}
}
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
parent::validateForm($form, $form_state);
if ($this->moduleHandler
->moduleExists('file')) {
// Check for a new uploaded logo.
if (isset($form['logo'])) {
$file = _file_save_upload_from_form($form['logo']['settings']['logo_upload'], $form_state, 0);
if ($file) {
// Put the temporary file in form_values so we can save it on submit.
$form_state
->setValue('logo_upload', $file);
}
}
// Check for a new uploaded favicon.
if (isset($form['favicon'])) {
$file = _file_save_upload_from_form($form['favicon']['settings']['favicon_upload'], $form_state, 0);
if ($file) {
// Put the temporary file in form_values so we can save it on submit.
$form_state
->setValue('favicon_upload', $file);
}
}
// When intending to use the default logo, unset the logo_path.
if ($form_state
->getValue('default_logo')) {
$form_state
->unsetValue('logo_path');
}
// When intending to use the default favicon, unset the favicon_path.
if ($form_state
->getValue('default_favicon')) {
$form_state
->unsetValue('favicon_path');
}
// If the user provided a path for a logo or favicon file, make sure a file
// exists at that path.
if ($form_state
->getValue('logo_path')) {
$path = $this
->validatePath($form_state
->getValue('logo_path'));
if (!$path) {
$form_state
->setErrorByName('logo_path', $this
->t('The custom logo path is invalid.'));
}
}
if ($form_state
->getValue('favicon_path')) {
$path = $this
->validatePath($form_state
->getValue('favicon_path'));
if (!$path) {
$form_state
->setErrorByName('favicon_path', $this
->t('The custom favicon path is invalid.'));
}
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
parent::submitForm($form, $form_state);
$config_key = $form_state
->getValue('config_key');
$this->editableConfig = [
$config_key,
];
$config = $this
->config($config_key);
// Exclude unnecessary elements before saving.
$form_state
->cleanValues();
$form_state
->unsetValue('var');
$form_state
->unsetValue('config_key');
$values = $form_state
->getValues();
// 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.
$default_scheme = $this
->config('system.file')
->get('default_scheme');
try {
if (!empty($values['logo_upload'])) {
$filename = $this->fileSystem
->copy($values['logo_upload']
->getFileUri(), $default_scheme . '://');
$values['default_logo'] = 0;
$values['logo_path'] = $filename;
}
} catch (FileException $e) {
// Ignore.
}
try {
if (!empty($values['favicon_upload'])) {
$filename = $this->fileSystem
->copy($values['favicon_upload']
->getFileUri(), $default_scheme . '://');
$values['default_favicon'] = 0;
$values['favicon_path'] = $filename;
$values['toggle_favicon'] = 1;
}
} catch (FileException $e) {
// Ignore.
}
unset($values['logo_upload']);
unset($values['favicon_upload']);
// If the user entered a path relative to the system files directory for
// a logo or favicon, store a public:// URI so the theme system can handle it.
if (!empty($values['logo_path'])) {
$values['logo_path'] = $this
->validatePath($values['logo_path']);
}
if (!empty($values['favicon_path'])) {
$values['favicon_path'] = $this
->validatePath($values['favicon_path']);
}
if (empty($values['default_favicon']) && !empty($values['favicon_path'])) {
if ($this->mimeTypeGuesser instanceof MimeTypeGuesserInterface) {
$values['favicon_mimetype'] = $this->mimeTypeGuesser
->guessMimeType($values['favicon_path']);
}
else {
$values['favicon_mimetype'] = $this->mimeTypeGuesser
->guess($values['favicon_path']);
@trigger_error('\\Symfony\\Component\\HttpFoundation\\File\\MimeType\\MimeTypeGuesserInterface is deprecated in drupal:9.1.0 and is removed from drupal:10.0.0. Implement \\Symfony\\Component\\Mime\\MimeTypeGuesserInterface instead. See https://www.drupal.org/node/3133341', E_USER_DEPRECATED);
}
}
theme_settings_convert_to_config($values, $config)
->save();
}
/**
* Helper function for the system_theme_settings form.
*
* Attempts to validate normal system paths, paths relative to the public files
* directory, or stream wrapper URIs. If the given path is any of the above,
* returns a valid path or URI that the theme system can display.
*
* @param string $path
* A path relative to the Drupal root or to the public files directory, or
* a stream wrapper URI.
*
* @return mixed
* A valid path that can be displayed through the theme system, or FALSE if
* the path could not be validated.
*/
protected function validatePath($path) {
// Absolute local file paths are invalid.
if ($this->fileSystem
->realpath($path) == $path) {
return FALSE;
}
// A path relative to the Drupal root or a fully qualified URI is valid.
if (is_file($path)) {
return $path;
}
// Prepend 'public://' for relative file paths within public filesystem.
if (StreamWrapperManager::getScheme($path) === FALSE) {
$path = 'public://' . $path;
}
if (is_file($path)) {
return $path;
}
return FALSE;
}
}
Members
Name | Modifiers | Type | Description | Overrides |
---|---|---|---|---|
ConfigFormBaseTrait:: |
protected | function | Retrieves a configuration object. | |
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
protected | property | ||
DependencySerializationTrait:: |
public | function | 2 | |
DependencySerializationTrait:: |
public | function | 2 | |
FormBase:: |
protected | property | The config factory. | 3 |
FormBase:: |
protected | property | The request stack. | 1 |
FormBase:: |
protected | property | The route match. | |
FormBase:: |
protected | function | Gets the config factory for this form. | 3 |
FormBase:: |
private | function | Returns the service container. | |
FormBase:: |
protected | function | Gets the current user. | |
FormBase:: |
protected | function | Gets the request object. | |
FormBase:: |
protected | function | Gets the route match. | |
FormBase:: |
protected | function | Gets the logger for a specific channel. | |
FormBase:: |
protected | function | Returns a redirect response object for the specified route. | |
FormBase:: |
public | function | Resets the configuration factory. | |
FormBase:: |
public | function | Sets the config factory for this form. | |
FormBase:: |
public | function | Sets the request stack object to use. | |
LoggerChannelTrait:: |
protected | property | The logger channel factory service. | |
LoggerChannelTrait:: |
protected | function | Gets the logger for a specific channel. | |
LoggerChannelTrait:: |
public | function | Injects the logger channel factory. | |
MessengerTrait:: |
protected | property | The messenger. | 27 |
MessengerTrait:: |
public | function | Gets the messenger. | 27 |
MessengerTrait:: |
public | function | Sets the messenger. | |
RedirectDestinationTrait:: |
protected | property | The redirect destination service. | 1 |
RedirectDestinationTrait:: |
protected | function | Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url. | |
RedirectDestinationTrait:: |
protected | function | Returns the redirect destination service. | |
RedirectDestinationTrait:: |
public | function | Sets the redirect destination service. | |
StringTranslationTrait:: |
protected | property | The string translation service. | 4 |
StringTranslationTrait:: |
protected | function | Formats a string containing a count of items. | |
StringTranslationTrait:: |
protected | function | Returns the number of plurals supported by a given language. | |
StringTranslationTrait:: |
protected | function | Gets the string translation service. | |
StringTranslationTrait:: |
public | function | Sets the string translation service to use. | 2 |
StringTranslationTrait:: |
protected | function | Translates a string to the current language or to a given language. | |
ThemeSettingsForm:: |
protected | property | An array of configuration names that should be editable. | |
ThemeSettingsForm:: |
protected | property | The file system. | |
ThemeSettingsForm:: |
protected | property | The MIME type guesser. | |
ThemeSettingsForm:: |
protected | property | The module handler. | |
ThemeSettingsForm:: |
protected | property | The theme handler. | |
ThemeSettingsForm:: |
protected | property | The theme manager. | |
ThemeSettingsForm:: |
public | function |
Overrides ConfigFormBase:: |
|
ThemeSettingsForm:: |
public static | function |
Instantiates a new instance of this class. Overrides ConfigFormBase:: |
|
ThemeSettingsForm:: |
protected | function |
Gets the configuration names that will be editable. Overrides ConfigFormBaseTrait:: |
|
ThemeSettingsForm:: |
public | function |
Returns a unique string identifying the form. Overrides FormInterface:: |
|
ThemeSettingsForm:: |
public | function |
Form submission handler. Overrides ConfigFormBase:: |
|
ThemeSettingsForm:: |
public | function |
Form validation handler. Overrides FormBase:: |
|
ThemeSettingsForm:: |
protected | function | Helper function for the system_theme_settings form. | |
ThemeSettingsForm:: |
public | function |
Constructs a ThemeSettingsForm object. Overrides ConfigFormBase:: |