X-Git-Url: http://www.aleph1.co.uk/gitweb/?a=blobdiff_plain;f=web%2Fcore%2Fmodules%2Fsystem%2Fsrc%2FForm%2FThemeSettingsForm.php;fp=web%2Fcore%2Fmodules%2Fsystem%2Fsrc%2FForm%2FThemeSettingsForm.php;h=f5cfb090826095502702deace675154248c0642b;hb=a2bd1bf0c2c1f1a17d188f4dc0726a45494cefae;hp=0000000000000000000000000000000000000000;hpb=57c063afa3f66b07c4bbddc2d6129a96d90f0aad;p=yaffs-website diff --git a/web/core/modules/system/src/Form/ThemeSettingsForm.php b/web/core/modules/system/src/Form/ThemeSettingsForm.php new file mode 100644 index 000000000..f5cfb0908 --- /dev/null +++ b/web/core/modules/system/src/Form/ThemeSettingsForm.php @@ -0,0 +1,500 @@ +moduleHandler = $module_handler; + $this->themeHandler = $theme_handler; + $this->mimeTypeGuesser = $mime_type_guesser; + $this->themeManager = $theme_manager; + } + + /** + * {@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') + ); + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'system_theme_settings'; + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return $this->editableConfig; + } + + /** + * {@inheritdoc} + * + * @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' => t('User pictures in posts'), + 'comment_user_picture' => t('User pictures in comments'), + 'comment_user_verification' => t('User verification status in comments'), + 'favicon' => 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' => 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' => t('Logo image'), + '#open' => TRUE, + ]; + $form['logo']['default_logo'] = [ + '#type' => 'checkbox', + '#title' => 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' => t('Path to custom logo'), + '#default_value' => theme_get_setting('logo.path', $theme), + ]; + $form['logo']['settings']['logo_upload'] = [ + '#type' => 'file', + '#title' => t('Upload logo image'), + '#maxlength' => 40, + '#description' => t("If you don't have direct file access to the server, use this field to upload your logo.") + ]; + } + + if (((!$theme) || in_array('favicon', $features)) && $this->moduleHandler->moduleExists('file')) { + $form['favicon'] = [ + '#type' => 'details', + '#title' => t('Favicon'), + '#open' => TRUE, + '#description' => 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' => 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' => t('Path to custom icon'), + '#default_value' => theme_get_setting('favicon.path', $theme), + ]; + $form['favicon']['settings']['favicon_upload'] = [ + '#type' => 'file', + '#title' => t('Upload favicon image'), + '#description' => t("If you don't have direct file access to the server, use this field to upload your shortcut icon.") + ]; + } + + // 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 (file_uri_scheme($original_path) == 'public') { + $friendly_path = file_uri_target($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 = drupal_get_path('theme', $theme) . '/' . $default; + } + else { + $local_file = $this->themeManager->getActiveTheme()->getPath() . '/' . $default; + } + + $element['#description'] = t('Examples: @implicit-public-file (for a file in the public filesystem), @explicit-file, or @local-file.', [ + '@implicit-public-file' => isset($friendly_path) ? $friendly_path : $default, + '@explicit-file' => file_uri_scheme($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' => t('Theme-engine-specific settings'), + '#open' => TRUE, + '#description' => 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. + $filename = DRUPAL_ROOT . '/' . $themes[$theme]->getPath() . '/theme-settings.php'; + if (file_exists($filename)) { + require_once $filename; + } + + // 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')) { + // Handle file uploads. + $validators = ['file_validate_is_image' => []]; + + // Check for a new uploaded logo. + $file = file_save_upload('logo_upload', $validators, FALSE, 0); + if (isset($file)) { + // File upload was attempted. + if ($file) { + // Put the temporary file in form_values so we can save it on submit. + $form_state->setValue('logo_upload', $file); + } + else { + // File upload failed. + $form_state->setErrorByName('logo_upload', $this->t('The logo could not be uploaded.')); + } + } + + $validators = ['file_validate_extensions' => ['ico png gif jpg jpeg apng svg']]; + + // Check for a new uploaded favicon. + $file = file_save_upload('favicon_upload', $validators, FALSE, 0); + if (isset($file)) { + // File upload was attempted. + if ($file) { + // Put the temporary file in form_values so we can save it on submit. + $form_state->setValue('favicon_upload', $file); + } + else { + // File upload failed. + $form_state->setErrorByName('favicon_upload', $this->t('The favicon could not be uploaded.')); + } + } + + // 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. + if (!empty($values['logo_upload'])) { + $filename = file_unmanaged_copy($values['logo_upload']->getFileUri()); + $values['default_logo'] = 0; + $values['logo_path'] = $filename; + } + if (!empty($values['favicon_upload'])) { + $filename = file_unmanaged_copy($values['favicon_upload']->getFileUri()); + $values['default_favicon'] = 0; + $values['favicon_path'] = $filename; + $values['toggle_favicon'] = 1; + } + 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'])) { + $values['favicon_mimetype'] = $this->mimeTypeGuesser->guess($values['favicon_path']); + } + + 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 (drupal_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 (file_uri_scheme($path) === FALSE) { + $path = 'public://' . $path; + } + if (is_file($path)) { + return $path; + } + return FALSE; + } + +}