You are here

class ThemeSettingsForm in Zircon Profile 8

Same name and namespace in other branches
  1. 8.0 core/modules/system/src/Form/ThemeSettingsForm.php \Drupal\system\Form\ThemeSettingsForm

Displays theme configuration for entire site and individual themes.

Hierarchy

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 24
Contains \Drupal\system\Form\ThemeSettingsForm.

Namespace

Drupal\system\Form
View 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\HttpFoundation\File\MimeType\MimeTypeGuesserInterface
   */
  protected $mimeTypeGuesser;

  /**
   * An array of configuration names that should be editable.
   *
   * @var array
   */
  protected $editableConfig = [];

  /**
   * 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\HttpFoundation\File\MimeType\MimeTypeGuesserInterface $mime_type_guesser
   *   The MIME type guesser instance to use.
   */
  public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler, MimeTypeGuesserInterface $mime_type_guesser) {
    parent::__construct($config_factory);
    $this->moduleHandler = $module_handler;
    $this->themeHandler = $theme_handler;
    $this->mimeTypeGuesser = $mime_type_guesser;
  }

  /**
   * {@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'));
  }

  /**
   * {@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'] = array(
      '#type' => 'hidden',
      '#value' => $var,
    );
    $form['config_key'] = array(
      '#type' => 'hidden',
      '#value' => $config_key,
    );

    // Toggle settings
    $toggles = array(
      '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 = array();
    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'] = array(
      '#type' => 'details',
      '#title' => t('Toggle display'),
      '#open' => TRUE,
      '#description' => t('Enable or disable the display of certain page elements.'),
    );
    foreach ($toggles as $name => $title) {
      if (!$theme || in_array($name, $features)) {
        $form['theme_settings']['toggle_' . $name] = array(
          '#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'] = array(
        '#type' => 'details',
        '#title' => t('Logo image settings'),
        '#open' => TRUE,
        '#states' => array(
          // Hide the logo image settings fieldset when logo display is disabled.
          'invisible' => array(
            'input[name="toggle_logo"]' => array(
              'checked' => FALSE,
            ),
          ),
        ),
      );
      $form['logo']['default_logo'] = array(
        '#type' => 'checkbox',
        '#title' => t('Use the default logo supplied by the theme'),
        '#default_value' => theme_get_setting('logo.use_default', $theme),
        '#tree' => FALSE,
      );
      $form['logo']['settings'] = array(
        '#type' => 'container',
        '#states' => array(
          // Hide the logo settings when using the default logo.
          'invisible' => array(
            'input[name="default_logo"]' => array(
              'checked' => TRUE,
            ),
          ),
        ),
      );
      $form['logo']['settings']['logo_path'] = array(
        '#type' => 'textfield',
        '#title' => t('Path to custom logo'),
        '#default_value' => theme_get_setting('logo.path', $theme),
      );
      $form['logo']['settings']['logo_upload'] = array(
        '#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'] = array(
        '#type' => 'details',
        '#title' => t('Shortcut icon settings'),
        '#open' => TRUE,
        '#description' => t("Your shortcut icon, or 'favicon', is displayed in the address bar and bookmarks of most browsers."),
        '#states' => array(
          // Hide the shortcut icon settings fieldset when shortcut icon display
          // is disabled.
          'invisible' => array(
            'input[name="toggle_favicon"]' => array(
              'checked' => FALSE,
            ),
          ),
        ),
      );
      $form['favicon']['default_favicon'] = array(
        '#type' => 'checkbox',
        '#title' => t('Use the default shortcut icon supplied by the theme'),
        '#default_value' => theme_get_setting('favicon.use_default', $theme),
      );
      $form['favicon']['settings'] = array(
        '#type' => 'container',
        '#states' => array(
          // Hide the favicon settings when using the default favicon.
          'invisible' => array(
            'input[name="default_favicon"]' => array(
              'checked' => TRUE,
            ),
          ),
        ),
      );
      $form['favicon']['settings']['favicon_path'] = array(
        '#type' => 'textfield',
        '#title' => t('Path to custom icon'),
        '#default_value' => theme_get_setting('favicon.path', $theme),
      );
      $form['favicon']['settings']['favicon_upload'] = array(
        '#type' => 'file',
        '#title' => t('Upload icon 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 (array(
      '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, array(
            'public:/' => PublicStream::basePath(),
          ));
        }
        elseif ($theme) {
          $local_file = drupal_get_path('theme', $theme) . '/' . $default;
        }
        else {
          $local_file = \Drupal::theme()
            ->getActiveTheme()
            ->getPath() . '/' . $default;
        }
        $element['#description'] = t('Examples: <code>@implicit-public-file</code> (for a file in the public filesystem), <code>@explicit-file</code>, or <code>@local-file</code>.', array(
          '@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'] = array(
          '#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.', array(
            '%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 = array(
          $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 = \Drupal::theme()
        ->getActiveTheme();
      $default_theme = $default_active_theme
        ->getName();

      /** @var \Drupal\Core\Theme\ThemeInitialization $theme_initialization */
      $theme_initialization = \Drupal::service('theme.initialization');
      \Drupal::theme()
        ->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)) {
        \Drupal::theme()
          ->setActiveTheme($default_active_theme);
      }
      else {
        \Drupal::theme()
          ->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 = array(
        'file_validate_is_image' => array(),
      );

      // 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 = array(
        'file_validate_extensions' => array(
          '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.'));
        }
      }

      // 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;
      $values['toggle_logo'] = 1;
    }
    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;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
