SettingsForm.php

Same filename in this branch
  1. 11.x core/modules/media_library/src/Form/SettingsForm.php
Same filename in other branches
  1. 9 core/modules/media_library/src/Form/SettingsForm.php
  2. 9 core/modules/aggregator/src/Form/SettingsForm.php
  3. 8.9.x core/modules/media_library/src/Form/SettingsForm.php
  4. 8.9.x core/modules/aggregator/src/Form/SettingsForm.php
  5. 10 core/modules/media_library/src/Form/SettingsForm.php
  6. 10 core/modules/navigation/src/Form/SettingsForm.php

Namespace

Drupal\navigation\Form

File

core/modules/navigation/src/Form/SettingsForm.php

View source
<?php

declare (strict_types=1);
namespace Drupal\navigation\Form;

use Drupal\Component\Utility\Environment;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\File\Exception\FileException;
use Drupal\Core\File\FileSystemInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Image\ImageFactory;
use Drupal\Core\Render\RendererInterface;
use Drupal\Core\StreamWrapper\PublicStream;
use Drupal\Core\StreamWrapper\StreamWrapperManager;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\file\Entity\File;
use Drupal\navigation\NavigationRenderer;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configure Navigation settings for this site.
 *
 * @internal
 */
final class SettingsForm extends ConfigFormBase {
    
    /**
     * Constructs a Navigation SettingsForm object.
     *
     * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
     *   The factory for configuration objects.
     * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager
     *   The typed config manager.
     * @param \Drupal\Core\File\FileSystemInterface $fileSystem
     *   The file system.
     * @param \Drupal\Core\Render\RendererInterface $renderer
     *   The renderer service.
     * @param \Drupal\Core\Image\ImageFactory $imageFactory
     *   The image factory.
     * @param \Drupal\Core\Theme\ThemeManagerInterface $themeManager
     *   The theme manager.
     */
    public function __construct(ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, FileSystemInterface $fileSystem, RendererInterface $renderer, ImageFactory $imageFactory, ThemeManagerInterface $themeManager) {
        parent::__construct($config_factory, $typed_config_manager);
    }
    
    /**
     * {@inheritdoc}
     */
    public static function create(ContainerInterface $container) {
        return new static($container->get('config.factory'), $container->get('config.typed'), $container->get('file_system'), $container->get('renderer'), $container->get('image.factory'), $container->get('theme.manager'));
    }
    
    /**
     * {@inheritdoc}
     */
    public function getFormId() : string {
        return 'navigation_settings';
    }
    
    /**
     * {@inheritdoc}
     */
    protected function getEditableConfigNames() : array {
        return [
            'navigation.settings',
        ];
    }
    
    /**
     * {@inheritdoc}
     */
    public function buildForm(array $form, FormStateInterface $form_state) : array {
        $config = $this->config('navigation.settings');
        $form['#attached']['library'][] = 'core/drupal.states';
        $form['logo'] = [
            '#type' => 'fieldset',
            '#title' => $this->t('Logo options'),
        ];
        $form['logo']['logo_provider'] = [
            '#type' => 'radios',
            '#title' => $this->t('Choose logo handling'),
            '#title_display' => 'invisible',
            '#options' => [
                NavigationRenderer::LOGO_PROVIDER_DEFAULT => $this->t('Default logo'),
                NavigationRenderer::LOGO_PROVIDER_HIDE => $this->t('Hide logo'),
                NavigationRenderer::LOGO_PROVIDER_CUSTOM => $this->t('Custom logo'),
            ],
            '#config_target' => 'navigation.settings:logo.provider',
        ];
        $form['logo']['custom'] = [
            '#type' => 'container',
            '#states' => [
                'visible' => [
                    ':input[name="logo_provider"]' => [
                        'value' => NavigationRenderer::LOGO_PROVIDER_CUSTOM,
                    ],
                ],
            ],
        ];
        // If path is a public:// URI, display the path relative to the files
        // directory; stream wrappers are not end-user friendly.
        $original_path = $config->get('logo.path') ?? '';
        $friendly_path = NULL;
        $default_path = $original_path;
        $default = 'logo.png';
        if (StreamWrapperManager::getScheme($original_path) === 'public') {
            $friendly_path = StreamWrapperManager::getTarget($original_path);
            $default_path = $friendly_path;
        }
        // Prepare local file path for description.
        if ($original_path && isset($friendly_path)) {
            $local_file = strtr($original_path, [
                'public:/' => PublicStream::basePath(),
            ]);
        }
        else {
            $local_file = $this->themeManager
                ->getActiveTheme()
                ->getPath() . '/' . $default;
        }
        $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' => $friendly_path ?? $default,
            '@explicit-file' => StreamWrapperManager::getScheme($original_path) !== FALSE ? $original_path : 'public://' . $default,
            '@local-file' => $local_file,
        ]);
        $allowed = 'png jpg jpeg';
        $max_navigation_allowed = $config->get('logo.max.filesize');
        $max_system_allowed = Environment::getUploadMaxSize();
        $max_allowed = $max_navigation_allowed < $max_system_allowed ? $max_navigation_allowed : $max_system_allowed;
        $upload_validators = [
            'FileExtension' => [
                'extensions' => $allowed,
            ],
            'FileSizeLimit' => [
                'fileLimit' => $max_allowed,
            ],
        ];
        $file_upload_help = [
            '#theme' => 'file_upload_help',
            '#description' => $this->t("If you don't have direct file access to the server, use this field to upload your logo. Recommended image dimension %width x %height pixels.", [
                '%width' => $config->get('logo.max.width'),
                '%height' => $config->get('logo.max.height'),
            ]),
            '#upload_validators' => $upload_validators,
            '#cardinality' => 1,
        ];
        $form['logo']['custom']['logo_path'] = [
            '#type' => 'textfield',
            '#title' => $this->t('Path to custom logo'),
            '#default_value' => $default_path,
            '#description' => $description,
            '#config_target' => 'navigation.settings:logo.path',
        ];
        $form['logo']['custom']['logo_upload'] = [
            '#type' => 'file',
            '#title' => $this->t('Upload logo image'),
            '#description' => $this->renderer
                ->renderInIsolation($file_upload_help),
            '#upload_validators' => $upload_validators,
        ];
        return parent::buildForm($form, $form_state);
    }
    
    /**
     * {@inheritdoc}
     */
    public function validateForm(array &$form, FormStateInterface $form_state) : void {
        // If the upload element is not empty, try to adjust the image dimensions
        // if needed.
        if ($form_state->getValue('logo_path')) {
            $path = $this->validateLogoPath($form_state->getValue('logo_path'));
            if (!$path) {
                $form_state->setErrorByName('logo_path', $this->t('The custom logo path is invalid.'));
            }
        }
        if ($form_state->getValue('logo_provider') !== NavigationRenderer::LOGO_PROVIDER_CUSTOM) {
            $form_state->setValue('logo_upload', '');
            $form_state->setValue('logo_path', '');
        }
        else {
            $file = _file_save_upload_from_form($form['logo']['custom']['logo_upload'], $form_state, 0);
            if ($file) {
                $logo_dimensions = $this->adjustLogoDimensions($file);
                if (!$logo_dimensions) {
                    $config = $this->config('navigation.settings');
                    $width = $config->get('logo.width');
                    $height = $config->get('logo.height');
                    $form_state->setErrorByName('logo_upload', $this->t('Image dimensions are bigger than the expected %widthx%height pixels and cannot be used as the navigation logo.', [
                        '%width' => $width,
                        '%height' => $height,
                    ]));
                }
                // Put the temporary file in form_values so we can save it on submit.
                $form_state->setValue('logo_upload', $file);
                $form_state->setValue('logo_path', $file->getFileUri());
                $form_state->setValue('logo_dimensions', $logo_dimensions);
            }
            if (empty($form_state->getValue('logo_path'))) {
                $form_state->setErrorByName('logo_path', 'An image file is required with the current logo handling option.');
            }
        }
        parent::validateForm($form, $form_state);
    }
    
    /**
     * {@inheritdoc}
     */
    public function submitForm(array &$form, FormStateInterface $form_state) : void {
        // If the user uploaded a new logo, save it to a permanent location
        // and use it in place of the default navigation-provided file.
        $default_scheme = $this->config('system.file')
            ->get('default_scheme');
        $values = $form_state->getValues();
        try {
            if (!empty($values['logo_upload'])) {
                $filename = $this->fileSystem
                    ->copy($values['logo_upload']->getFileUri(), $default_scheme . '://');
                $values['logo_path'] = $filename;
                if ($values['logo_dimensions']['resize']) {
                    $config = $this->config('navigation.settings');
                    $this->messenger()
                        ->addStatus($this->t('The image was resized to fit within the navigation logo expected dimensions of %widthx%height pixels. The new dimensions of the resized image are %new_widthx%new_height pixels.', [
                        '%width' => $config->get('logo.max.width'),
                        '%height' => $config->get('logo.max.height'),
                        '%new_width' => $values['logo_dimensions']['width'],
                        '%new_height' => $values['logo_dimensions']['height'],
                    ]));
                }
            }
        } catch (FileException) {
            $this->messenger()
                ->addError($this->t('The file %file could not be copied to the permanent destination. Contact the site administrator if the problem persists.', [
                '%file' => $values['logo_upload']->getFilename(),
            ]));
            return;
        }
        // If the user entered a path relative to the system files directory for
        // the logo, store a public:// URI so the theme system can handle it.
        if (!empty($values['logo_path'])) {
            $form_state->setValue('logo_path', $this->validateLogoPath($values['logo_path']));
        }
        parent::submitForm($form, $form_state);
    }
    
    /**
     * Adjusts the custom logo dimensions according to navigation settings.
     *
     * @param \Drupal\file\Entity\File $file
     *   The file entity that contains the image.
     *
     * @return array|null
     *   Array containing the logo dimensions properly adjusted. NULL if fails.
     */
    protected function adjustLogoDimensions(File $file) : ?array {
        $config = $this->config('navigation.settings');
        $image = $this->imageFactory
            ->get($file->getFileUri());
        if (!$image->isValid()) {
            return NULL;
        }
        $width = $config->get('logo.max.width');
        $height = $config->get('logo.max.height');
        if ($image->getWidth() <= $width && $image->getHeight() <= $height) {
            return [
                'width' => $width,
                'height' => $width,
                'resize' => FALSE,
            ];
        }
        if ($image->scale($width, $height) && $image->save()) {
            return [
                'width' => $image->getWidth(),
                'height' => $image->getHeight(),
                'resize' => TRUE,
            ];
        }
        return NULL;
    }
    
    /**
     * Helper function for the navigation 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 string|null
     *   A valid path that can be displayed through the theme system, or NULL if
     *   the path could not be validated.
     */
    protected function validateLogoPath(string $path) : ?string {
        // Absolute local file paths are invalid.
        if ($this->fileSystem
            ->realpath($path) == $path) {
            return NULL;
        }
        // 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 NULL;
    }

}

Classes

Title Deprecated Summary
SettingsForm Configure Navigation settings for this site.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.