ConfigFormBaseTrait::config protected function Retrieves a configuration object.
DependencySerializationTrait::$_serviceIds protected property An array of service IDs keyed by property name used for serialization.
DependencySerializationTrait::__sleep public function 1
DependencySerializationTrait::__wakeup public function 2
FormBase::$configFactory protected property The config factory. 3
FormBase::$loggerFactory protected property The logger factory.
FormBase::$requestStack protected property The request stack. 1
FormBase::$routeMatch protected property The route match.
FormBase::configFactory protected function Gets the config factory for this form. 3
FormBase::container private function Returns the service container.
FormBase::currentUser protected function Gets the current user.
FormBase::getRequest protected function Gets the request object.
FormBase::getRouteMatch protected function Gets the route match.
FormBase::logger protected function Gets the logger for a specific channel.
FormBase::resetConfigFactory public function Resets the configuration factory.
FormBase::setConfigFactory public function Sets the config factory for this form.
FormBase::setRequestStack public function Sets the request stack object to use.
LinkGeneratorTrait::$linkGenerator protected property The link generator. 1
LinkGeneratorTrait::getLinkGenerator protected function Returns the link generator.
LinkGeneratorTrait::l protected function Renders a link to a route given a route name and its parameters.
LinkGeneratorTrait::setLinkGenerator public function Sets the link generator service.
RedirectDestinationTrait::$redirectDestination protected property The redirect destination service.
RedirectDestinationTrait::getDestinationArray protected function Prepares a 'destination' URL query parameter for use with \Drupal\Core\Url.
RedirectDestinationTrait::getRedirectDestination protected function Returns the redirect destination service.
RedirectDestinationTrait::setRedirectDestination public function Sets the redirect destination service.
StringTranslationTrait::$stringTranslation protected property The string translation service.
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
ThemeSettingsForm::$editableConfig protected property An array of configuration names that should be editable.
ThemeSettingsForm::$mimeTypeGuesser protected property The MIME type guesser.
ThemeSettingsForm::$moduleHandler protected property The module handler.
ThemeSettingsForm::$themeHandler protected property The theme handler.
ThemeSettingsForm::buildForm public function Overrides ConfigFormBase::buildForm
ThemeSettingsForm::create public static function Instantiates a new instance of this class. Overrides ConfigFormBase::create
ThemeSettingsForm::getEditableConfigNames protected function Gets the configuration names that will be editable. Overrides ConfigFormBaseTrait::getEditableConfigNames
ThemeSettingsForm::getFormId public function Returns a unique string identifying the form. Overrides FormInterface::getFormId
ThemeSettingsForm::submitForm public function Form submission handler. Overrides ConfigFormBase::submitForm
ThemeSettingsForm::validateForm public function Form validation handler. Overrides FormBase::validateForm
ThemeSettingsForm::validatePath protected function Helper function for the system_theme_settings form.
ThemeSettingsForm::__construct public function Constructs a ThemeSettingsForm object. Overrides ConfigFormBase::__construct
UrlGeneratorTrait::$urlGenerator protected property The url generator.
UrlGeneratorTrait::getUrlGenerator protected function Returns the URL generator service.
UrlGeneratorTrait::redirect protected function Returns a redirect response object for the specified route.
UrlGeneratorTrait::setUrlGenerator public function Sets the URL generator service.
UrlGeneratorTrait::url protected function Generates a URL or path for a specific route based on the given parameters.